<?php



namespace HaoZiTeam\ChatGPT;



use Exception;

use GuzzleHttp\Client;

use GuzzleHttp\Exception\GuzzleException;



class V2

{

    private $baseUrl = 'https://api.openai.com/';

    private $model;

    private $key;

    private $temperature = 1;

    private $topP = 1;

    private $messages = [];

    private $http;

    private $isDeepseek = false;

    private $isDoubao = false;

    private $isBaiLian = false;

    private $customHeaders = [];

    private $requestFormat = '';

    private $verify = true; // Guzzle verify: true|false|string(path)
    private $caBundle = null; // detected CA bundle path if any



    /**
     * 初始化ChatGPT对象
     * @param string $key API密钥
     * @param string|null $baseUrl 基础URL，自定义服务器可配置，DeepSeek和豆包API地址已内置固定
     * @param string|null $model 使用的模型
     * @param int|null $temperature 温度
     * @param int|null $topP Top P
     * @param int $timeout 超时时间
     * @param array $options 其他选项
     */
    public function __construct(

        string $key,

        string $baseUrl = null,

        string $model = null,

        int $temperature = null,

        int $topP = null,

        int $timeout = 360,

        array $options = []

    ) {

        // 确保key格式正确
        if (empty($key)) {
            throw new Exception('API key is empty');
        }
        
        // 如果key不以Bearer开头，则添加Bearer前缀
        if (strpos($key, 'Bearer ') !== 0 && strpos($key, 'bearer ') !== 0) {
            $this->key = 'Bearer ' . $key;
            error_log("ChatGPT-PHP: 已添加Bearer前缀到API密钥");
        } else {
            $this->key = $key;
            error_log("ChatGPT-PHP: API密钥已包含Bearer前缀");
        }

        if ($baseUrl) {
            $this->baseUrl = $baseUrl;
        }
        
        if ($model) {
            $this->model = $model; // Use the model provided
            error_log("ChatGPT-PHP: 使用指定模型: {$model}");
        } else {
            // 设置默认模型
            $this->model = 'gpt-3.5-turbo';
            error_log("ChatGPT-PHP: 未指定模型，使用默认值: gpt-3.5-turbo");
        }

        if ($temperature) {
            $this->temperature = $temperature;
        }

        if ($topP) {
            $this->topP = $topP;
        }

        $this->isDeepseek = $options['is_deepseek'] ?? false;
        $this->isDoubao = $options['is_doubao'] ?? false;
        $this->isBaiLian = $options['is_bai_lian'] ?? false;
        $this->customHeaders = $options['headers'] ?? [];
        $this->requestFormat = $options['request_format'] ?? '';

        // SSL verify handling: allow caller to pass verify option, otherwise try to detect CA bundle
        if (array_key_exists('verify', $options)) {
            $this->verify = $options['verify'];
            if (is_string($this->verify)) {
                $this->caBundle = $this->verify;
            }
        } else {
            $this->caBundle = $this->resolveCABundlePath();
            if ($this->caBundle && @file_exists($this->caBundle)) {
                $this->verify = $this->caBundle;
            } else {
                $this->verify = true; // fallback to default
            }
        }

        // 确保 baseUrl 末尾有斜杠，防止路径拼接问题
        if ($this->baseUrl && substr($this->baseUrl, -1) !== '/') {
            $this->baseUrl .= '/';
            error_log("ChatGPT-PHP: 已为baseUrl添加末尾斜杠: {$this->baseUrl}");
        }

        $clientOpts = [
            'base_uri' => $this->baseUrl,
            'timeout' => $timeout,
            // 关闭流式模式，避免某些主机环境下的 cURL 句柄配置兼容问题
            'stream' => false,
            // 显式关闭 Guzzle 调试，防止在禁用 readlink() 的环境中触发 CurlFactory 调试路径崩溃
            'debug' => false,
            'verify' => $this->verify,
        ];
        $this->http = new Client($clientOpts);
        
        $verify_msg = is_bool($this->verify)
            ? ('verify=' . ($this->verify ? 'true' : 'false'))
            : ('verify_path=' . (string)$this->verify);
        error_log("ChatGPT-PHP: 客户端初始化完成，baseUrl: {$this->baseUrl}, 模型: {$this->model}, {$verify_msg}");
    }

    private function resolveCABundlePath()
    {
        // Priority: env -> ini -> common locations
        $candidates = [];
        $env1 = getenv('CURL_CA_BUNDLE');
        $env2 = getenv('SSL_CERT_FILE');
        if (!empty($env1)) $candidates[] = $env1;
        if (!empty($env2)) $candidates[] = $env2;
        $ini1 = ini_get('curl.cainfo');
        $ini2 = ini_get('openssl.cafile');
        if (!empty($ini1)) $candidates[] = $ini1;
        if (!empty($ini2)) $candidates[] = $ini2;
        // WordPress paths (if running inside WP)
        if (defined('WP_CONTENT_DIR')) {
            $candidates[] = WP_CONTENT_DIR . '/uploads/cacert.pem';
            $candidates[] = WP_CONTENT_DIR . '/plugins/ai-post/resources/cacert.pem';
        }
        // Common Windows BtSoft paths (best-effort)
        $candidates[] = 'D:\\BtSoft\\php\\80\\extras\\ssl\\cacert.pem';
        $candidates[] = 'D:\\BtSoft\\php\\82\\extras\\ssl\\cacert.pem';

        foreach ($candidates as $p) {
            if (!empty($p) && @file_exists($p)) {
                $size = @filesize($p);
                error_log('ChatGPT-PHP: 检测到可用 CA 路径: ' . $p . ' (size=' . ($size === false ? 'n/a' : $size) . ' bytes)');
                return $p;
            }
        }
        error_log('ChatGPT-PHP: 未检测到可用 CA 路径，使用系统默认 verify');
        return null;
    }



