<?php

namespace App\Http\Controllers\Admin\System;

use App\Http\Controllers\Controller;
use App\Services\Api\API;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Inertia\Inertia;
use Inertia\Response;

class ChatController extends Controller
{
    protected API $api;

    public function __construct(API $api)
    {
        $this->api = $api;
    }

    /**
     * Display Chat dashboard
     */
    public function index(): Response
    {
        $settings = [
            'logs_path' => config('pw-api.chat.logs_path'),
        ];

        // Get file info for the chat log file
        $logsPath = config('pw-api.chat.logs_path');
        $chatLogPath = $logsPath.'/world2.chat';

        $fileInfo = [];
        if (file_exists($chatLogPath)) {
            $fileInfo = [
                'exists' => true,
                'size' => filesize($chatLogPath),
                'modified' => filemtime($chatLogPath),
            ];
        } else {
            $fileInfo = ['exists' => false, 'path' => $chatLogPath];
        }

        // Read and parse recent chat logs from file
        $logs = $this->parseChatLogs($chatLogPath, 100);

        // Get available channels from logs
        $channels = array_unique(array_column($logs, 'channel'));

        return Inertia::render('admin/system/chat/index', [
            'settings' => $settings,
            'fileInfo' => $fileInfo,
            'logs' => $logs,
            'channels' => array_values(array_filter($channels)),
        ]);
    }

    /**
     * Parse chat logs from file
     */
    private function parseChatLogs(string $filePath, int $limit = 100): array
    {
        if (! file_exists($filePath)) {
            return [];
        }

        $logs = [];
        $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

        if (! $lines) {
            return [];
        }

        $id = 0;
        foreach ($lines as $line) {
            $parsed = $this->parseChatLine($line, ++$id);
            if ($parsed && ! in_array($parsed['channel'], ['trade', 'system'])) {
                $logs[] = $parsed;
            }
        }

        // Return most recent logs first, limited
        $logs = array_reverse($logs);

        return array_slice($logs, 0, $limit);
    }

    /**
     * Parse a single chat line
     * Format examples:
     * 2025-12-28 18:41:07 deb glinkd-1: chat : Chat: src=1025 chl=0 msg=dwB3AA==
     * 2025-12-28 18:41:11 deb gfaction: chat : Guild: src=1025 fid=1 msg=ZQBxAHcAZQB3AGUA
     * 2025-12-28 20:01:10 deb glinkd-1: chat : Whisper: src=1025 dst=1088 msg=base64
     */
    private function parseChatLine(string $line, int $id): ?array
    {
        // Extract timestamp
        if (! preg_match('/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/', $line, $tsMatch)) {
            return null;
        }
        $timestamp = $tsMatch[1];

        // Determine chat type and extract data
        $chatType = 'unknown';
        $roleId = 0;
        $message = '';
        $channel = 'chat';

        // Chat: src=1025 chl=0 msg=base64
        if (preg_match('/Chat:\s*src=(\d+)\s+chl=(\d+)\s+msg=([^\s]+)/', $line, $matches)) {
            $chatType = 'chat';
            $roleId = (int) $matches[1];
            $channel = $this->getChannelName((int) $matches[2]);
            $message = $this->decodeMessage($matches[3]);
        }
        // Guild: src=1025 fid=1 msg=base64
        elseif (preg_match('/Guild:\s*src=(\d+)\s+fid=\d+\s+msg=([^\s]+)/', $line, $matches)) {
            $chatType = 'guild';
            $roleId = (int) $matches[1];
            $channel = 'faction';
            $message = $this->decodeMessage($matches[2]);
        }
        // Whisper: src=1025 dst=1088 msg=base64
        elseif (preg_match('/Whisper:\s*src=(\d+)\s+dst=(\d+)\s+msg=([^\s]+)/', $line, $matches)) {
            $chatType = 'whisper';
            $roleId = (int) $matches[1];
            $channel = 'whisper';
            $destRoleId = (int) $matches[2];
            $destRoleInfo = $this->getRoleInfo($destRoleId);
            $message = $this->decodeMessage($matches[3]);
        }
        // System: msg=base64
        elseif (preg_match('/System:\s*msg=([^\s]+)/', $line, $matches)) {
            $chatType = 'system';
            $roleId = 0;
            $channel = 'system';
            $message = $this->decodeMessage($matches[1]);
        } else {
            return null;
        }

        if (empty($message)) {
            return null;
        }

        // Get role info from API
        $roleInfo = $roleId > 0 ? $this->getRoleInfo($roleId) : ['name' => 'System', 'cls' => 0, 'faction' => null];

        return [
            'id' => $id,
            'role_id' => $roleId,
            'role_name' => $roleInfo['name'],
            'cls' => $roleInfo['cls'],
            'faction' => $roleInfo['faction'],
            'dst_role_id' => $destRoleId ?? null,
            'dst_role_name' => isset($destRoleInfo) ? $destRoleInfo['name'] : null,
            'dst_cls' => isset($destRoleInfo) ? $destRoleInfo['cls'] : null,
            'dst_faction' => isset($destRoleInfo) ? $destRoleInfo['faction'] : null,
            'channel' => $channel,
            'message' => $message,
            'log_timestamp' => $timestamp,
        ];
    }

