<?php
/**
 * Ticket Tailor API Client
 *
 * Handles all communication with the Ticket Tailor API
 */

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

class Ticket_Tailor_API_Client {
    
    /**
     * API Base URL
     */
    private $base_url = 'https://api.tickettailor.com/v1/';
    
    /**
     * API Key
     */
    private $api_key;
    
    /**
     * Rate limit tracking
     */
    private $rate_limit_remaining;
    private $rate_limit_reset;

    /**
     * Retry configuration - ENTERPRISE: Exponential backoff
     */
    private $max_retries = 3;
    private $retry_delay_ms = 1000; // 1 second base delay
    
    /**
     * Constructor - SECURITY ENHANCEMENT: Use encrypted API key
     */
    public function __construct() {
        $this->api_key = $this->get_api_key();
    }

    /**
     * Get decrypted API key - SECURITY ENHANCEMENT
     */
    private function get_api_key() {
        // Check for encrypted key first
        $encrypted = get_option('ticket_tailor_api_key_encrypted', '');

        if (!empty($encrypted)) {
            return $this->decrypt_api_key($encrypted);
        }

        // Fallback to plain text key (for migration)
        $plain_key = get_option('ticket_tailor_api_key', '');

        // If plain key exists, encrypt it and migrate
        if (!empty($plain_key)) {
            $this->migrate_api_key($plain_key);
            return $plain_key;
        }

        return '';
    }

    /**
     * Encrypt API key - SECURITY ENHANCEMENT
     */
    private function encrypt_api_key($key) {
        if (!function_exists('openssl_encrypt') || !function_exists('wp_salt')) {
            // Fallback if OpenSSL or wp_salt not available
            return base64_encode($key);
        }

        $method = 'AES-256-CBC';
        $salt = wp_salt('auth');
        $encryption_key = substr(hash('sha256', $salt), 0, 32);
        $iv = substr(hash('sha256', wp_salt('nonce')), 0, 16);

        $encrypted = openssl_encrypt($key, $method, $encryption_key, 0, $iv);

        return base64_encode($encrypted);
    }

    /**
     * Decrypt API key - SECURITY ENHANCEMENT
     */
    private function decrypt_api_key($encrypted) {
        if (!function_exists('openssl_decrypt') || !function_exists('wp_salt')) {
            // Fallback if OpenSSL or wp_salt not available
            return base64_decode($encrypted);
        }

        $method = 'AES-256-CBC';
        $salt = wp_salt('auth');
        $encryption_key = substr(hash('sha256', $salt), 0, 32);
        $iv = substr(hash('sha256', wp_salt('nonce')), 0, 16);

        $decrypted = openssl_decrypt(base64_decode($encrypted), $method, $encryption_key, 0, $iv);

        return $decrypted !== false ? $decrypted : '';
    }

    /**
     * Migrate plain text API key to encrypted - SECURITY ENHANCEMENT
     */
    private function migrate_api_key($plain_key) {
        $encrypted = $this->encrypt_api_key($plain_key);
        update_option('ticket_tailor_api_key_encrypted', $encrypted);

        // Remove plain text key
        delete_option('ticket_tailor_api_key');
    }
    
    /**
     * Check if API is configured
     */
    public function is_configured() {
        return !empty($this->api_key);
    }
    
    /**
     * Make API request with retry logic - ENTERPRISE: Exponential backoff
     */
    private function request($endpoint, $method = 'GET', $data = array()) {
        if (!$this->is_configured()) {
            return new WP_Error('no_api_key', __('API key not configured', 'ticket-tailor'));
        }

        $attempt = 0;
        $last_error = null;

        while ($attempt < $this->max_retries) {
            $attempt++;

            $result = $this->make_request_attempt($endpoint, $method, $data, $attempt);

            // Success - return immediately
            if (!is_wp_error($result)) {
                return $result;
            }

            $last_error = $result;
            $error_data = $result->get_error_data();

            // Don't retry on client errors (4xx except 429)
            if (isset($error_data['status'])) {
                $status = $error_data['status'];

                // Retry only on: 429 (rate limit), 5xx (server errors), network errors
                if ($status >= 400 && $status < 500 && $status !== 429) {
                    // Client error - don't retry
                    error_log(sprintf(
                        'Ticket Tailor API: Client error %d on %s, not retrying',
                        $status,
                        $endpoint
                    ));
                    return $result;
                }
            }

            // If we have more retries, wait with exponential backoff
            if ($attempt < $this->max_retries) {
                $delay = $this->calculate_backoff_delay($attempt, $error_data);

                error_log(sprintf(
                    'Ticket Tailor API: Attempt %d/%d failed for %s, retrying in %dms',
                    $attempt,
                    $this->max_retries,
                    $endpoint,
                    $delay
                ));

                usleep($delay * 1000); // Convert ms to microseconds
            }
        }

        // All retries exhausted
        error_log(sprintf(
            'Ticket Tailor API: All %d attempts failed for %s: %s',
            $this->max_retries,
            $endpoint,
            $last_error->get_error_message()
        ));

        return $last_error;
    }

