<?php

namespace App\LLM;

/**
 * Google Gemini provider implementation.
 *
 * Config keys (from sp_orgs_meta metval JSON):
 *   secretKey   — required — Google AI API key
 *   model       — optional — default: gemini-2.5-flash
 *   max_tokens  — optional — default: 4096
 *   temperature — optional — default: 0.7
 *
 * Note: Gemini uses a different API structure from OpenAI-compatible providers.
 * Messages use 'parts' arrays and roles are 'user' / 'model' (not 'assistant').
 *
 * Usage:
 *   $llm = LLMManager::forOrg($orgId, 'gemini');
 *   $res = $llm->sendPrompt([['role' => 'user', 'content' => 'Hello']]);
 *   echo $res['content'];
 */
class svcGemini extends LLMProvider
{
    private const API_BASE    = 'https://generativelanguage.googleapis.com/v1beta/models';
    private const API_CHAT    = ':generateContent';
    private const API_STREAM  = ':streamGenerateContent';

    private static array $availableModels = [
        ['id' => 'gemini-2.5-flash',       'label' => 'Gemini 2.5 Flash'],
        ['id' => 'gemini-2.5-flash-lite',   'label' => 'Gemini 2.5 Flash Lite'],
        ['id' => 'gemini-2.0-flash',        'label' => 'Gemini 2.0 Flash'],
        ['id' => 'gemini-1.5-pro',          'label' => 'Gemini 1.5 Pro'],
        ['id' => 'gemini-1.5-flash',        'label' => 'Gemini 1.5 Flash'],
    ];

    public function __construct(array $config)
    {
        if (empty($config['secretKey'])) {
            throw new \Exception('Gemini secretKey is required');
        }

        $this->apiKey      = $config['secretKey'];
        $this->model       = $config['model']        ?? 'gemini-2.5-flash';
        $this->maxTokens   = (int)($config['max_tokens']   ?? 4096);
        $this->temperature = (float)($config['temperature'] ?? 0.7);
    }

    /**
     * Send a chat prompt and return the full response.
     */
    public function sendPrompt(array $messages, array $options = []): array
    {
        try {
            $model   = $options['model'] ?? $this->model;
            $url     = self::API_BASE . '/' . $model . self::API_CHAT . '?key=' . $this->apiKey;
            $payload = $this->buildPayload($messages, $options);

            $response = $this->curlPost($url, $this->headers(), $payload);

            $content = $response['candidates'][0]['content']['parts'][0]['text'] ?? '';
            $usage   = $response['usageMetadata'] ?? [];

            return $this->successResponse($content, [
                'prompt_tokens'     => $usage['promptTokenCount']     ?? 0,
                'completion_tokens' => $usage['candidatesTokenCount'] ?? 0,
                'total_tokens'      => $usage['totalTokenCount']       ?? 0,
            ]);

        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    /**
     * Stream a chat prompt, calling $callback with each text chunk.
     *
     * @param callable $callback  function(string $chunk)
     */
    public function streamPrompt(array $messages, array $options, callable $callback): array
    {
        try {
            $model   = $options['model'] ?? $this->model;
            $url     = self::API_BASE . '/' . $model . self::API_STREAM . '?key=' . $this->apiKey . '&alt=sse';
            $payload = $this->buildPayload($messages, $options);

            $this->curlStream($url, $this->headers(), $payload, function(array $chunk) use ($callback) {
                $text = $chunk['candidates'][0]['content']['parts'][0]['text'] ?? '';
                if ($text !== '') {
                    $callback($text);
                }
            });

            return ['success' => true, 'error' => ''];

        } catch (\Exception $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Return supported Gemini models.
     */
    public function getModels(): array
    {
        return self::$availableModels;
    }

    /**
     * Validate the API key with a minimal request.
     */
    public function testConnection(): array
    {
        try {
            $start   = microtime(true);
            $model   = $this->model;
            $url     = self::API_BASE . '/' . $model . self::API_CHAT . '?key=' . $this->apiKey;
            $payload = [
                'contents'          => [['role' => 'user', 'parts' => [['text' => 'hi']]]],
                'generationConfig'  => ['maxOutputTokens' => 1],
            ];

            $this->curlPost($url, $this->headers(), $payload);

            $latency = (int)((microtime(true) - $start) * 1000);

            return ['success' => true, 'latency_ms' => $latency, 'error' => ''];

        } catch (\Exception $e) {
            return ['success' => false, 'latency_ms' => 0, 'error' => $e->getMessage()];
        }
    }

    // ─── Private Helpers ─────────────────────────────────────────────────────

    private function headers(): array
    {
        return ['Content-Type: application/json'];
    }

    private function buildPayload(array $messages, array $options): array
    {
        $maxTokens   = (int)($options['max_tokens']   ?? $this->maxTokens);
        $temperature = (float)($options['temperature'] ?? $this->temperature);

        // Convert OpenAI-style messages to Gemini format
        // Roles: 'user' stays 'user', 'assistant' becomes 'model'
        $contents = [];
        foreach ($messages as $msg) {
            $contents[] = [
                'role'  => $msg['role'] === 'assistant' ? 'model' : 'user',
                'parts' => [['text' => $msg['content']]],
            ];
        }

        $payload = [
            'contents'         => $contents,
            'generationConfig' => [
                'maxOutputTokens' => $maxTokens,
                'temperature'     => $temperature,
            ],
        ];

        // Gemini uses a top-level 'systemInstruction' for system prompts
        if ($this->systemPrompt !== '') {
            $payload['systemInstruction'] = [
                'parts' => [['text' => $this->systemPrompt]],
            ];
        }

        return $payload;
    }
}
