<?php

namespace ZenoChatbot\Services\LLMService\AIClient\Providers\Adapters;

use ZenoChatbot\Services\HttpClient;
use ZenoChatbot\Services\LLMService\AIClient\Config\AiConfig;
use ZenoChatbot\Services\LLMService\AIClient\Providers\Gemini;
use ZenoChatbot\Services\LLMService\AIClient\Results\Candidate;
use ZenoChatbot\Services\LLMService\AIClient\Results\GenerativeAiResult;
use ZenoChatbot\Services\LLMService\AIClient\Results\TokenUsage;
use ZenoChatbot\Services\LLMService\AIClient\Support\ResponseParser;

if (!defined('ABSPATH')) {
    exit;
}

class GeminiTextModel implements AdapterInterface
{
    public function generate(array $input, AiConfig $config): GenerativeAiResult
    {
        // Build payload using dedicated provider prompt builder
        $payload = \ZenoChatbot\Services\LLMService\AIClient\Providers\PromptBuilders\GeminiPromptBuilder::buildPayload($config, $input);

        $model = $config->getModel();
        $client = new HttpClient();

        // Try stable v1 first, then fall back to v1beta for older/experimental models
        $bases = [Gemini::API_V1, Gemini::API_V1BETA];
        $body = null;
        $lastError = null;
        foreach ($bases as $base) {
            $url = sprintf('%s/models/%s%s?key=%s', $base, rawurlencode($model), Gemini::GENERATE_CONTENT_SUFFIX, rawurlencode($config->getApiKey()));
            try {
                $resp = $client->post($url, $payload);
                $body = $resp['body'] ?? [];
                if (is_array($body)) { break; }
            } catch (\Throwable $e) {
                $lastError = $e;
                $body = null;
                continue;
            }
        }
        if ($body === null) {
            if ($lastError) { throw $lastError; }
            throw new \Exception('Gemini API request failed');
        }

        $candidates = [];
        $finishReason = null;
        if (!empty($body['candidates']) && is_array($body['candidates'])) {
            foreach ($body['candidates'] as $cand) {
                $txt = '';
                if (!empty($cand['content']['parts'])) {
                    foreach ($cand['content']['parts'] as $p) {
                        $txt .= (string)($p['text'] ?? '');
                    }
                }
                $candidates[] = new Candidate($txt, $cand['finishReason'] ?? null);
                $finishReason = $finishReason ?? ($cand['finishReason'] ?? null);
            }
        }

        // If no candidates returned but a fallback was provided (strict mode), use the fallback
        if (empty($candidates) && !empty($input['fallback'])) {
            $candidates[] = new Candidate((string)$input['fallback'], 'STOP');
        }

        $usageMeta = $body['usageMetadata'] ?? [];
        $tokenUsage = new TokenUsage(
            (int)($usageMeta['promptTokenCount'] ?? 0),
            (int)($usageMeta['candidatesTokenCount'] ?? 0),
            (int)($usageMeta['totalTokenCount'] ?? 0)
        );

        $citations = ResponseParser::extractCitations($candidates);

        // Prepare raw request (sanitize API key)
        $rawRequest = ResponseParser::sanitizeRequest($input, [
            'contents_count' => count($payload['contents'] ?? []),
            'has_system_instruction' => isset($payload['systemInstruction']),
            'generation_config' => $payload['generationConfig'] ?? null,
        ]);

        // Prepare raw response
        $rawResponse = $body;

        return GenerativeAiResult::create([
            'candidates'    => $candidates,
            'token_usage'   => $tokenUsage,
            'finish_reason' => $finishReason,
            'citations'     => $citations,
            'provider'      => 'gemini',
            'model'         => $config->getModel(),
            'raw_response'  => $rawResponse,
            'raw_request'   => $rawRequest,
        ]);
    }

}

