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

/**
 * Shared text LLM client — OpenAI, Claude (Anthropic), Gemini (Google).
 * Copy into each PEWIT plugin as includes/class-pewit-ai-schema-builder-llm-api.php
 *
 * User-facing strings are plain English here; translate in each plugin via *_llm_localize_message().
 */
class AISB_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;
    }

    /**
     * Whether WordPress 7.0+ core AI Client can handle text generation on this site.
     */
    public static function core_text_generation_supported() {
        if (!function_exists('wp_ai_client_prompt')) {
            return false;
        }
        $builder = self::build_core_text_builder('test', ['max_tokens' => 5]);
        return is_object($builder)
            && method_exists($builder, 'is_supported_for_text_generation')
            && $builder->is_supported_for_text_generation();
    }

    /**
     * @param array{api_key?:string,provider?:string,model?:string,max_tokens?:int,temperature?:float} $args
     * @return object|null
     */
    private static function build_core_text_builder($prompt, array $args) {
        $builder = wp_ai_client_prompt((string) $prompt);
        $max_tokens = max(1, (int) ($args['max_tokens'] ?? 4096));
        if (method_exists($builder, 'using_max_tokens')) {
            $builder = $builder->using_max_tokens($max_tokens);
        }
        if (isset($args['temperature']) && method_exists($builder, 'using_temperature')) {
            $builder = $builder->using_temperature((float) $args['temperature']);
        }
        $model = (string) ($args['model'] ?? '');
        if ($model !== '' && method_exists($builder, 'using_model_preference')) {
            $builder = $builder->using_model_preference($model);
        }
        return $builder;
    }

    /**
     * @param array{api_key?:string,provider?:string,model?:string,max_tokens?:int,temperature?:float} $args
     * @return string|WP_Error
     */
    private static function complete_via_core_ai_client($prompt, array $args) {
        $builder = self::build_core_text_builder($prompt, $args);
        if (!is_object($builder) || !method_exists($builder, 'generate_text')) {
            return new WP_Error('pewit_llm_core_unavailable', 'WordPress AI Client is not available.');
        }
        if (method_exists($builder, 'is_supported_for_text_generation') && !$builder->is_supported_for_text_generation()) {
            return new WP_Error('pewit_llm_core_unsupported', 'WordPress AI connectors are not configured for text generation.');
        }
        $text = $builder->generate_text();
        if (is_wp_error($text)) {
            return $text;
        }
        $text = trim((string) $text);
        if ($text === '') {
            return new WP_Error('pewit_llm_core_empty', 'Empty response from WordPress AI Client.');
        }
        return $text;
    }

    /**
     * @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) {
        if (self::core_text_generation_supported()) {
            $core = self::complete_via_core_ai_client($prompt, $args);
            if (!is_wp_error($core)) {
                return $core;
            }
        }

        $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) {
        if (self::core_text_generation_supported()) {
            $prompt = self::messages_to_prompt($messages);
            $core = self::complete_via_core_ai_client($prompt, $args);
            if (!is_wp_error($core)) {
                return $core;
            }
        }

        $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 messages_to_prompt(array $messages) {
        $parts = [];
        foreach ($messages as $msg) {
            $role = sanitize_key((string) ($msg['role'] ?? 'user'));
            $content = (string) ($msg['content'] ?? '');
            if ($content === '') {
                continue;
            }
            $parts[] = strtoupper($role) . ': ' . $content;
        }
        return implode("\n\n", $parts);
    }

    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;
    }
}