    public function getModel()

    {

        return $this->model; // Add a method to retrieve the model if needed

    }



    /**

     * 添加消息

     * @param  string  $message

     * @param  string  $role

     * @return void

     */

    public function addMessage(string $message, string $role = 'user'): void

    {

        $this->messages[] = [

            'role' => $role,

            'content' => $message,

        ];

    }



    /**

     * 发送消息

     * @param  string  $prompt

     * @param  string|null  $user

     * @param  bool  $stream

     * @return mixed

     * @throws Exception

     */

    public function ask(string $prompt, string $user = null, bool $stream = false)

    {

        if ($this->isDeepseek) {

            error_log("DeepSeek ask method called");

            error_log("Prompt: " . $prompt);

            

            $this->addMessage($prompt);

            error_log("Messages: " . json_encode($this->messages));



            $data = [

                'model' => $this->model,

                'messages' => $this->messages,

                'temperature' => 0.7,

                'max_tokens' => 2000,

                'stream' => false

            ];

            error_log("Request data: " . json_encode($data));



            try {

                // 使用rtrim移除baseUrl末尾的斜杠，然后使用正确格式拼接路径
                $apiEndpoint = rtrim($this->baseUrl, '/') . '/chat/completions';
                error_log("Sending request to: " . $apiEndpoint);
                $response = $this->http->post('chat/completions', [

                    'headers' => array_merge([

                        'Authorization' => $this->key,

                        'Content-Type' => 'application/json',

                    ], $this->customHeaders),

                    'json' => $data

                ]);

                

                $result = json_decode($response->getBody()->getContents(), true);

                error_log("API Response: " . json_encode($result));

                

                if (isset($result['error'])) {

                    error_log("API Error: " . json_encode($result['error']));

                    throw new Exception($result['error']['message'] ?? 'Unknown DeepSeek API error');

                }



                if (isset($result['choices'][0]['message']['content'])) {

                    $this->addMessage($result['choices'][0]['message']['content'], 'assistant');

                }

                

                return [

                    'answer' => $result['choices'][0]['message']['content'] ?? '',

                    'id' => $result['id'] ?? '',

                    'model' => $this->model,

                    'usage' => $result['usage'] ?? []

                ];

            } catch (Exception $e) {

                error_log("DeepSeek request failed: " . $e->getMessage());

                error_log("Stack trace: " . $e->getTraceAsString());

                throw $e;

            }

        } elseif ($this->isDoubao) {
            error_log("Doubao ask method called");
            error_log("Prompt: " . $prompt);

            $this->addMessage($prompt);
            error_log("Messages: " . json_encode($this->messages));

            $data = [
                'model' => $this->model,
                'messages' => $this->messages,
                'stream' => false
            ];
            error_log("Request data: " . json_encode($data));

            try {
                // 修正 API 路径，确保使用正确格式拼接
                $apiEndpoint = rtrim($this->baseUrl, '/') . '/chat/completions';
                error_log("Sending request to: " . $apiEndpoint);
                
                $response = $this->http->post('chat/completions', [
                    'headers' => array_merge([
                    ], $this->customHeaders),
                    'json' => $data
                ]);

                $result = json_decode($response->getBody()->getContents(), true);
                error_log("Doubao API Response: " . json_encode($result));

                if (isset($result['error'])) {
                    error_log("Doubao API Error: " . json_encode($result['error']));
                    throw new Exception($result['error']['message'] ?? 'Doubao API error');
                }

                $answer = '';
                if (isset($result['choices'][0]['message']['content'])) {
                    $answer = $result['choices'][0]['message']['content'];
                    $this->addMessage($answer, 'assistant');
                }

                return [
                    'answer' => $answer,
                    'id' => $result['id'] ?? ('doubao-' . uniqid()),
                    'model' => $this->model,
                    'usage' => $result['usage'] ?? []
                ];

            } catch (GuzzleException $e) {
                error_log("GuzzleException: " . $e->getMessage());
                throw new Exception("Doubao API request failed: " . $e->getMessage());
            }
        } elseif ($this->isBaiLian) {
            error_log("Bai Lian ask method called");
            error_log("Prompt: " . $prompt);
            
            $this->addMessage($prompt);
            error_log("Messages: " . json_encode($this->messages));

            // 获取传入的温度参数，如果未指定则使用默认值0.7
            $temperature = isset($this->temperature) && $this->temperature > 0 ? $this->temperature : 0.7;
            
            $data = [
                'model' => $this->model,
                'messages' => $this->messages,
                'stream' => false,
                'temperature' => $temperature
            ];
            error_log("Request data: " . json_encode($data));

            try {
                // 使用阿里百炼的兼容模式API
                $apiEndpoint = rtrim($this->baseUrl, '/') . '/chat/completions';
                error_log("Sending request to: " . $apiEndpoint);
                
                $response = $this->http->post('chat/completions', [
                    'headers' => array_merge([
                        'Authorization' => $this->key, // 确保使用正确的授权头
                        'Content-Type' => 'application/json'
                    ], $this->customHeaders),
                    'json' => $data
                ]);

                $result = json_decode($response->getBody()->getContents(), true);
                error_log("Bai Lian API Response: " . json_encode($result));

                if (isset($result['error'])) {
                    error_log("Bai Lian API Error: " . json_encode($result['error']));
                    throw new Exception($result['error']['message'] ?? 'Bai Lian API error');
                }

                $answer = '';
                if (isset($result['choices'][0]['message']['content'])) {
                    $answer = $result['choices'][0]['message']['content'];
                    $this->addMessage($answer, 'assistant');
                }

                return [
                    'answer' => $answer,
                    'id' => $result['id'] ?? ('bai-lian-' . uniqid()),
                    'model' => $this->model,
                    'usage' => $result['usage'] ?? []
                ];

            } catch (GuzzleException $e) {
                error_log("GuzzleException: " . $e->getMessage());
                throw new Exception("Bai Lian API request failed: " . $e->getMessage());
            }
        } else {
            error_log("Standard API ask method called");
            // Use multibyte-safe substring to prevent garbled logs when prompt contains CJK characters
            $___prompt_preview = function_exists('mb_substr') ? mb_substr($prompt, 0, 100) : substr($prompt, 0, 100);
            error_log("Prompt: " . $___prompt_preview . "...");
            error_log("Using model: " . $this->model);
            error_log("API URL: " . $this->baseUrl);
            
            // 检查密钥
            $keyHint = substr($this->key, 0, 10) . "..." . substr($this->key, -5);
            error_log("Using key: " . $keyHint);
            
            $this->addMessage($prompt);

            $data = [
                'model' => $this->model,
                'messages' => $this->messages,
                'stream' => $stream,
                'temperature' => $this->temperature,
                'top_p' => $this->topP,
                'n' => 1,
                'user' => $user ?? 'chatgpt-php',
            ];
            
            error_log("Request data prepared: " . json_encode([
                'model' => $this->model,
                'messages_count' => count($this->messages),
                'stream' => $stream,
                'temperature' => $this->temperature,
            ]));

            try {
                // 说明：此处的 baseUrl 已在上游规范化为以 "/v1/" 结尾
                $endpoint = rtrim($this->baseUrl, '/') . '/chat/completions';
                error_log("Sending request to: " . $endpoint);

                $response = $this->http->post(
                    'chat/completions',
                    [
                        'json' => $data,
                        'headers' => [
                            'Authorization' => $this->key,
                        ],
                        'stream' => $stream,
                    ]
                );
                
                error_log("Received response with status: " . $response->getStatusCode());
            } catch (GuzzleException $e) {
                error_log("API request failed: " . $e->getMessage());
                throw new Exception("API request failed: " . $e->getMessage());
            }

            // 如果是数据流模式，则直接返回数据流
            if ($stream) {
                return $response->getBody();
            }

            $responseBody = $response->getBody()->getContents();
            $data = json_decode($responseBody, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                error_log("Response is not valid JSON: " . substr($responseBody, 0, 100));
                throw new Exception('Response is not json: ' . substr($responseBody, 0, 100));
            }

            if (!$this->checkFields($data)) {
                error_log("Missing fields in response: " . json_encode($data));
                throw new Exception('Field missing in API response');
            }

            $answer = $data['choices'][0]['message']['content'];
            $this->addMessage($answer, 'assistant');
            
            error_log("Successfully received response with answer length: " . strlen($answer));

            return [
                'answer' => $answer,
                'id' => $data['id'],
                'model' => $this->model,
                'usage' => $data['usage'],
            ];
        }
    }



    /**

     * 检查响应行是否包含必要的字段

     * @param  mixed  $line

     * @return bool

     */

    public function checkFields($line): bool

    {

        return isset($line['choices'][0]['message']['content']) && isset($line['id']) && isset($line['usage']);

    }



    /**

     * 检查流响应行是否包含必要的字段

     * @param  mixed  $line

     * @return bool

     */

    public function checkStreamFields($line): bool

    {

        return isset($line['choices'][0]['delta']['content']) && isset($line['id']);

    }



    /**

     * 格式化流消息为数组

     * @param  string  $line

     * @return mixed

     */

    public function formatStreamMessage(string $line)

    {

        preg_match('/data: (.*)/', $line, $matches);

        if (empty($matches[1])) {

            return false;

        }



        $line = $matches[1];

        $data = json_decode($line, true);



        if (json_last_error() !== JSON_ERROR_NONE) {

            return false;

        }



        return $data;

    }

}

