<?php
if (!defined('ABSPATH')) exit;

/**
 * Shared text LLM client — OpenAI, Claude (Anthropic), Gemini (Google).
 * User-facing strings are plain English here; translate in each plugin via *_llm_localize_message().
 */
class AIVT_LLM_API {

    public static function normalize_provider($provider) {
        $provider = sanitize_key((string) $provider);
        return in_array($provider, ['openai', 'anthropic', 'google'], true) ? $provider : 'openai';
    }

    public static function default_model($provider) {
        switch (self::normalize_provider($provider)) {
            case 'anthropic':
                return 'claude-3-5-haiku-20241022';
            case 'google':
                return 'gemini-2.0-flash';
            default:
                return 'gpt-4o-mini';
        }
    }

    public static function provider_label($provider) {
        switch (self::normalize_provider($provider)) {
            case 'anthropic':
                return 'Claude';
            case 'google':
                return 'Gemini';
            default:
                return 'OpenAI';
        }
    }

    public static function sanitize_provider_setting($provider, $pro_allowed) {
        $provider = self::normalize_provider($provider);
        if (!$pro_allowed && $provider !== 'openai') {
            return 'openai';
        }
        return $provider;
    }

    /**
     * @param array{api_key:string,provider?:string,model?:string,max_tokens?:int,pro_allowed?:bool} $args
     */
    public static function test(array $args) {
        $args['max_tokens'] = 20;
        $prompt = 'Reply with OK only.';
        $res = self::complete($prompt, $args);
        if (is_wp_error($res)) {
            return ['ok' => false, 'message' => $res->get_error_message()];
        }
        return ['ok' => true, 'message' => 'Connected successfully'];
    }

    /**
     * @param array{api_key:string,provider?:string,model?:string,max_tokens?:int,pro_allowed?:bool} $args
     * @return string|WP_Error
     */
    public static function complete($prompt, array $args) {
        $provider = self::normalize_provider($args['provider'] ?? 'openai');
        $key = (string) ($args['api_key'] ?? '');
        $model = (string) ($args['model'] ?? '');
        if ($model === '') {
            $model = self::default_model($provider);
        }
        $max_tokens = max(1, (int) ($args['max_tokens'] ?? 4096));
        $pro_allowed = !empty($args['pro_allowed']);

        if ($key === '') {
            return new WP_Error('pewit_llm_no_key', 'API key is empty');
        }
        if (!$pro_allowed && $provider !== 'openai') {
            return new WP_Error('pewit_llm_pro', 'Claude and Gemini require Pro or trial.');
        }

        switch ($provider) {
            case 'anthropic':
                return self::complete_anthropic($key, $model, $prompt, $max_tokens);
            case 'google':
                return self::complete_google($key, $model, $prompt, $max_tokens);
            default:
                return self::complete_openai($key, $model, $prompt, $max_tokens);
        }
    }

    /**
     * @param array<int,array{role:string,content:string}> $messages
     * @param array{api_key:string,provider?:string,model?:string,max_tokens?:int,temperature?:float,pro_allowed?:bool} $args
     * @return string|WP_Error
     */
    public static function chat_messages(array $messages, array $args) {
        $provider = self::normalize_provider($args['provider'] ?? 'openai');
        $key = (string) ($args['api_key'] ?? '');
        $model = (string) ($args['model'] ?? '');
        if ($model === '') {
            $model = self::default_model($provider);
        }
        $max_tokens = max(1, (int) ($args['max_tokens'] ?? 800));
        $temperature = isset($args['temperature']) ? (float) $args['temperature'] : 0.7;
        $pro_allowed = !empty($args['pro_allowed']);

        if ($key === '') {
            return new WP_Error('pewit_llm_no_key', 'API key is empty');
        }
        if (!$pro_allowed && $provider !== 'openai') {
            return new WP_Error('pewit_llm_pro', 'Claude and Gemini require Pro or trial.');
        }

        switch ($provider) {
            case 'anthropic':
                return self::chat_anthropic($key, $model, $messages, $max_tokens);
            case 'google':
                return self::chat_google($key, $model, $messages, $max_tokens, $temperature);
            default:
                return self::chat_openai($key, $model, $messages, $max_tokens, $temperature);
        }
    }

