<?php

namespace Core\App\Services;

/**
 * Universal AI Service
 * Supports: Gemini, Claude (Anthropic), OpenAI (ChatGPT)
 */
class AiService
{
    private array  $config;
    private string $provider;

    public function __construct(array $config)
    {
        $this->config   = $config;
        $this->provider = $config['provider'] ?? 'gemini';
    }

    /* =====================================================
       FACTORY — load active provider from DB
    ===================================================== */
    public static function fromActiveProvider(): self
    {
        $row = self::getActiveConfig();

        if (!$row || empty($row['api_key'])) {
            throw new \Exception(__('ai.error.not_configured', 'AI is not configured or disabled'));
        }

        return new self($row);
    }

    public static function fromProvider(string $provider): self
    {
        $db   = \db();
        $stmt = $db->prepare("SELECT * FROM ai_settings WHERE provider = ? LIMIT 1");
        $stmt->execute([$provider]);
        $row  = $stmt->fetch(\PDO::FETCH_ASSOC);

        if (!$row || empty($row['api_key'])) {
            throw new \Exception("Provider '{$provider}' not configured");
        }

        return new self($row);
    }

    public static function getActiveConfig(): ?array
    {
        $db   = \db();
        $stmt = $db->prepare("SELECT * FROM ai_settings WHERE is_active = 1 ORDER BY id DESC LIMIT 1");
        $stmt->execute();
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);

