<?php
/**
 * Google Gemini Provider.
 *
 * Implements the Google Gemini API.
 *
 * @package Inqyra
 */

// If this file is called directly, abort.
if (!defined('WPINC')) {
    die;
}

/**
 * Gemini Provider class.
 */
class Inqyra_Gemini_Provider extends Inqyra_AI_Provider_Abstract {

    /**
     * API endpoint base.
     */
    const API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models/';

    /**
     * Pricing per 1M tokens (input/output) by model.
     * Updated January 2025.
     */
    const PRICING = [
        'gemini-2.5-flash' => ['input' => 0.15, 'output' => 0.60],
        'gemini-2.5-flash-lite' => ['input' => 0.075, 'output' => 0.30],
        'gemini-2.5-pro' => ['input' => 1.25, 'output' => 5.00],
        'gemini-2.0-flash' => ['input' => 0.10, 'output' => 0.40],
        'gemini-2.0-flash-lite' => ['input' => 0.075, 'output' => 0.30],
    ];

    /**
     * Send a chat completion request.
     *
     * @param array $messages Array of messages with 'role' and 'content'.
     * @param array $options  Additional options.
     * @return array Response with 'content', 'tokens', and 'model'.
     * @throws Exception On API error.
     */
    public function chat(array $messages, array $options = []): array {
        if (!$this->is_configured()) {
            throw new Exception(esc_html(__('Gemini API key not configured.', 'inqyra')));
        }

        $model = $options['model'] ?? $this->get_default_model();
        $max_tokens = $options['max_tokens'] ?? 4096;

        // Convert messages to Gemini format
        $contents = [];
        $system_instruction = '';

        foreach ($messages as $message) {
            if ($message['role'] === 'system') {
                $system_instruction .= ($system_instruction ? "\n\n" : '') . $message['content'];
            } else {
                $contents[] = [
                    'role' => $message['role'] === 'assistant' ? 'model' : 'user',
                    'parts' => [['text' => $message['content']]],
                ];
            }
        }

        $generation_config = [
            'maxOutputTokens' => $max_tokens,
        ];

        if (isset($options['temperature'])) {
            $generation_config['temperature'] = (float) $options['temperature'];
        }

        $request_data = [
            'contents' => $contents,
            'generationConfig' => $generation_config,
        ];

        if ($system_instruction) {
            $request_data['systemInstruction'] = [
                'parts' => [['text' => $system_instruction]],
            ];
        }

        $url = self::API_BASE . $model . ':generateContent';

        $response = $this->make_request($url, $request_data, [
            'x-goog-api-key' => $this->api_key,
        ]);

        $content = $response['candidates'][0]['content']['parts'][0]['text'] ?? '';
        $tokens = [
            'input' => $response['usageMetadata']['promptTokenCount'] ?? 0,
            'output' => $response['usageMetadata']['candidatesTokenCount'] ?? 0,
        ];

        $cost = $this->calculate_cost($tokens['input'], $tokens['output'], $model);

        $this->log_api_call('gemini', $model, $tokens, $cost);

        return [
            'content' => $content,
            'tokens' => $tokens,
            'model' => $model,
            'cost' => $cost,
        ];
    }

    /**
     * Get the provider identifier.
     *
     * @return string Provider ID.
     */
    public function get_id(): string {
        return 'gemini';
    }

    /**
     * Get the provider name.
     *
     * @return string Provider name.
     */
    public function get_name(): string {
        return 'Google Gemini';
    }

    /**
     * Get the default model.
     *
     * @return string Default model ID.
     */
    public function get_default_model(): string {
        return 'gemini-2.5-flash';
    }

    /**
     * Calculate cost for token usage.
     *
     * @param int    $input_tokens  Number of input tokens.
     * @param int    $output_tokens Number of output tokens.
     * @param string $model         Model identifier.
     * @return float Cost in USD.
     */
    public function calculate_cost(int $input_tokens, int $output_tokens, string $model): float {
        $pricing = self::PRICING[$model] ?? self::PRICING['gemini-2.0-flash'];

        $input_cost = ($input_tokens / 1000000) * $pricing['input'];
        $output_cost = ($output_tokens / 1000000) * $pricing['output'];

        return $input_cost + $output_cost;
    }

    /**
     * Extract error message from API response.
     *
     * @param array|null $response    The decoded response.
     * @param int        $status_code The HTTP status code.
     * @return string Error message.
     */
    protected function extract_error_message($response, int $status_code): string {
        if (is_array($response) && isset($response['error']['message'])) {
            return $response['error']['message'];
        }

        return parent::extract_error_message($response, $status_code);
    }

    /**
     * Get hardcoded models - curated list of recommended chat models.
     *
     * @return array Model list with pricing.
     */
    public function get_hardcoded_models(): array {
        return [
            'gemini-2.0-flash' => 'Gemini 2.0 Flash ($0.10/$0.40 per 1M)',
            'gemini-2.0-flash-lite' => 'Gemini 2.0 Flash Lite ($0.075/$0.30 per 1M)',
        ];
    }

    /**
     * Fetch available models from the Gemini API.
     *
     * @return array Associative array of model_id => display_name.
     */
    public function fetch_available_models(): array {
        if (!$this->is_configured()) {
            return [];
        }

        $response = $this->make_get_request(
            'https://generativelanguage.googleapis.com/v1beta/models',
            ['x-goog-api-key' => $this->api_key]
        );

        $models = [];
        if (!empty($response['models']) && is_array($response['models'])) {
            foreach ($response['models'] as $model) {
                $methods = $model['supportedGenerationMethods'] ?? [];
                if (!in_array('generateContent', $methods, true)) {
                    continue;
                }

                // Model name format: "models/gemini-2.0-flash" → extract ID
                $full_name = $model['name'] ?? '';
                $id = str_replace('models/', '', $full_name);
                $display_name = $model['displayName'] ?? $id;

                if ($id) {
                    $models[$id] = $display_name;
                }
            }
        }

        return $models;
    }

    /**
     * Get pricing for a specific model.
     *
     * @param string $model Model identifier.
     * @return array Pricing with 'input' and 'output' per 1M tokens.
     */
    protected function get_model_pricing(string $model): array {
        // Check exact match first
        if (isset(self::PRICING[$model])) {
            return self::PRICING[$model];
        }

        // Check for partial matches (e.g., gemini-2.5-flash-preview matches gemini-2.5-flash)
        foreach (self::PRICING as $price_model => $pricing) {
            if (strpos($model, $price_model) === 0) {
                return $pricing;
            }
        }

        // Default pricing if unknown
        return ['input' => 0, 'output' => 0];
    }
}