    /**
     * Get role info (name, class, faction) from API with caching
     */
    private function getRoleInfo(int $roleId): array
    {
        if ($roleId <= 0) {
            return ['name' => 'System', 'cls' => 0, 'faction' => null];
        }

        // Cache role info for 5 minutes to reduce API calls
        $cacheKey = "role_info_{$roleId}";

        return Cache::remember($cacheKey, 300, function () use ($roleId) {
            $result = ['name' => "Role#{$roleId}", 'cls' => 0, 'faction' => null];

            try {
                $roleBase = $this->api->getRoleBase($roleId);
                if ($roleBase && isset($roleBase['name'])) {
                    $result['name'] = $roleBase['name'];
                    $result['cls'] = $roleBase['cls'] ?? 0;
                }

                // Get faction info
                $userFaction = $this->api->getUserFaction($roleId);
                if (isset($userFaction['fid']) && $userFaction['fid'] > 0) {
                    $factionInfo = $this->api->getFactionInfo($userFaction['fid']);
                    $result['faction'] = $factionInfo['name'] ?? null;
                }
            } catch (\Exception $e) {
                // Fail silently
            }

            return $result;
        });
    }

    /**
     * Decode base64 message (handles both UTF-8 and UTF-16LE encoding)
     */
    private function decodeMessage(string $encoded): string
    {
        $decoded = base64_decode($encoded);

        if ($decoded === false) {
            return $this->sanitizeUtf8($encoded);
        }

        // Check if it's UTF-16LE (has null bytes between characters)
        if (strlen($decoded) >= 2 && $decoded[1] === "\x00") {
            // Convert from UTF-16LE to UTF-8
            $converted = @mb_convert_encoding($decoded, 'UTF-8', 'UTF-16LE');
            if ($converted !== false) {
                $decoded = $converted;
            }
        }

        // Remove null bytes and trim
        $decoded = str_replace("\x00", '', $decoded);
        $decoded = trim($decoded);

        // Ensure valid UTF-8 output
        $decoded = $this->sanitizeUtf8($decoded);

        return $decoded ?: $this->sanitizeUtf8($encoded);
    }

    /**
     * Sanitize string to ensure valid UTF-8 encoding
     */
    private function sanitizeUtf8(string $string): string
    {
        // Remove any invalid UTF-8 sequences
        $string = mb_convert_encoding($string, 'UTF-8', 'UTF-8');

        // Remove control characters except for common whitespace
        $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $string);

        // If preg_replace failed (invalid UTF-8), try a fallback
        if ($string === null) {
            // Force conversion and strip invalid sequences
            $string = iconv('UTF-8', 'UTF-8//IGNORE', $string);
            if ($string === false) {
                return '[encoding error]';
            }
        }

        return $string;
    }

    /**
     * Get channel name from channel ID
     */
    private function getChannelName(int $channelId): string
    {
        $channels = [
            0 => 'normal',
            1 => 'world',
            2 => 'squad',
            3 => 'team',
            4 => 'private',
            5 => 'faction',
            6 => 'system',
            7 => 'trade',
            9 => 'battle',
        ];

        return $channels[$channelId] ?? "channel_{$channelId}";
    }

    /**
     * Update Chat settings
     */
    public function updateSettings(Request $request)
    {
        $request->validate([
            'logs_path' => 'required|string',
        ]);

        $this->updateEnv([
            'CHAT_LOGS_PATH' => $request->logs_path,
        ]);

        return redirect()->back()->with('success', 'Settings updated successfully!');
    }

    /**
     * Get chat logs with filters (API endpoint)
     */
    public function getLogs(Request $request)
    {
        $logsPath = config('pw-api.chat.logs_path');
        $chatLogPath = $logsPath.'/world2.chat';

        $logs = $this->parseChatLogs($chatLogPath, 500);

        // Apply filters
        if ($request->filled('channel')) {
            $logs = array_filter($logs, fn ($log) => $log['channel'] === $request->channel);
        }

        if ($request->filled('search')) {
            $search = strtolower($request->search);
            $logs = array_filter($logs, fn ($log) => str_contains(strtolower($log['message']), $search) ||
                str_contains(strtolower($log['role_name']), $search)
            );
        }

        return response()->json([
            'data' => array_values($logs),
            'total' => count($logs),
        ]);
    }

    /**
     * Update environment file
     */
    private function updateEnv($data): void
    {
        $envPath = base_path('.env');
        $envContent = file_get_contents($envPath);

        foreach ($data as $key => $value) {
            // Handle values with spaces by wrapping in quotes
            $escapedValue = str_contains($value, ' ') ? "\"{$value}\"" : $value;

            if (preg_match("/^{$key}=.*/m", $envContent)) {
                $envContent = preg_replace("/^{$key}=.*/m", "{$key}={$escapedValue}", $envContent);
            } else {
                $envContent .= "\n{$key}={$escapedValue}";
            }
        }

        file_put_contents($envPath, $envContent);
    }
}