        return is_array($row) ? $row : null;
    }

    public static function getProviderLabel(string $provider): string
    {
        switch ($provider) {
            case 'gemini':
                return 'Google Gemini';
            case 'claude':
                return 'Claude';
            case 'openai':
                return 'OpenAI';
            default:
                return strtoupper($provider);
        }
    }

    public static function getDialectInstruction(?string $dialect = null, string $lang = 'ar'): string
    {
        if ($dialect === null || $dialect === '') {
            $dialect = self::resolveActiveDialect();
        }

        if ($lang !== 'ar') {
            if ($lang === 'en') {
                return 'Write in English.';
            }
            return "Write in {$lang}.";
        }

        switch ($dialect ?: 'fusha') {
            case 'algerian':
                return 'اكتب باللهجة الجزائرية الدارجة';
            case 'moroccan':
                return 'اكتب باللهجة المغربية الدارجة';
            case 'egyptian':
                return 'اكتب باللهجة المصرية';
            case 'saudi':
                return 'اكتب باللهجة السعودية';
            default:
                return 'اكتب باللغة العربية الفصحى';
        }
    }

    public function buildDialectInstruction(string $lang = 'ar'): string
    {
        return self::getDialectInstruction($this->getDialect(), $lang);
    }

    private static function resolveActiveDialect(): string
    {
        try {
            $row = self::getActiveConfig();
            if (is_array($row) && !empty($row['dialect'])) {
                return (string)$row['dialect'];
            }
        } catch (\Throwable $e) {
        }

        return 'fusha';
    }

    /* =====================================================
       GENERATE — dispatcher
    ===================================================== */
    public function generate(string $prompt, array $options = []): string
    {
        switch ($this->provider) {
            case 'gemini':
                return $this->callGemini($prompt, $options);
            case 'claude':
                return $this->callClaude($prompt, $options);
            case 'openai':
                return $this->callOpenAI($prompt, $options);
            default:
                throw new \Exception("Unknown provider: {$this->provider}");
        }
    }

    public function generateJson(string $prompt, array $options = []): array
    {
        $text = $this->generate($prompt, $options);

        try {
            return $this->extractJsonObject($text);
        } catch (\Throwable $e) {
            return [];
        }
    }

    public function visionGenerate(string $prompt, array $images, array $options = []): string
    {
        switch ($this->provider) {
            case 'gemini':
                return $this->callGeminiVision($prompt, $images, $options);
            case 'claude':
                return $this->callClaudeVision($prompt, $images, $options);
            case 'openai':
                return $this->callOpenAIVision($prompt, $images, $options);
            default:
                throw new \Exception("Unknown provider: {$this->provider}");
        }
    }

    /* =====================================================
       GEMINI (Google)
    ===================================================== */
    private function callGemini(string $prompt, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'models/gemini-2.0-flash';
        $apiKey = $this->config['api_key'];
        $url    = "https://generativelanguage.googleapis.com/v1beta/{$model}:generateContent?key=" . urlencode($apiKey);

        // Prepend system prompt to user message for Gemini (no separate system field)
        $fullPrompt = !empty($options['system'])
            ? $options['system'] . "\n\n---\n\n" . $prompt
            : $prompt;

        $payload = json_encode([
            'contents' => [['parts' => [['text' => $fullPrompt]]]],
            'generationConfig' => [
                'temperature'     => $options['temperature'] ?? 0.8,
                'maxOutputTokens' => $options['max_tokens']  ?? 1024,
                'topP'            => $options['top_p']       ?? 0.9,
            ],
        ], JSON_UNESCAPED_UNICODE);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
            CURLOPT_POSTFIELDS     => $payload,
            CURLOPT_TIMEOUT        => (int)($options['timeout'] ?? 60),
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if (!$response) throw new \Exception('Gemini: connection failed');

        $json = json_decode($response, true);
        $parts = $json['candidates'][0]['content']['parts'] ?? [];
        $textParts = [];
        if (is_array($parts)) {
            foreach ($parts as $part) {
                if (is_array($part) && isset($part['text']) && is_string($part['text'])) {
                    $textParts[] = $part['text'];
                }
            }
        }
        $text = trim(implode("\n", $textParts));

        if (!$text) {
            $errMsg = $json['error']['message'] ?? 'Empty response';
            throw new \Exception("Gemini: {$errMsg}");
        }

        return trim($text);
    }

    /* =====================================================
       CLAUDE (Anthropic)
    ===================================================== */
    private function callClaude(string $prompt, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'claude-3-5-haiku-20241022';
        $apiKey = $this->config['api_key'];

        $body = [
            'model'      => $model,
            'max_tokens' => $options['max_tokens'] ?? 1024,
            'messages'   => [['role' => 'user', 'content' => $prompt]],
        ];

        // Support optional system prompt (used by AiLandingPageService)
        if (!empty($options['system'])) {
            $body['system'] = (string)$options['system'];
        }

        $payload = json_encode($body, JSON_UNESCAPED_UNICODE);

        $ch = curl_init('https://api.anthropic.com/v1/messages');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'x-api-key: ' . $apiKey,
                'anthropic-version: 2023-06-01',
            ],
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_TIMEOUT    => (int)($options['timeout'] ?? 60),
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if (!$response) throw new \Exception('Claude: connection failed');

        $json = json_decode($response, true);
        $text = $json['content'][0]['text'] ?? '';

        if (!$text) {
            $errMsg = $json['error']['message'] ?? 'Empty response';
            throw new \Exception("Claude: {$errMsg}");
        }

        return trim($text);
    }

    /* =====================================================
       OPENAI (ChatGPT)
    ===================================================== */
    private function callOpenAI(string $prompt, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'gpt-4o-mini';
        $apiKey = $this->config['api_key'];

        $messages = [];
        if (!empty($options['system'])) {
            $messages[] = ['role' => 'system', 'content' => (string)$options['system']];
        }
        $messages[] = ['role' => 'user', 'content' => $prompt];

        $payload = json_encode([
            'model'       => $model,
            'max_tokens'  => $options['max_tokens']  ?? 1024,
            'temperature' => $options['temperature'] ?? 0.8,
            'messages'    => $messages,
        ], JSON_UNESCAPED_UNICODE);

        $ch = curl_init('https://api.openai.com/v1/chat/completions');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $apiKey,
            ],
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_TIMEOUT    => (int)($options['timeout'] ?? 60),
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        if (!$response) throw new \Exception('OpenAI: connection failed');

        $json = json_decode($response, true);
        $text = $json['choices'][0]['message']['content'] ?? '';

        if (!$text) {
            $errMsg = $json['error']['message'] ?? 'Empty response';
            throw new \Exception("OpenAI: {$errMsg}");
        }

        return trim($text);
    }

    private function callGeminiVision(string $prompt, array $images, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'models/gemini-2.0-flash';
        $apiKey = $this->config['api_key'];
        $url    = "https://generativelanguage.googleapis.com/v1beta/{$model}:generateContent?key=" . urlencode($apiKey);

        $fullPrompt = !empty($options['system'])
            ? $options['system'] . "\n\n---\n\n" . $prompt
            : $prompt;

        $parts = [['text' => $fullPrompt]];
        foreach ($images as $image) {
            $prepared = $this->prepareVisionImage($image);
            $parts[] = [
                'inline_data' => [
                    'mime_type' => $prepared['mime'],
                    'data' => $prepared['base64'],
                ],
            ];
        }

        $payload = json_encode([
            'contents' => [['parts' => $parts]],
            'generationConfig' => [
                'temperature' => $options['temperature'] ?? 0.2,
                'maxOutputTokens' => $options['max_tokens'] ?? 2048,
                'topP' => $options['top_p'] ?? 0.9,
            ],
        ], JSON_UNESCAPED_UNICODE);

        return $this->curlJsonRequest($url, $payload, ['Content-Type: application/json'], 'Gemini');
    }

    private function callClaudeVision(string $prompt, array $images, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'claude-3-5-haiku-20241022';
        $apiKey = $this->config['api_key'];

        $content = [['type' => 'text', 'text' => $prompt]];
        foreach ($images as $image) {
            $prepared = $this->prepareVisionImage($image);
            $content[] = [
                'type' => 'image',
                'source' => [
                    'type' => 'base64',
                    'media_type' => $prepared['mime'],
                    'data' => $prepared['base64'],
                ],
            ];
        }

        $body = [
            'model' => $model,
            'max_tokens' => $options['max_tokens'] ?? 2048,
            'messages' => [
                ['role' => 'user', 'content' => $content],
            ],
        ];

        if (!empty($options['system'])) {
            $body['system'] = (string)$options['system'];
        }

        $payload = json_encode($body, JSON_UNESCAPED_UNICODE);

        return $this->curlJsonRequest(
            'https://api.anthropic.com/v1/messages',
            $payload,
            [
                'Content-Type: application/json',
                'x-api-key: ' . $apiKey,
                'anthropic-version: 2023-06-01',
            ],
            'Claude'
        );
    }

    private function callOpenAIVision(string $prompt, array $images, array $options = []): string
    {
        $model  = $this->config['model'] ?: 'gpt-4o-mini';
        $apiKey = $this->config['api_key'];

        $content = [['type' => 'text', 'text' => $prompt]];
        foreach ($images as $image) {
            $prepared = $this->prepareVisionImage($image);
            $content[] = [
                'type' => 'image_url',
                'image_url' => [
                    'url' => 'data:' . $prepared['mime'] . ';base64,' . $prepared['base64'],
                ],
            ];
        }

        $messages = [];
        if (!empty($options['system'])) {
            $messages[] = ['role' => 'system', 'content' => (string)$options['system']];
        }
        $messages[] = ['role' => 'user', 'content' => $content];

        $payload = json_encode([
            'model' => $model,
            'messages' => $messages,
            'max_tokens' => $options['max_tokens'] ?? 2048,
            'temperature' => $options['temperature'] ?? 0.2,
        ], JSON_UNESCAPED_UNICODE);

        return $this->curlJsonRequest(
            'https://api.openai.com/v1/chat/completions',
            $payload,
            [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $apiKey,
            ],
            'OpenAI'
        );
    }

    /* =====================================================
       LIST MODELS — per provider
    ===================================================== */
    public function listModels(): array
    {
        switch ($this->provider) {
            case 'gemini':
                return $this->listGeminiModels();
            case 'claude':
                return $this->listClaudeModels();
            case 'openai':
                return $this->listOpenAIModels();
            default:
                return [];
        }
    }

    private function listGeminiModels(): array
    {
        $url = "https://generativelanguage.googleapis.com/v1beta/models?key=" . urlencode($this->config['api_key']);
        $ch  = curl_init($url);
        curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20]);
        $response = curl_exec($ch);
        curl_close($ch);

        $json   = json_decode($response, true);
        $models = [];
        foreach ($json['models'] ?? [] as $m) {
            if (in_array('generateContent', $m['supportedGenerationMethods'] ?? [], true)) {
                $models[] = $m['name'];
            }
        }
        return $models;
    }

    private function listClaudeModels(): array
    {
        // Claude models — قائمة ثابتة (API لا توفر endpoint للـ list)
        return [
            'claude-opus-4-5',
            'claude-sonnet-4-5',
            'claude-haiku-4-5-20251001',
            'claude-3-5-sonnet-20241022',
            'claude-3-5-haiku-20241022',
            'claude-3-opus-20240229',
        ];
    }

    private function listOpenAIModels(): array
    {
        $ch = curl_init('https://api.openai.com/v1/models');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $this->config['api_key']],
            CURLOPT_TIMEOUT        => 20,
        ]);
        $response = curl_exec($ch);
        curl_close($ch);

        $json   = json_decode($response, true);
        $models = [];
        foreach ($json['data'] ?? [] as $m) {
            if (strpos((string)$m['id'], 'gpt-') === 0) {
                $models[] = $m['id'];
            }
        }
        sort($models);
        return array_reverse($models);
    }

    /* =====================================================
       TEST CONNECTION
    ===================================================== */
    public function testConnection(): array
    {
        try {
            $result = $this->generate('Say "OK" only.', ['max_tokens' => 10]);
            return ['success' => true, 'response' => $result];
        } catch (\Throwable $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    public function getProvider(): string { return $this->provider; }
    public function getModel(): string    { return $this->config['model'] ?? ''; }
    public function getDialect(): string   { return $this->config['dialect'] ?? 'fusha'; }

    private function prepareVisionImage($image): array
    {
        $path = is_array($image) ? (string)($image['path'] ?? '') : (string)$image;
        if ($path === '' || !is_file($path)) {
            throw new \Exception('Vision image file not found');
        }

        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = $finfo ? (string)finfo_file($finfo, $path) : 'image/png';
        if ($finfo) {
            finfo_close($finfo);
        }

        if (strpos($mime, 'image/') !== 0) {
            throw new \Exception('Unsupported vision image MIME type');
        }

        return [
            'path' => $path,
            'mime' => $mime,
            'base64' => base64_encode((string)file_get_contents($path)),
        ];
    }

    private function curlJsonRequest(string $url, string $payload, array $headers, string $providerLabel): string
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_TIMEOUT => 90,
        ]);

        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        if (!$response) {
            throw new \Exception($providerLabel . ': ' . ($error ?: 'connection failed'));
        }

        $json = json_decode($response, true);
        $text = '';

        if ($providerLabel === 'Claude') {
            $text = trim((string)($json['content'][0]['text'] ?? ''));
        } elseif ($providerLabel === 'OpenAI') {
            $text = trim((string)($json['choices'][0]['message']['content'] ?? ''));
        } else {
            $parts = $json['candidates'][0]['content']['parts'] ?? [];
            $texts = [];
            foreach ($parts as $part) {
                if (is_array($part) && isset($part['text']) && is_string($part['text'])) {
                    $texts[] = $part['text'];
                }
            }
            $text = trim(implode("\n", $texts));
        }

        if ($text === '') {
            $errMsg = $json['error']['message'] ?? 'Empty response';
            throw new \Exception("{$providerLabel}: {$errMsg}");
        }

        return $text;
    }

    private function extractJsonObject(string $raw): array
    {
        $raw = trim($raw);
        $raw = preg_replace('/```(?:json)?/i', '', $raw);
        $raw = trim((string)$raw);

        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            return $decoded;
        }

        if (preg_match('/\{[\s\S]*\}/', $raw, $m)) {
            $decoded = json_decode($m[0], true);
            if (is_array($decoded)) {
                return $decoded;
            }
        }

        throw new \Exception('AI did not return valid JSON');
    }
}