    /**
     * Make single API request attempt - ENTERPRISE: Separated for retry logic
     */
    private function make_request_attempt($endpoint, $method, $data, $attempt_number) {
        $url = $this->base_url . ltrim($endpoint, '/');

        $args = array(
            'method' => $method,
            'headers' => array(
                'Accept' => 'application/json',
                'Authorization' => 'Basic ' . base64_encode($this->api_key . ':'),
                'X-TT-Request-ID' => wp_generate_uuid4(), // ENTERPRISE: Request tracking
            ),
            'timeout' => 30,
        );

        if (!empty($data)) {
            if ($method === 'GET') {
                $url = add_query_arg($data, $url);
            } else {
                $args['body'] = wp_json_encode($data);
                $args['headers']['Content-Type'] = 'application/json';
            }
        }

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

        // Handle network errors
        if (is_wp_error($response)) {
            return new WP_Error(
                'network_error',
                sprintf('Network error: %s', $response->get_error_message()),
                array(
                    'attempt' => $attempt_number,
                    'endpoint' => $endpoint,
                    'original_error' => $response->get_error_code()
                )
            );
        }

        // Track rate limits
        $headers = wp_remote_retrieve_headers($response);
        if (isset($headers['x-rate-limit-remaining'])) {
            $this->rate_limit_remaining = (int) $headers['x-rate-limit-remaining'];
        }
        if (isset($headers['x-rate-limit-reset'])) {
            $this->rate_limit_reset = (int) $headers['x-rate-limit-reset'];
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        // Handle HTTP errors
        if ($code < 200 || $code >= 300) {
            $error_data = json_decode($body, true);
            $message = isset($error_data['message']) ? $error_data['message'] : 'API request failed';

            return new WP_Error(
                'api_error',
                sprintf('%s (HTTP %d)', $message, $code),
                array(
                    'status' => $code,
                    'response' => $error_data,
                    'attempt' => $attempt_number,
                    'endpoint' => $endpoint,
                    'rate_limit_remaining' => $this->rate_limit_remaining
                )
            );
        }

        $decoded = json_decode($body, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            return new WP_Error(
                'json_decode_error',
                sprintf('Failed to decode API response: %s', json_last_error_msg()),
                array(
                    'attempt' => $attempt_number,
                    'endpoint' => $endpoint,
                    'body_preview' => substr($body, 0, 200)
                )
            );
        }

        return $decoded;
    }

    /**
     * Calculate backoff delay - ENTERPRISE: Exponential backoff with jitter
     */
    private function calculate_backoff_delay($attempt, $error_data = array()) {
        // For rate limits, use the reset time if available
        if (isset($error_data['status']) && $error_data['status'] === 429) {
            if ($this->rate_limit_reset) {
                $wait_time = max(0, $this->rate_limit_reset - time());
                return min($wait_time * 1000, 60000); // Max 60 seconds
            }
        }

        // Exponential backoff: delay = base * (2 ^ attempt)
        $delay = $this->retry_delay_ms * pow(2, $attempt - 1);

        // Add jitter (random ±25%)
        $jitter = $delay * (rand(75, 125) / 100);

        // Cap at 30 seconds
        return min($jitter, 30000);
    }
    
    /**
     * Get all events
     */
    public function get_events($args = array()) {
        $defaults = array(
            'limit' => 100,
        );
        
        $args = wp_parse_args($args, $defaults);
        
        return $this->request('events', 'GET', $args);
    }
    
    /**
     * Get single event - SECURITY FIX: Added length validation
     */
    public function get_event($event_id) {
        $event_id = sanitize_text_field($event_id);

        // SECURITY FIX: Validate ID length (reasonable limit)
        if (strlen($event_id) > 100) {
            return new WP_Error('invalid_id', __('Invalid event ID', 'ticket-tailor'));
        }

        return $this->request("events/{$event_id}");
    }

    /**
     * Get event ticket types - SECURITY FIX: Added length validation
     */
    public function get_ticket_types($event_id) {
        $event_id = sanitize_text_field($event_id);

        // SECURITY FIX: Validate ID length
        if (strlen($event_id) > 100) {
            return new WP_Error('invalid_id', __('Invalid event ID', 'ticket-tailor'));
        }

        return $this->request("events/{$event_id}/ticket_types");
    }
    
    /**
     * Get orders
     */
    public function get_orders($args = array()) {
        $defaults = array(
            'limit' => 100,
        );
        
        $args = wp_parse_args($args, $defaults);
        
        return $this->request('orders', 'GET', $args);
    }
    
    /**
     * Get single order - SECURITY FIX: Added length validation
     */
    public function get_order($order_id) {
        $order_id = sanitize_text_field($order_id);

        // SECURITY FIX: Validate ID length
        if (strlen($order_id) > 100) {
            return new WP_Error('invalid_id', __('Invalid order ID', 'ticket-tailor'));
        }

        return $this->request("orders/{$order_id}");
    }

    /**
     * Get issued tickets for an order - SECURITY FIX: Added length validation
     */
    public function get_order_tickets($order_id) {
        $order_id = sanitize_text_field($order_id);

        // SECURITY FIX: Validate ID length
        if (strlen($order_id) > 100) {
            return new WP_Error('invalid_id', __('Invalid order ID', 'ticket-tailor'));
        }

        return $this->request("orders/{$order_id}/issued_tickets");
    }
    
    /**
     * Get box office overview
     */
    public function get_overview() {
        return $this->request('overview');
    }
    
    /**
     * Create a hold - SECURITY FIX: Added validation
     */
    public function create_hold($ticket_type_id, $quantity) {
        $ticket_type_id = sanitize_text_field($ticket_type_id);
        $quantity = absint($quantity);

        // SECURITY FIX: Validate inputs
        if (strlen($ticket_type_id) > 100) {
            return new WP_Error('invalid_id', __('Invalid ticket type ID', 'ticket-tailor'));
        }

        if ($quantity < 1 || $quantity > 100) {
            return new WP_Error('invalid_quantity', __('Invalid quantity (must be 1-100)', 'ticket-tailor'));
        }

        $data = array(
            'ticket_type_id' => $ticket_type_id,
            'quantity' => $quantity,
        );

        return $this->request('holds', 'POST', $data);
    }
    
    /**
     * Get voucher codes
     */
    public function get_voucher_codes($args = array()) {
        return $this->request('voucher_codes', 'GET', $args);
    }
    
    /**
     * Get discount codes
     */
    public function get_discount_codes($args = array()) {
        return $this->request('discount_codes', 'GET', $args);
    }
    
    /**
     * Paginate through all results - PERFORMANCE FIX: Removed array_merge memory leak
     */
    public function get_all_paginated($endpoint, $args = array()) {
        $all_results = array();
        $args['limit'] = 100;
        $iteration_count = 0;
        $max_iterations = 100; // 100 * 100 = 10,000 items max
        $start_time = time();
        $max_execution_time = 60; // 60 seconds max

        do {
            $response = $this->request($endpoint, 'GET', $args);

            if (is_wp_error($response)) {
                return $response;
            }

            // PERFORMANCE FIX: Use array push instead of array_merge
            if (isset($response['data']) && is_array($response['data'])) {
                foreach ($response['data'] as $item) {
                    $all_results[] = $item;
                }
            }

            // Check for next page
            $has_more = false;
            if (isset($response['pagination']['has_more']) && $response['pagination']['has_more']) {
                $last_item = end($response['data']);
                if (isset($last_item['id'])) {
                    $args['starting_after'] = $last_item['id'];
                    $has_more = true;
                } else {
                    break;
                }
            }

            // Safety checks to prevent infinite loops
            $iteration_count++;
            if ($iteration_count >= $max_iterations) {
                error_log('Ticket Tailor API: Reached maximum iterations (' . $max_iterations . ')');
                break;
            }

            // Timeout protection
            if ((time() - $start_time) >= $max_execution_time) {
                error_log('Ticket Tailor API: Reached maximum execution time (' . $max_execution_time . 's)');
                break;
            }

            // Item count protection
            if (count($all_results) >= 10000) {
                error_log('Ticket Tailor API: Reached maximum items (10000)');
                break;
            }

        } while ($has_more);

        return $all_results;
    }
    
    /**
     * Test API connection
     */
    public function test_connection() {
        $response = $this->request('events', 'GET', array('limit' => 1));
        
        if (is_wp_error($response)) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Get rate limit info
     */
    public function get_rate_limit_info() {
        return array(
            'remaining' => $this->rate_limit_remaining,
            'reset' => $this->rate_limit_reset,
        );
    }
    
    /**
     * Set API key - SECURITY ENHANCEMENT: Store encrypted
     */
    public function set_api_key($api_key) {
        $api_key = sanitize_text_field($api_key);

        // Get old key hash for logging
        $old_key_hash = hash('sha256', $this->api_key);

        $this->api_key = $api_key;

        // Store encrypted
        $encrypted = $this->encrypt_api_key($api_key);
        update_option('ticket_tailor_api_key_encrypted', $encrypted);

        // Remove plain text key if it exists
        delete_option('ticket_tailor_api_key');

        // Log the change
        $security_logger = new Ticket_Tailor_Security_Logger();
        $security_logger->log_api_key_change($old_key_hash, hash('sha256', $api_key));

        // Test the connection
        return $this->test_connection();
    }

    /**
     * Clear API key - SECURITY ENHANCEMENT: Clear encrypted key
     */
    public function clear_api_key() {
        $this->api_key = '';
        delete_option('ticket_tailor_api_key_encrypted');
        delete_option('ticket_tailor_api_key');
    }
}
