<?php

namespace App\LLM;

/**
 * DeepSeek provider implementation.
 *
 * DeepSeek is fully OpenAI API-compatible, so the structure mirrors
 * svcOpenAI with DeepSeek-specific endpoints and models.
 *
 * Config keys (from sp_orgs_meta metval JSON):
 *   secretKey   — required — DeepSeek API key
 *   model       — optional — default: deepseek-chat
 *   max_tokens  — optional — default: 4096
 *   temperature — optional — default: 0.7
 *
 * Usage:
 *   $llm = LLMManager::forOrg($orgId, 'deepseek');
 *   $res = $llm->sendPrompt([['role' => 'user', 'content' => 'Hello']]);
 *   echo $res['content'];
 */
class svcDeepSeek extends LLMProvider
{
    private const API_BASE   = 'https://api.deepseek.com/v1';
    private const API_CHAT   = self::API_BASE . '/chat/completions';
    private const API_MODELS = self::API_BASE . '/models';

    private static array $availableModels = [
        ['id' => 'deepseek-chat',      'label' => 'DeepSeek Chat (V3)'],
        ['id' => 'deepseek-reasoner',  'label' => 'DeepSeek Reasoner (R1)'],
    ];

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

        $this->apiKey      = $config['secretKey'];
        $this->model       = $config['model']        ?? 'deepseek-chat';
        $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 {
            $payload  = $this->buildPayload($messages, $options);
            $response = $this->curlPost(self::API_CHAT, $this->headers(), $payload);

            $content = $response['choices'][0]['message']['content'] ?? '';
            $usage   = $response['usage'] ?? [];

            // DeepSeek Reasoner includes reasoning_content separately
            $reasoning = $response['choices'][0]['message']['reasoning_content'] ?? '';

            return $this->successResponse($content, [
                'prompt_tokens'     => $usage['prompt_tokens']     ?? 0,
                'completion_tokens' => $usage['completion_tokens'] ?? 0,
                'total_tokens'      => $usage['total_tokens']      ?? 0,
                'reasoning_tokens'  => $usage['completion_tokens_details']['reasoning_tokens'] ?? 0,
                'reasoning_content' => $reasoning,
            ]);

        } 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 {
            $payload = $this->buildPayload($messages, $options);

            $this->curlStream(self::API_CHAT, $this->headers(), $payload, function(array $chunk) use ($callback) {
                $delta = $chunk['choices'][0]['delta']['content'] ?? '';
                if ($delta !== '') {
                    $callback($delta);
                }
            });

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

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

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

    /**
     * Validate the API key via the models endpoint.
     */
    public function testConnection(): array
    {
        try {
            $start = microtime(true);

            $ch = curl_init(self::API_MODELS);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTPHEADER     => $this->headers(),
                CURLOPT_TIMEOUT        => $this->timeout,
            ]);

            $body  = curl_exec($ch);
            $error = curl_error($ch);
            $code  = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

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

            if ($error) throw new \Exception("cURL error: $error");

            $decoded = json_decode($body, true);

            if ($code !== 200) {
                $msg = $decoded['error']['message'] ?? "HTTP $code";
                throw new \Exception($msg);
            }

            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',
            'Authorization: Bearer ' . $this->apiKey,
        ];
    }

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

        if ($this->systemPrompt !== '') {
            array_unshift($messages, ['role' => 'system', 'content' => $this->systemPrompt]);
        }

        $payload = [
            'model'       => $model,
            'messages'    => $messages,
            'max_tokens'  => $maxTokens,
            'temperature' => $temperature,
        ];

        // deepseek-reasoner does not support temperature
        if ($model === 'deepseek-reasoner') {
            unset($payload['temperature']);
        }

        return $payload;
    }
}
