<?php

namespace XZD\AI\V5\AI;

/**
 * Anthropic Claude Provider
 *
 * 请求格式: POST /messages
 * 认证: x-api-key: {API_KEY} + anthropic-version: 2023-06-01
 */
class ClaudeProvider implements ProviderInterface
{
    protected array $config;

    public function __construct(array $config)
    {
        $this->config = array_merge([
            'provider'    => 'claude',
            'api_key'     => '',
            'base_url'    => 'https://api.anthropic.com/v1',
            'model'       => 'claude-sonnet-4-6',
            '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, false);
        $result = $this->request('/messages', $body);

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

        $content = '';
        $toolCalls = null;
        foreach (($result['content'] ?? []) as $block) {
            if ($block['type'] === 'text') {
                $content .= $block['text'];
            } elseif ($block['type'] === 'tool_use') {
                $toolCalls[] = [
                    'id'   => $block['id'],
                    'type' => 'function',
                    'function' => [
                        'name'      => $block['name'],
                        'arguments' => wp_json_encode($block['input']),
                    ],
                ];
            }
        }

        return Types::response(
            content: $content,
            inputTokens: $result['usage']['input_tokens'] ?? 0,
            outputTokens: $result['usage']['output_tokens'] ?? 0,
            toolCalls: $toolCalls,
        );
    }

    public function chatStream(array $messages, array $options = [], ?callable $onChunk = null): \Generator
    {
        $body = $this->buildRequestBody($messages, $options, true);
        $url = rtrim($this->getBaseUrl(), '/') . '/messages';

        $chunks = [];

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => wp_json_encode($body),
            CURLOPT_HTTPHEADER     => $this->buildCurlHeaders(),
            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;
                    $event = $json['type'] ?? '';
                    $chunk = null;
                    switch ($event) {
                        case 'content_block_delta':
                            $delta = $json['delta'] ?? [];
                            $chunk = ['delta' => $delta['text'] ?? '', 'type' => $delta['type'] === 'input_json_delta' ? 'tool_call' : 'text', 'done' => false];
                            break;
                        case 'message_stop':
                            $chunk = ['delta' => '', 'type' => 'text', 'done' => true];
                            break;
                        case 'error':
                            $chunk = ['delta' => '', 'type' => 'error', 'done' => true, 'error' => $json['error']['message'] ?? 'Unknown'];
                            break;
                    }
                    if ($chunk) {
                        $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);

        // V3.1.7: 检查 curl 错误和 HTTP 状态码
        if ($curlError) {
            yield ['delta' => '', 'type' => 'error', 'done' => true, 'error' => "连接失败: {$curlError}"];
            return;
        }
        if ($httpCode >= 400) {
            yield ['delta' => '', 'type' => 'error', 'done' => true, 'error' => "API 返回错误 (HTTP {$httpCode})"];
            return;
        }

        if (empty($chunks)) {
            yield ['delta' => '', 'type' => 'error', 'done' => true, 'error' => 'API 返回空响应'];
            return;
        }

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

    public function testConnection(): array
    {
        $result = $this->chat(
            [Types::user('Hi')],
            ['max_tokens' => 10]
        );
        return isset($result['error'])
            ? ['success' => false, 'message' => $result['error']]
            : ['success' => true, 'message' => '连接正常'];
    }

    public function getModels(): array
    {
        return ['success' => false, 'models' => [], 'error' => 'Claude API 不支持 /models 端点，请手动输入模型名'];
    }

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

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

    // ===== 内部 =====

    protected function buildRequestBody(array $messages, array $options, bool $stream): array
    {
        // 转换 OpenAI messages 格式 → Anthropic 格式
        $systemPrompt = '';
        $anthropicMessages = [];

        foreach ($messages as $msg) {
            if ($msg['role'] === 'system') {
                $systemPrompt .= $msg['content'] . "\n";
            } elseif ($msg['role'] === 'tool') {
                $anthropicMessages[] = [
                    'role'    => 'user',
                    'content' => [[
                        'type'        => 'tool_result',
                        'tool_use_id' => $msg['tool_call_id'],
                        'content'     => $msg['content'],
                    ]],
                ];
            } elseif ($msg['role'] === 'assistant' && !empty($msg['tool_calls'])) {
                $contentBlocks = [];
                if (!empty($msg['content'])) {
                    $contentBlocks[] = ['type' => 'text', 'text' => $msg['content']];
                }
                foreach ($msg['tool_calls'] as $tc) {
                    $contentBlocks[] = [
                        'type'  => 'tool_use',
                        'id'    => $tc['id'],
                        'name'  => $tc['function']['name'],
                        'input' => json_decode($tc['function']['arguments'], true) ?? [],
                    ];
                }
                $anthropicMessages[] = ['role' => 'assistant', 'content' => $contentBlocks];
            } else {
                $anthropicMessages[] = [
                    'role'    => $msg['role'],
                    'content' => $msg['content'],
                ];
            }
        }

        $body = [
            'model'      => $this->config['model'] ?: 'claude-sonnet-4-6',
            'max_tokens' => $options['max_tokens'] ?? 4096,
            'messages'   => $anthropicMessages,
            'stream'     => $stream,
        ];

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

        return $body;
    }

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

    protected function buildHeaders(): array
    {
        return [
            'x-api-key'          => $this->config['api_key'],
            'anthropic-version'  => '2023-06-01',
            'Content-Type'       => 'application/json',
        ];
    }

    protected function buildCurlHeaders(): array
    {
        $headers = [];
        foreach ($this->buildHeaders() as $k => $v) {
            $headers[] = "{$k}: {$v}";
        }
        return $headers;
    }

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

    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' => $this->buildHeaders(),
                'body'    => wp_json_encode($body),
            ]);

            if (is_wp_error($response)) {
                $lastError = $response->get_error_message();
                $attempts++;
                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'];
    }
}
