<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

/**
 * PufferGo API Client - 统一的外部AI服务HTTP客户端
 * * 功能说明：
 * 1. 统一封装所有对外部AI服务的HTTP请求（POST/GET）
 * 2. 统一错误处理和日志记录
 * 3. 预留权限验证和用户体系接入钩子
 * 4. 支持请求拦截器（用于添加认证Token、签名等）
 * * @package PufferGo
 * @since 1.0.0
 */

class PufferGo_API_Client {
    
    /**
     * 单例实例
     * @var PufferGo_API_Client
     */
    private static $instance = null;
    
    /**
     * API基础URL
     * @var string
     */
    private $base_url;
    
    /**
     * 默认超时时间（秒）
     * @var int
     */
    private $default_timeout = 120;
    
    /**
     * 请求拦截器钩子列表
     * @var array
     */
    private $request_interceptors = array();
    
    /**
     * 响应拦截器钩子列表
     * @var array
     */
    private $response_interceptors = array();
    
    /**
     * 可信域名后缀白名单（只有这些域名才会携带敏感凭证）
     * @var array
     */
    private static $trusted_domain_suffixes = array(
        'puffergo.com',
    );
    
    /**
     * 可信本地域名（开发环境）
     * @var array
     */
    private static $trusted_local_hosts = array(
        'localhost',
        '127.0.0.1',
    );
    
    /**
     * 私有构造函数（单例模式）
     */
    private function __construct() {
        $this->base_url = defined('PUFFERGO_AI_PAGE_BUILDER_BASE_URL') 
            ? PUFFERGO_AI_PAGE_BUILDER_BASE_URL 
            : 'https://api.puffergo.com';
        
        // 注册默认的请求拦截器（用于后续添加认证）
        $this->register_default_interceptors();
    }
    
    /**
     * 获取单例实例
     * * @return PufferGo_API_Client
     */
    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * 注册默认拦截器
     */
    private function register_default_interceptors() {
        // 安全拦截器：只有可信域名才注入敏感凭证和元信息
        $this->add_request_interceptor(function($args, $endpoint) {
            $full_url = $this->base_url . $endpoint;
            
            // 安全检查：只有可信域名才携带敏感信息
            if ( ! $this->is_trusted_url( $full_url ) ) {
                return $args;
            }
            
            // 注入 API 密钥
            $api_key = get_option('puffergo_api_key', '');
            if ( ! empty( $api_key ) ) {
                $args['headers']['Authorization'] = 'Bearer ' . $api_key;
            }
            
            // 注入插件元信息（用于服务端统计和兼容性处理）
            $args['headers']['X-PufferGo-Version'] = defined( 'PUFFERGO_CORE_VERSION' ) ? PUFFERGO_CORE_VERSION : 'unknown';
            $args['headers']['X-PufferGo-Site']    = home_url();
            $args['headers']['X-WP-Version']       = get_bloginfo( 'version' );
            // PHP版本
            $args['headers']['X-PHP-Version'] = PHP_VERSION;
            
            return $args;
        });
    }
    