    private static function complete_openai($key, $model, $prompt, $max_tokens) {
        $res = wp_remote_post('https://api.openai.com/v1/responses', [
            'timeout' => 120,
            'headers' => [
                'Authorization' => 'Bearer ' . $key,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode([
                'model'             => $model,
                'input'             => [[
                    'role'    => 'user',
                    'content' => [['type' => 'input_text', 'text' => $prompt]],
                ]],
                'max_output_tokens' => $max_tokens,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
        return self::parse_openai_response($res);
    }

    private static function chat_openai($key, $model, array $messages, $max_tokens, $temperature) {
        $res = wp_remote_post('https://api.openai.com/v1/chat/completions', [
            'timeout' => 120,
            'headers' => [
                'Authorization' => 'Bearer ' . $key,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode([
                'model'       => $model,
                'messages'    => $messages,
                'max_tokens'  => $max_tokens,
                'temperature' => $temperature,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
        if (is_wp_error($res)) {
            return $res;
        }
        $code = wp_remote_retrieve_response_code($res);
        $body = json_decode(wp_remote_retrieve_body($res), true);
        if ($code < 200 || $code >= 300) {
            return new WP_Error('pewit_llm_api', self::http_error_message($body, $code));
        }
        $text = trim((string) ($body['choices'][0]['message']['content'] ?? ''));
        if ($text === '') {
            return new WP_Error('pewit_llm_api', 'Empty response from OpenAI.');
        }
        return $text;
    }

    private static function complete_anthropic($key, $model, $prompt, $max_tokens) {
        return self::chat_anthropic($key, $model, [['role' => 'user', 'content' => $prompt]], $max_tokens);
    }

    private static function chat_anthropic($key, $model, array $messages, $max_tokens) {
        $system = '';
        $api_messages = [];
        foreach ($messages as $msg) {
            $role = $msg['role'] ?? 'user';
            $content = (string) ($msg['content'] ?? '');
            if ($role === 'system') {
                $system .= ($system ? "\n\n" : '') . $content;
                continue;
            }
            $api_messages[] = [
                'role'    => $role === 'assistant' ? 'assistant' : 'user',
                'content' => $content,
            ];
        }
        if (!$api_messages) {
            return new WP_Error('pewit_llm_api', 'No messages to send.');
        }

        $payload = [
            'model'      => $model ?: 'claude-3-5-haiku-20241022',
            'max_tokens' => $max_tokens,
            'messages'   => $api_messages,
        ];
        if ($system !== '') {
            $payload['system'] = $system;
        }

        $res = wp_remote_post('https://api.anthropic.com/v1/messages', [
            'timeout' => 120,
            'headers' => [
                'x-api-key'         => $key,
                'anthropic-version' => '2023-06-01',
                'Content-Type'      => 'application/json',
            ],
            'body' => wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
        if (is_wp_error($res)) {
            return $res;
        }
        $code = wp_remote_retrieve_response_code($res);
        $body = json_decode(wp_remote_retrieve_body($res), true);
        if ($code < 200 || $code >= 300) {
            $msg = $body['error']['message'] ?? self::http_error_message($body, $code);
            return new WP_Error('pewit_llm_api', $msg);
        }
        if (!empty($body['content'][0]['text'])) {
            return (string) $body['content'][0]['text'];
        }
        return new WP_Error('pewit_llm_api', 'Empty response from Claude.');
    }

    private static function complete_google($key, $model, $prompt, $max_tokens) {
        return self::chat_google($key, $model, [['role' => 'user', 'content' => $prompt]], $max_tokens, 0.7);
    }

    private static function chat_google($key, $model, array $messages, $max_tokens, $temperature) {
        $system = '';
        $contents = [];
        foreach ($messages as $msg) {
            $role = $msg['role'] ?? 'user';
            $content = (string) ($msg['content'] ?? '');
            if ($role === 'system') {
                $system .= ($system ? "\n\n" : '') . $content;
                continue;
            }
            $contents[] = [
                'role'  => $role === 'assistant' ? 'model' : 'user',
                'parts' => [['text' => $content]],
            ];
        }
        if (!$contents) {
            return new WP_Error('pewit_llm_api', 'No messages to send.');
        }

        $model = $model ?: 'gemini-2.0-flash';
        $url = 'https://generativelanguage.googleapis.com/v1beta/models/' . rawurlencode($model) . ':generateContent?key=' . rawurlencode($key);
        $payload = [
            'contents'         => $contents,
            'generationConfig' => [
                'maxOutputTokens' => $max_tokens,
                'temperature'     => $temperature,
            ],
        ];
        if ($system !== '') {
            $payload['systemInstruction'] = ['parts' => [['text' => $system]]];
        }

        $res = wp_remote_post($url, [
            'timeout' => 120,
            'headers' => ['Content-Type' => 'application/json'],
            'body'    => wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
        if (is_wp_error($res)) {
            return $res;
        }
        $code = wp_remote_retrieve_response_code($res);
        $body = json_decode(wp_remote_retrieve_body($res), true);
        if ($code < 200 || $code >= 300) {
            $msg = $body['error']['message'] ?? self::http_error_message($body, $code);
            return new WP_Error('pewit_llm_api', $msg);
        }
        $text = $body['candidates'][0]['content']['parts'][0]['text'] ?? '';
        if ($text === '') {
            return new WP_Error('pewit_llm_api', 'Empty response from Gemini.');
        }
        return (string) $text;
    }

    private static function parse_openai_response($res) {
        if (is_wp_error($res)) {
            return $res;
        }
        $code = wp_remote_retrieve_response_code($res);
        $body = json_decode(wp_remote_retrieve_body($res), true);
        if ($code < 200 || $code >= 300) {
            return new WP_Error('pewit_llm_api', self::http_error_message($body, $code));
        }
        if (!empty($body['output_text'])) {
            return (string) $body['output_text'];
        }
        if (!empty($body['output'][0]['content'])) {
            $out = '';
            foreach ($body['output'][0]['content'] as $c) {
                if (!empty($c['text'])) {
                    $out .= $c['text'];
                }
            }
            if ($out !== '') {
                return $out;
            }
        }
        return new WP_Error('pewit_llm_api', 'Empty response from OpenAI.');
    }

    private static function http_error_message($body, $code) {
        $msg = is_array($body) ? ($body['error']['message'] ?? ('HTTP ' . $code)) : ('HTTP ' . $code);
        if ((int) $code === 401) {
            return 'Invalid API key or unauthorized account.';
        }
        if ((int) $code === 429) {
            return 'Rate limit or billing issue. ' . $msg;
        }
        return $msg;
    }
}
