<?php
/**
 * CloudFlare-Friendly HTTP Client for Teleship
 *
 * Implements request fingerprinting, retries with backoff, and CloudFlare-friendly
 * patterns to reduce the likelihood of requests being blocked by CloudFlare protection.
 *
 * @package Teleship
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Teleship HTTP Client with CloudFlare optimizations
 */
class Teleship_HTTP_Client {
    /**
     * Plugin version for User-Agent
     */
    const PLUGIN_VERSION = '1.0.16';

    /**
     * Retry settings
     */
    const MAX_RETRIES = 3;
    const RETRY_DELAY_BASE = 2; // Base delay for exponential backoff

    /**
     * CloudFlare detection patterns
     */
    const CLOUDFLARE_INDICATORS = [
        'cloudflare',
        'cf-ray',
        'cf-cache-status',
        '403 Forbidden',
        'security check',
        'attention required'
    ];

    /**
     * Generate site identifier for request fingerprinting
     */
    public static function get_site_identifier() {
        static $site_id = null;

        if ($site_id === null) {
            $site_url = get_site_url();
            $site_id = substr(md5($site_url . get_option('teleship_installation_id', '')), 0, 8);
        }

        return $site_id;
    }

    /**
     * Get CloudFlare-friendly User-Agent string
     */
    public static function get_user_agent() {
        $wc_version = defined('WC_VERSION') ? WC_VERSION : '8.0';
        $wp_version = get_bloginfo('version');
        $site_url = wp_parse_url(get_site_url(), PHP_URL_HOST);

        return sprintf(
            'Teleship-WooCommerce-Plugin/%s (WordPress/%s; WooCommerce/%s; +https://teleship.com/bot) Site: %s',
            self::PLUGIN_VERSION,
            $wp_version,
            $wc_version,
            $site_url
        );
    }

    /**
     * Get standard headers for all requests
     */
    private static function get_standard_headers() {
        return [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
            'User-Agent' => self::get_user_agent(),
            'X-Plugin-Version' => self::PLUGIN_VERSION,
            'X-Site-Identifier' => self::get_site_identifier(),
            'X-Platform' => 'woocommerce',
            'X-WP-Version' => get_bloginfo('version'),
            'X-WC-Version' => defined('WC_VERSION') ? WC_VERSION : '8.0',
        ];
    }

    /**
     * Detect if response indicates CloudFlare blocking
     */
    private static function is_cloudflare_block($response) {
        if (is_wp_error($response)) {
            return false;
        }

        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        $response_headers = wp_remote_retrieve_headers($response);

        // Check for CloudFlare indicators
        $content_to_check = strtolower($response_body . json_encode($response_headers));

        foreach (self::CLOUDFLARE_INDICATORS as $indicator) {
            if (strpos($content_to_check, strtolower($indicator)) !== false) {
                return true;
            }
        }

        // Check for specific response codes that might indicate CloudFlare blocking
        return in_array($response_code, [403, 429, 503]);
    }

    /**
     * Get CloudFlare troubleshooting message
     */
    private static function get_cloudflare_message() {
        $user_agent = self::get_user_agent();
        $site_id = self::get_site_identifier();

        return sprintf(
            /* translators: %1$s is the User-Agent string to whitelist, %2$s is the Site ID */
            __('CloudFlare protection may be blocking Teleship requests. Please whitelist our plugin in your CloudFlare settings. User-Agent to whitelist: "%1$s". Site ID: %2$s. Visit https://teleship.com/support for detailed instructions.', 'teleship-global-shipping'),
            $user_agent,
            $site_id
        );
    }

