<?php

namespace XZD\AI\V5\AI;

/**
 * Google Gemini Provider
 *
 * 请求格式: POST /models/{model}:generateContent
 * 认证: x-goog-api-key header 或 ?key= query
 */
class GeminiProvider implements ProviderInterface
{
    protected array $config;

    public function __construct(array $config)
    {
        $this->config = array_merge([
            'provider'    => 'google',
            'api_key'     => '',
            'base_url'    => 'https://generativelanguage.googleapis.com/v1beta',
            'model'       => 'gemini-2.5-flash',
            'timeout'     => 90,
            'max_retries' => 3,
        ], $config);

        // 安全底线：timeout不得低于60秒
        if (($this->config['timeout'] ?? 0) < 60) {
            $this->config['timeout'] = 60;
        }
    }

    public function chat(array $messages, array $options = []): array
    {
        $body = $this->buildRequestBody($messages, $options);
        // 支持按调用指定模型
        $model = $options['model'] ?? ($this->config['model'] ?: 'gemini-2.5-flash');
        $result = $this->request("/models/{$model}:generateContent", $body);

        if (isset($result['error'])) {
            return Types::error($result['error'], $result['error_code'] ?? 'api_error');
        }

        $candidate = $result['candidates'][0]['content'] ?? [];
        $contentText = '';
        $toolCalls = null;

        foreach (($candidate['parts'] ?? []) as $part) {
            if (isset($part['text'])) {
                $contentText .= $part['text'];
            } elseif (isset($part['functionCall'])) {
                $fc = $part['functionCall'];
                $toolCalls[] = [
                    'id'   => $fc['name'] . '_' . uniqid(),
                    'type' => 'function',
                    'function' => [
                        'name'      => $fc['name'],
                        'arguments' => wp_json_encode($fc['args'] ?? []),
                    ],
                ];
            }
        }

        return Types::response(
            content: $contentText,
            inputTokens: $result['usageMetadata']['promptTokenCount'] ?? 0,
            outputTokens: $result['usageMetadata']['candidatesTokenCount'] ?? 0,
            toolCalls: $toolCalls,
            // Gemini context caching
            cacheTokens: (int) ($result['usageMetadata']['cachedContentTokenCount'] ?? 0),
        );
    }

