<?php

if (! defined('ABSPATH')) {
    exit;
} class PufferGo_API_Client
{
    private static $instance = null;

    private $base_url;

    private $default_timeout = 120;

    private $request_interceptors = array();

    private $response_interceptors = array();

    private static $trusted_domain_suffixes = array('puffergo.com', );

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

    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_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');
            $args['headers']['X-PHP-Version'] = PHP_VERSION;

            return $args;
        });
    }

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

foreach (self::$trusted_domain_suffixes as $suffix) {
            if ($host === $suffix || $this->str_ends_with($host, '.' . $suffix)) {
                return true;
            }
        }

return false;
    }

    private function str_ends_with($haystack, $needle)
    {
        $length = strlen($needle);

        if (0 === $length) {
            return true;
        }

return substr($haystack, -$length) === $needle;
    }

    public function add_request_interceptor($interceptor)
    {
        if (is_callable($interceptor)) {
            $this->request_interceptors[] = $interceptor;
        }
    }

    public function add_response_interceptor($interceptor)
    {
        if (is_callable($interceptor)) {
            $this->response_interceptors[] = $interceptor;
        }
    }

    public function post($endpoint, $data = array(), $options = array())
    {
        return $this->request('POST', $endpoint, $data, $options);
    }

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

    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 = $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', );

        if ($method === 'POST' && $data !== null) {
            if (is_array($data) && class_exists('PufferGo_Json_Object_Codec')) {
                if (isset($data['jobs']) && is_array($data['jobs'])) {
                    $data['jobs'] = PufferGo_Json_Object_Codec::normalize_workflow_jobs($data['jobs']);
                } $data = PufferGo_Json_Object_Codec::normalize_generate_block_payload($data);
            } $request_args['body'] = wp_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);
        } $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, ));

            throw new Exception(__('Unable to connect to AI service: ', 'puffergo') . 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);
        } $response = apply_filters('puffergo_api_response', $response, $endpoint, $method);
        $result = json_decode($response_body, true);

        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), ));

            throw new Exception("AI数据格式错误(Code:" . intval($response_code) . ", Len:" . intval($body_len) . "): " . $raw_snippet . " ... [错误原因: " . esc_html($json_error) . "]");
        } $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)) {
            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), ));

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

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 service returned an error.', 'puffergo');

            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 service returned an error.', 'puffergo'), ));

            throw new Exception($error_message, intval($response_code));
        }

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

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 服务返回未知错误';

            throw new Exception($final_message);
        }

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

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

    public function set_base_url($url)
    {
        $this->base_url = rtrim($url, '/');
    }

    public function get_base_url()
    {
        return $this->base_url;
    }

    public function set_default_timeout($timeout)
    {
        $this->default_timeout = intval($timeout);
    }
}