    /**
     * Make a CloudFlare-friendly HTTP request with retries
     */
    public static function request($url, $args = []) {
        // Merge with default arguments
        $default_args = [
            'timeout' => 30,
            'redirection' => 5,
            'httpversion' => '1.1',
            'headers' => [],
            'sslverify' => true,
            'cookies' => [],
        ];

        $args = wp_parse_args($args, $default_args);

        // Add standard headers
        $args['headers'] = array_merge(self::get_standard_headers(), $args['headers']);

        // Non-blocking (fire-and-forget) requests: send and return immediately
        // No retry or response validation — there is no response to check
        if (isset($args['blocking']) && $args['blocking'] === false) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                do_action('teleship_log', sprintf('HTTP request sent (fire-and-forget) to: %s', $url));
            }
            return wp_remote_request($url, $args);
        }

        // Per-request retry override (default: class constant MAX_RETRIES)
        $max_retries = isset($args['max_retries']) ? (int) $args['max_retries'] : self::MAX_RETRIES;
        $max_retries = max(1, $max_retries); // Guard: must attempt at least once
        unset($args['max_retries']); // Don't pass to wp_remote_request

        $last_error = null;

        // Retry logic with exponential backoff
        for ($attempt = 1; $attempt <= $max_retries; $attempt++) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                do_action('teleship_log', sprintf('Making HTTP request (attempt %d/%d) to: %s', $attempt, $max_retries, $url));
            }

            $response = wp_remote_request($url, $args);

            // Check if request was successful
            if (!is_wp_error($response)) {
                $response_code = wp_remote_retrieve_response_code($response);

                // Success codes
                if ($response_code >= 200 && $response_code < 300) {
                    if (defined('WP_DEBUG') && WP_DEBUG) {
                        do_action('teleship_log', sprintf('HTTP request successful (attempt %d): %d %s', $attempt, $response_code, wp_remote_retrieve_response_message($response)));
                    }
                    return $response;
                }

                // 4xx client errors should not be retried (except 429 rate limiting)
                // These are permanent errors — the request itself is the problem
                if ($response_code >= 400 && $response_code < 500 && $response_code !== 429) {
                    if (defined('WP_DEBUG') && WP_DEBUG) {
                        do_action('teleship_log', sprintf('HTTP client error (attempt %d): %d %s — not retrying', $attempt, $response_code, wp_remote_retrieve_response_message($response)));
                    }
                    return $response;
                }

                // Check for CloudFlare blocking
                if (self::is_cloudflare_block($response)) {
                    $last_error = new WP_Error('cloudflare_block', self::get_cloudflare_message());

                    if (defined('WP_DEBUG') && WP_DEBUG) {
                        do_action('teleship_log', sprintf('CloudFlare block detected (attempt %d): %d', $attempt, $response_code));
                    }
                } else {
                    // 5xx server errors and other transient errors — retry
                    $last_error = new WP_Error('http_error', sprintf(
                        /* translators: %1$d is the HTTP status code, %2$s is the error message */
                        __('HTTP request failed with status %1$d: %2$s', 'teleship-global-shipping'),
                        $response_code,
                        wp_remote_retrieve_response_message($response)
                    ));
                }
            } else {
                $last_error = $response;
            }

            // Don't retry on the last attempt
            if ($attempt < $max_retries) {
                // Exponential backoff with jitter
                $delay = (self::RETRY_DELAY_BASE ** $attempt) + (wp_rand(100, 1000) / 1000);

                if (defined('WP_DEBUG') && WP_DEBUG) {
                    do_action('teleship_log', sprintf('Retrying request in %.2f seconds (attempt %d failed)', $delay, $attempt));
                }

                usleep((int) ($delay * 1000000));
            }
        }

        // All retries failed
        if (defined('WP_DEBUG') && WP_DEBUG) {
            $error_message = is_wp_error($last_error) ? $last_error->get_error_message() : 'Unknown error';
            do_action('teleship_log', sprintf('HTTP request failed after %d attempts: %s', $max_retries, $error_message));
        }

        return $last_error;
    }

    /**
     * Make a POST request
     */
    public static function post($url, $data = [], $args = []) {
        $args['method'] = 'POST';
        if (!empty($data)) {
            $args['body'] = is_array($data) ? json_encode($data) : $data;
        }

        return self::request($url, $args);
    }

    /**
     * Make a GET request
     */
    public static function get($url, $args = []) {
        $args['method'] = 'GET';
        return self::request($url, $args);
    }

    /**
     * Make a PUT request
     */
    public static function put($url, $data = [], $args = []) {
        $args['method'] = 'PUT';
        if (!empty($data)) {
            $args['body'] = is_array($data) ? json_encode($data) : $data;
        }

        return self::request($url, $args);
    }

    /**
     * Make a PATCH request
     */
    public static function patch($url, $data = [], $args = []) {
        $args['method'] = 'PATCH';
        if (!empty($data)) {
            $args['body'] = is_array($data) ? json_encode($data) : $data;
        }

        return self::request($url, $args);
    }

    /**
     * Make a DELETE request
     */
    public static function delete($url, $args = []) {
        $args['method'] = 'DELETE';
        return self::request($url, $args);
    }

    /**
     * Check if a response indicates CloudFlare issues
     */
    public static function has_cloudflare_issues($response) {
        return self::is_cloudflare_block($response);
    }

    /**
     * Get troubleshooting instructions for CloudFlare issues
     */
    public static function get_troubleshooting_message() {
        return self::get_cloudflare_message();
    }
}