    public function chatStream(array $messages, array $options = [], ?callable $onChunk = null): \Generator
    {
        $body = $this->buildRequestBody($messages, $options);
        // 支持按调用指定模型
        $model = $options['model'] ?? ($this->config['model'] ?: 'gemini-2.5-flash');
        $url = rtrim($this->getBaseUrl(), '/') . "/models/{$model}:streamGenerateContent?alt=sse";

        $chunks = [];
        $bodyJson = wp_json_encode($body);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $bodyJson,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'x-goog-api-key: ' . $this->config['api_key'],
            ],
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_TIMEOUT        => $this->config['timeout'] ?? 60,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_WRITEFUNCTION  => function ($_, $data) use (&$chunks, $onChunk) {
                $lines = explode("\n", $data);
                foreach ($lines as $line) {
                    $line = trim($line);
                    if (!str_starts_with($line, 'data: ')) continue;
                    $json = json_decode(substr($line, 6), true);
                    if (!$json) continue;
                    $candidate = $json['candidates'][0]['content'] ?? [];
                    $parts = $candidate['parts'] ?? [];
                    $text = '';
                    $toolCall = null;
                    foreach ($parts as $part) {
                        if (isset($part['text'])) $text .= $part['text'];
                        elseif (isset($part['functionCall'])) $toolCall = $part['functionCall'];
                    }
                    $finish = $json['candidates'][0]['finishReason'] ?? null;
                    $chunk = ['delta' => $text, 'type' => $toolCall ? 'tool_call' : 'text', 'done' => $finish !== null, 'finish_reason' => $finish];
                    $chunks[] = $chunk;
                    if ($onChunk) $onChunk($chunk);
                }
                return strlen($data);
            },
        ]);

        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        // HTTP 错误处理（修复静默失败）
        if ($curlError || $httpCode >= 400) {
            $errorMsg = $curlError ?: "HTTP {$httpCode}";
            if (!empty($chunks)) {
                // 尝试从响应体中提取错误详情
                $lastChunk = end($chunks);
                $errorBody = $lastChunk['delta'] ?? '';
                if ($errorBody) $errorMsg .= ': ' . mb_substr($errorBody, 0, 200);
            }
            yield ['delta' => '', 'type' => 'error', 'done' => true, 'finish_reason' => $errorMsg];
            return;
        }

        foreach ($chunks as $chunk) {
            yield $chunk;
        }
    }

    public function testConnection(): array
    {
        $model = $this->config['model'] ?: 'gemini-2.5-flash';
        $url = rtrim($this->getBaseUrl(), '/') . "/models/{$model}:generateContent";

        $body = wp_json_encode([
            'contents' => [['parts' => [['text' => 'Hi']]]],
            'generationConfig' => ['maxOutputTokens' => 10],
        ]);

        $response = wp_remote_post($url, [
            'timeout' => 10,
            'headers' => [
                'Content-Type' => 'application/json',
                'x-goog-api-key' => $this->config['api_key'],
            ],
            'body' => $body,
        ]);

        if (is_wp_error($response)) {
            return ['success' => false, 'message' => $response->get_error_message()];
        }

        $code = wp_remote_retrieve_response_code($response);
        if ($code >= 400) {
            $data = json_decode(wp_remote_retrieve_body($response), true);
            return ['success' => false, 'message' => $data['error']['message'] ?? "HTTP {$code}"];
        }

        return ['success' => true, 'message' => '连接正常'];
    }

    public function getModels(): array
    {
        $url = rtrim($this->getBaseUrl(), '/') . '/models';
        $response = wp_remote_get($url, [
            'timeout' => 10,
            'headers' => ['x-goog-api-key' => $this->config['api_key']],
        ]);

        if (is_wp_error($response)) {
            return ['success' => false, 'models' => [], 'error' => $response->get_error_message()];
        }

        $data = json_decode(wp_remote_retrieve_body($response), true);
        $models = [];
        foreach (($data['models'] ?? []) as $m) {
            if (str_contains($m['name'], 'gemini')) {
                $id = str_replace('models/', '', $m['name']);
                $models[] = ['id' => $id, 'name' => $m['displayName'] ?? $id];
            }
        }

        return ['success' => true, 'models' => $models];
    }

    public function getProviderKey(): string
    {
        return 'google';
    }

    public function getConfig(): array
    {
        return $this->config;
    }



    protected function buildRequestBody(array $messages, array $options): array
    {
        // 转换 OpenAI messages → Gemini contents
        $systemInstructions = '';
        $contents = [];

        foreach ($messages as $msg) {
            if ($msg['role'] === 'system') {
                $systemInstructions .= $msg['content'] . "\n";
                continue;
            }

            $role = match ($msg['role']) {
                'assistant' => 'model',
                'tool'      => 'user',
                default     => 'user',
            };

            if ($msg['role'] === 'tool') {
                $contents[] = [
                    'role'  => 'user',
                    'parts' => [[
                        'functionResponse' => [
                            'name'     => $msg['tool_call_id'] ?? '',
                            'response' => ['content' => $msg['content']],
                        ],
                    ]],
                ];
                continue;
            }

            if ($msg['role'] === 'assistant' && !empty($msg['tool_calls'])) {
                $parts = [];
                foreach ($msg['tool_calls'] as $tc) {
                    $parts[] = [
                        'functionCall' => [
                            'name' => $tc['function']['name'],
                            'args' => json_decode($tc['function']['arguments'], true) ?? [],
                        ],
                    ];
                }
                $contents[] = ['role' => 'model', 'parts' => $parts];
                continue;
            }

            $contents[] = ['role' => $role, 'parts' => [['text' => $msg['content']]]];
        }

        $body = ['contents' => $contents];

        if ($systemInstructions !== '') {
            $body['systemInstruction'] = ['parts' => [['text' => trim($systemInstructions)]]];
        }

        $body['generationConfig'] = [];
        if (isset($options['temperature'])) {
            $body['generationConfig']['temperature'] = $options['temperature'];
        }
        // 默认 maxOutputTokens=4096
        $body['generationConfig']['maxOutputTokens'] = $options['max_tokens'] ?? 4096;

        if (!empty($options['tools'])) {
            $body['tools'] = [['functionDeclarations' => $this->convertTools($options['tools'])]];
        }

        return $body;
    }

    protected function convertTools(array $openaiTools): array
    {
        $declarations = [];
        foreach ($openaiTools as $t) {
            $declarations[] = [
                'name'        => $t['function']['name'],
                'description' => $t['function']['description'] ?? '',
                'parameters'  => $t['function']['parameters'] ?? ['type' => 'object', 'properties' => []],
            ];
        }
        return $declarations;
    }

    protected function getBaseUrl(): string
    {
        return $this->config['base_url'] ?: 'https://generativelanguage.googleapis.com/v1beta';
    }

    protected function request(string $path, array $body): array
    {
        $url = rtrim($this->getBaseUrl(), '/') . $path;
        $attempts = 0;
        $maxRetries = $this->config['max_retries'] ?? 3;
        $lastError = null;

        while ($attempts <= $maxRetries) {
            $response = wp_remote_post($url, [
                'timeout' => $this->config['timeout'] ?? 90,
                'headers' => [
                    'Content-Type'   => 'application/json',
                    'x-goog-api-key' => $this->config['api_key'],
                ],
                'body' => wp_json_encode($body),
            ]);

            if (is_wp_error($response)) {
                $lastError = $response->get_error_message();
                $attempts++;
                // 网络层失败加指数退避
                usleep(min(4000000, 500000 * (2 ** ($attempts - 1))));
                continue;
            }

            $httpCode = wp_remote_retrieve_response_code($response);
            $respBody = wp_remote_retrieve_body($response);
            $data = json_decode($respBody, true);

            if ($httpCode >= 500) {
                $lastError = "服务器错误 {$httpCode}";
                $attempts++;
                usleep(500000 * $attempts);
                continue;
            }

            if ($httpCode >= 400) {
                $errMsg = $data['error']['message'] ?? "HTTP {$httpCode}";
                $errCode = \XZD\AI\V5\AI\ErrorClassifier::classify($errMsg, $httpCode);
                return ['error' => $errMsg, 'error_code' => $errCode];
            }

            if (json_last_error() !== JSON_ERROR_NONE) {
                return ['error' => '响应解析失败', 'error_code' => 'parse_error'];
            }

            return $data;
        }

        return ['error' => $lastError ?? '未知错误', 'error_code' => 'max_retries'];
    }
}