    /**
     * 检查URL是否为可信域名
     * 
     * 可信域名包括：
     * 1. 本地开发环境（localhost、127.0.0.1）
     * 2. puffergo.com 及其子域名
     * 
     * @param string $url 要检查的完整URL
     * @return bool
     */
    private function is_trusted_url( $url ) {
        $parsed = wp_parse_url( $url );
        
        if ( ! isset( $parsed['host'] ) ) {
            return false;
        }
        
        $host = strtolower( $parsed['host'] );
        
        // 检查本地开发环境
        if ( in_array( $host, self::$trusted_local_hosts, true ) ) {
            return true;
        }
        
        // 检查可信域名后缀（支持子域名，如 api.puffergo.com）
        foreach ( self::$trusted_domain_suffixes as $suffix ) {
            // 精确匹配或子域名匹配
            if ( $host === $suffix || $this->str_ends_with( $host, '.' . $suffix ) ) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 检查字符串是否以指定后缀结尾（兼容 PHP 7.x）
     * 
     * @param string $haystack 要检查的字符串
     * @param string $needle 后缀
     * @return bool
     */
    private function str_ends_with( $haystack, $needle ) {
        $length = strlen( $needle );
        if ( 0 === $length ) {
            return true;
        }
        return substr( $haystack, -$length ) === $needle;
    }
    
    /**
     * 添加请求拦截器
     * * @param callable $interceptor 拦截器函数，接收 ($request_args, $endpoint) 参数，返回修改后的 $request_args
     */
    public function add_request_interceptor($interceptor) {
        if (is_callable($interceptor)) {
            $this->request_interceptors[] = $interceptor;
        }
    }
    
    /**
     * 添加响应拦截器
     * * @param callable $interceptor 拦截器函数，接收 ($response, $endpoint) 参数，返回修改后的 $response
     */
    public function add_response_interceptor($interceptor) {
        if (is_callable($interceptor)) {
            $this->response_interceptors[] = $interceptor;
        }
    }
    
    /**
     * 发送POST请求
     * * @param string $endpoint API端点（相对路径，如 '/api/workflow/1-structure-data'）
     * @param array $data 请求数据
     * @param array $options 可选配置 ['timeout' => 120, 'log_prefix' => 'AI Service']
     * @return array 返回格式: ['success' => bool, 'data' => mixed, 'message' => string]
     * @throws Exception
     */
    public function post($endpoint, $data = array(), $options = array()) {
        return $this->request('POST', $endpoint, $data, $options);
    }
    
    /**
     * 发送GET请求
     * * @param string $endpoint API端点
     * @param array $query_params 查询参数
     * @param array $options 可选配置
     * @return array
     * @throws Exception
     */
    public function get($endpoint, $query_params = array(), $options = array()) {
        // 构建查询字符串
        if (!empty($query_params)) {
            $endpoint .= '?' . http_build_query($query_params);
        }
        return $this->request('GET', $endpoint, null, $options);
    }
    
    /**
     * 核心请求方法
     * * @param string $method HTTP方法（GET/POST）
     * @param string $endpoint API端点
     * @param array|null $data 请求数据（POST时使用）
     * @param array $options 可选配置
     * @return array
     * @throws Exception
     */
    private function request($method, $endpoint, $data = null, $options = array()) {
        // 解析配置选项
        $timeout = isset($options['timeout']) ? intval($options['timeout']) : $this->default_timeout;
        $log_prefix = isset($options['log_prefix']) ? $options['log_prefix'] : 'PufferGo API';
        
        // 构建完整URL
        $url = $this->base_url . $endpoint;
        
        // 记录请求日志
        if ( function_exists( 'puffergo_log' ) ) {
            puffergo_log(esc_html($log_prefix) . ": 发送 " . esc_html($method) . " 请求到: " . esc_url($url));
        }
        
        // 构建请求参数
        $request_args = array(
            'method'      => $method,
            'headers'     => array('Content-Type' => 'application/json; charset=utf-8'),
            'timeout'     => $timeout,
            'data_format' => 'body',
        );
        
        // 如果是POST请求，添加请求体
        if ($method === 'POST' && $data !== null) {
            $request_args['body'] = json_encode($data);
            if (isset($options['debug']) && $options['debug'] && function_exists('puffergo_log')) {
                puffergo_log(esc_html($log_prefix) . ": 请求数据: " . json_encode($data, JSON_UNESCAPED_UNICODE));
            }
        }
        
        // 执行请求拦截器
        foreach ($this->request_interceptors as $interceptor) {
            $request_args = call_user_func($interceptor, $request_args, $endpoint);
        }
        
        // 应用WordPress过滤器钩子（允许外部插件修改请求）
        $request_args = apply_filters('puffergo_api_request_args', $request_args, $endpoint, $method);
        
        // 发送请求
        $start_time = microtime(true);
        if ($method === 'GET') {
            $response = wp_remote_get($url, $request_args);
        } else {
            $response = wp_remote_post($url, $request_args);
        }
        $duration = microtime(true) - $start_time;
        
        // 检查连接错误
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            if ( function_exists( 'puffergo_log' ) ) {
                puffergo_log(esc_html($log_prefix) . ": 连接失败 - " . esc_html($error_message));
            }
            $this->report_remote_api_issue(
                $endpoint,
                'transport',
                array(
                    'message' => $error_message,
                )
            );
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
            throw new Exception("无法连接到 AI 服务: " . esc_html($error_message));
        }
        
        // 获取响应信息
        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        
        if ( function_exists( 'puffergo_log' ) ) {
            puffergo_log(esc_html($log_prefix) . ": 响应状态码: " . intval($response_code) . "，耗时: " . round($duration, 4) . " 秒");
        }
        
        // 执行响应拦截器
        foreach ($this->response_interceptors as $interceptor) {
            $response = call_user_func($interceptor, $response, $endpoint);
        }
        
        // 应用WordPress过滤器钩子
        $response = apply_filters('puffergo_api_response', $response, $endpoint, $method);
        
        // 解析响应
        $result = json_decode($response_body, true);
        
        // 处理JSON解析错误
        if (json_last_error() !== JSON_ERROR_NONE) {
            $json_error = json_last_error_msg();
            $raw_snippet = htmlspecialchars(substr($response_body, 0, 200));
            $body_len = strlen($response_body);

            if ( function_exists( 'puffergo_log' ) ) {
                puffergo_log(esc_html($log_prefix) . ": JSON 解析失败。错误: " . esc_html($json_error));
            }

            $this->report_remote_api_issue(
                $endpoint,
                'invalid_json',
                array(
                    'httpStatus'  => intval($response_code),
                    'jsonError'   => $json_error,
                    'bodySnippet' => substr($response_body, 0, 2048),
                    'bodyLength'  => intval($body_len),
                )
            );

            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
            throw new Exception("AI数据格式错误(Code:" . intval($response_code) . ", Len:" . intval($body_len) . "): " . $raw_snippet . " ... [错误原因: " . esc_html($json_error) . "]");
        }
        
        // 检查 HTTP 状态码（默认可为 200；异步 Job 创建可传 success_http_codes 含 202）
        $success_codes = isset( $options['success_http_codes'] ) && is_array( $options['success_http_codes'] )
            ? $options['success_http_codes']
            : array( 200 );
        if ( ! in_array( $response_code, $success_codes, true ) ) {
            // SaaS 计量错误处理 - 使用自定义异常类透传状态码
            if ($response_code === 401 || $response_code === 403) {
                if ( function_exists( 'puffergo_log' ) ) {
                    puffergo_log(esc_html($log_prefix) . ": API 密钥无效（状态码: " . intval($response_code) . "）");
                }
                $this->report_remote_api_issue(
                    $endpoint,
                    'http_auth',
                    array(
                        'httpStatus' => intval($response_code),
                    )
                );
                // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
                throw PufferGo_API_Exception::authInvalid( $response_code );
            } elseif ($response_code === 402) {
                if ( function_exists( 'puffergo_log' ) ) {
                    puffergo_log(esc_html($log_prefix) . ": AI 积分不足（状态码: 402）");
                }
                $this->report_remote_api_issue(
                    $endpoint,
                    'http_payment',
                    array(
                        'httpStatus' => 402,
                    )
                );
                throw PufferGo_API_Exception::creditsInsufficient();
            }

            // 4xx（除已处理的 401/402/403）可原样返回 JSON，供 Job 轮询 404 等
            if ( ! empty( $options['return_body_on_client_error'] ) && $response_code >= 400 && $response_code < 500 && is_array( $result ) ) {
                return array_merge( array( '_httpStatus' => $response_code ), $result );
            }

            $error_message = isset($result['message']) ? $result['message'] : 'AI 服务返回错误';
            if ( function_exists( 'puffergo_log' ) ) {
                puffergo_log(esc_html($log_prefix) . ": API错误 - " . esc_html($error_message));
            }
            $this->report_remote_api_issue(
                $endpoint,
                'http_error',
                array(
                    'httpStatus' => intval($response_code),
                    'message'    => is_string($error_message) ? $error_message : 'AI 服务返回错误',
                )
            );
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
            throw new Exception($error_message);
        }
        
        // 检查业务状态码
        if (isset($result['success']) && $result['success'] === false) {
            $error_message = isset($result['message']) ? $result['message'] : '';
            if ( function_exists( 'puffergo_log' ) ) {
                puffergo_log(esc_html($log_prefix) . ": 业务错误 - " . esc_html($error_message));
            }
            // 只上报服务端未知错误（message 为空或通用文案），用户输入类拒绝（如「请输入内容」）不上报以避免 Axiom 噪音
            if ( empty( $error_message ) || ! isset( $result['message'] ) ) {
                $this->report_remote_api_issue(
                    $endpoint,
                    'business_false',
                    array(
                        'httpStatus' => 200,
                        'message'    => 'AI 服务返回未知错误',
                    )
                );
            }
            $final_message = ! empty( $error_message ) ? $error_message : 'AI 服务返回未知错误';
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
            throw new Exception($final_message);
        }
        
        // 返回数据（统一格式）。轮询 Job 等需完整根对象时设 return_raw_result => true
        if ( ! empty( $options['return_raw_result'] ) ) {
            return $result;
        }
        if (isset($result['success']) && $result['success'] === true) {
            return $result['data'] ?? $result;
        } elseif (isset($result['data'])) {
            return $result['data'];
        } else {
            return $result;
        }
    }
    
    /**
     * 将远端 API 失败上报至遥测（Axiom），不抛错。
     *
     * @param string               $endpoint 相对路径，如 /api/ai/generate-block。
     * @param string               $phase    失败阶段标识。
     * @param array<string, mixed> $extra    附加字段。
     * @return void
     */
    private function report_remote_api_issue( $endpoint, $phase, array $extra = array() ) {
        if ( ! class_exists( 'PufferGo_Telemetry' ) ) {
            return;
        }
        $detail = array_merge(
            array(
                'source'   => 'php_api_client',
                'phase'    => $phase,
                'endpoint' => $endpoint,
            ),
            $extra
        );
        PufferGo_Telemetry::send_error_event( $detail );
    }

    /**
     * 设置基础URL（用于测试环境切换）
     * * @param string $url
     */
    public function set_base_url($url) {
        $this->base_url = rtrim($url, '/');
    }
    
    /**
     * 获取基础URL
     * * @return string
     */
    public function get_base_url() {
        return $this->base_url;
    }
    
    /**
     * 设置默认超时时间
     * * @param int $timeout 超时时间（秒）
     */
    public function set_default_timeout($timeout) {
        $this->default_timeout = intval($timeout);
    }
}