<?php
/**
 * Pollinations.ai Provider
 * AI models via Pollinations.ai API
 */

if ( ! defined( 'ABSPATH' ) ) exit; // Thoát nếu truy cập trực tiếp

class AIAutotool_AI_Provider_Pollinations extends AIAutotool_AI_Provider_Base {
    
    protected $name = 'Pollinations.ai';
    protected $id = 'pollinations';
    protected $base_url = 'https://enter.pollinations.ai/api/generate/v1';
    protected $default_model = 'gemini';  # Google Gemini 2.5 Flash Lite - Fast & Multimodal
    protected $capabilities = array('text_generation', 'image_generation');
    
    protected $available_models = array(
        // Models từ Pollinations.ai API (cập nhật từ https://enter.pollinations.ai/api/generate/text/models)
        // Fast models
       
        'openai' => 'OpenAI GPT-5 Nano - Ultra Fast & Affordable',
        'gemini' => 'Google Gemini 2.5 Flash Lite - Fast & Multimodal',
        'mistral' => 'Mistral Small 3.2 24B - Efficient & Cost-Effective',
        'perplexity-fast' => 'Perplexity Sonar - Fast & Affordable',
        'claude-fast' => 'Anthropic Claude Haiku 4.5 - Fast & Intelligent',
        
        // Standard models
        'deepseek' => 'DeepSeek V3.1 - Advanced Reasoning & Coding',
        'claude' => 'Anthropic Claude Sonnet 4.5 - Most Capable & Balanced',
        
        'gemini-search' => 'Google Gemini 2.5 Flash Lite - With Google Search',
        
        // Advanced/Reasoning models
       
    );
    
    /**
     * Model mapping for invalid/old model names to valid ones
     */
    protected $model_mapping = array(
        // Map old OpenAI model names to Pollinations.ai models
        'gpt-4' => 'openai',
        'gpt-4o' => 'openai',
        'gpt-4-turbo' => 'openai',
        'gpt-3.5-turbo' => 'openai',
        'gpt-4o-mini' => 'openai',
        'gpt-4.1-nano-2025-04-14' => 'openai',
        'gpt-4.1-nano' => 'openai',
    );
    
    /**
     * Models that don't support max_tokens parameter
     */
    protected $models_without_max_tokens = array(
        // Some models may not support max_tokens, add them here if needed
    );
    
    /**
     * Models that don't support temperature parameter (only support default value 1)
     */
    protected $models_without_temperature = array(
        'openai',
        'openai',
        'openai',
        'openai-reasoning',
        'gpt-5-nano',
        'gpt-5-nano-2025-08-07',
        'gpt-4.1',
        'gpt-4.1-2025-04-14',
        'o4-mini',
    );

    public function get_image_models() {
        return array(
            'flux' => 'Flux (Default)',
            'flux-realism' => 'Flux Realism',
            'flux-anime' => 'Flux Anime',
            'flux-3d' => 'Flux 3D',
            'any-dark' => 'Any Dark',
            'turbo' => 'Turbo',
        );
    }

    private function is_usable_image_generation_key($api_key) {
        $api_key = trim((string) $api_key);
        return $api_key !== '' && strtolower($api_key) !== 'unused' && strlen($api_key) >= 20;
    }

    private function get_image_generation_key() {
        if ($this->is_usable_image_generation_key($this->api_key)) {
            return $this->api_key;
        }

        if (class_exists('AIAutotool_AI_Provider_API_Keys')) {
            $keys_manager = AIAutotool_AI_Provider_API_Keys::get_instance();
            $candidate_sets = array(
                $keys_manager->get_active_keys('pollinations'),
                $keys_manager->get_all_keys('pollinations'),
            );

            foreach ($candidate_sets as $keys) {
                foreach ($keys as $key_data) {
                    $candidate_key = isset($key_data['key']) ? $key_data['key'] : '';
                    if ($this->is_usable_image_generation_key($candidate_key) && !$this->is_key_403_blocked($candidate_key)) {
                        $this->api_key = $candidate_key;
                        return $candidate_key;
                    }
                }
            }
        }

        $old_key = get_option('aiautotool_pollinations_api_key', '');
        if ($this->is_usable_image_generation_key($old_key) && !$this->is_key_403_blocked($old_key)) {
            $this->api_key = $old_key;
            return $old_key;
        }

        foreach ($this->get_default_keys() as $default_key) {
            if ($this->is_usable_image_generation_key($default_key) && !$this->is_key_403_blocked($default_key)) {
                $this->api_key = $default_key;
                return $default_key;
            }
        }

        return '';
    }

    public function generate_image($prompt, $options = array()) {
        $prompt = trim((string) $prompt);
        if ($prompt === '') {
            return array('success' => false, 'error' => 'Image prompt is required');
        }

        $models = $this->get_image_models();
        $model = isset($options['model']) ? sanitize_text_field($options['model']) : 'flux';
        if (!isset($models[$model])) {
            $model = 'flux';
        }

        $width = isset($options['width']) ? max(256, min(2048, (int) $options['width'])) : 1024;
        $height = isset($options['height']) ? max(256, min(2048, (int) $options['height'])) : 1024;
        $size = $width . 'x' . $height;
        $safe = isset($options['safe']) ? (bool) $options['safe'] : false;

        $api_key_to_use = $this->get_image_generation_key();
        if (empty($api_key_to_use)) {
            return array(
                'success' => false,
                'error' => 'Pollinations image API requires an API key. Add a Pollinations API key in AI Provider settings.'
            );
        }

        $headers = array(
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . $api_key_to_use,
        );

        $body = array(
            'prompt' => $prompt,
            'model' => $model,
            'n' => 1,
            'size' => $size,
            'quality' => !empty($options['quality']) ? sanitize_text_field($options['quality']) : 'medium',
            'response_format' => 'b64_json',
            'user' => '',
            'image' => '',
            'safe' => $safe,
        );

        $response = $this->make_request(
            'https://gen.pollinations.ai/v1/images/generations?key=' . rawurlencode($api_key_to_use),
            $body,
            'POST',
            $headers,
            array('timeout' => isset($options['timeout']) ? (int) $options['timeout'] : 120, 'connect_timeout' => 15)
        );

        if (!$response['success']) {
            return $response;
        }

        $data = $response['data'];
        $image_data = isset($data['data'][0]) && is_array($data['data'][0]) ? $data['data'][0] : array();
        if (!empty($image_data['b64_json'])) {
            return array(
                'success' => true,
                'data_url' => 'data:image/png;base64,' . $image_data['b64_json'],
                'model' => $model,
                'provider' => $this->id,
                'width' => $width,
                'height' => $height,
                'size' => $size,
            );
        }

        if (!empty($image_data['url'])) {
            return array(
                'success' => true,
                'url' => esc_url_raw($image_data['url']),
                'model' => $model,
                'provider' => $this->id,
                'width' => $width,
                'height' => $height,
                'size' => $size,
            );
        }

        return array(
            'success' => false,
            'error' => 'Pollinations image response missing b64_json image data'
        );
    }
    
    /**
     * Mask API key for logging (show first 4 and last 4 characters)
     * Override base class method (already exists in base, but keeping for compatibility)
     * @param string $api_key
     * @return string
     */
    protected function mask_api_key($api_key) {
        if (empty($api_key) || strlen($api_key) <= 8) {
            return '***';
        }
        return substr($api_key, 0, 4) . '...' . substr($api_key, -4);
    }
    
    /**
     * Get option name for storing 403 blocked keys
     * @return string
     */
    private function get_403_blocked_keys_option() {
        return 'aiautotool_pollinations_403_blocked_keys';
    }
    
    /**
     * Mark API key as blocked due to 403 error
     * @param string $api_key
     */
    private function mark_key_403_blocked($api_key) {
        if (empty($api_key)) {
            return;
        }
        
        $option_name = $this->get_403_blocked_keys_option();
        $blocked_keys = get_option($option_name, array());
        
        // Store key with current timestamp (will be unblocked after 15 minutes)
        $blocked_keys[$api_key] = time();
        
        update_option($option_name, $blocked_keys, false);
    }
    
    /**
     * Check if API key is currently blocked due to 403 error
     * @param string $api_key
     * @return bool
     */
    private function is_key_403_blocked($api_key) {
        if (empty($api_key)) {
            return false;
        }
        
        $option_name = $this->get_403_blocked_keys_option();
        $blocked_keys = get_option($option_name, array());
        
        if (!isset($blocked_keys[$api_key])) {
            return false;
        }
        
        $blocked_time = $blocked_keys[$api_key];
        $current_time = time();
        $minutes_passed = ($current_time - $blocked_time) / 60;
        
        // If less than 15 minutes have passed, key is still blocked
        if ($minutes_passed < 15) {
            $remaining_minutes = 15 - $minutes_passed;
            return true;
        }
        
        // More than 15 minutes passed, unblock the key
        unset($blocked_keys[$api_key]);
        update_option($option_name, $blocked_keys, false);
        return false;
    }
    
    /**
     * Get default API keys array
     * @return array
     */
    private function get_default_keys() {
        return array(
            'plln_sk_eTyGU6GvbvldldtmX1TL1Vka5s1Mtvwt',
            'plln_sk_huG7IqWkOFAH2iBwhLK7KvcoB7mgZHOz',
            'plln_sk_9pFDvuJQm8fG2EHL6WuAYeFm7ykzDnMy',
            'plln_sk_lTRSODoh0DVSncr2CCbDMPv5uR8Syqw4',
            'plln_sk_aeMIzfyRXsZgbwkEezoGOs1KSTq4RkEZ',
            'plln_sk_iS4uFBePSaS57S7l1Ldode1fiOsH2rkR'
        );
    }
    
    /**
     * Get and rotate default API key
     * @return string
     */
    private function get_default_key() {
        $default_keys = $this->get_default_keys();
        if (empty($default_keys)) {
            return '';
        }
        
        // Get rotation index for default keys
        $rotation_index_option = 'aiautotool_pollinations_default_keys_rotation_index';
        $start_index = get_option($rotation_index_option, 0);
        
        // Ensure index is valid
        if ($start_index >= count($default_keys)) {
            $start_index = 0;
        }
        
        // Get current key and check if blocked
        $selected_key = null;
        $selected_index = null;
        $current_index = $start_index;
        $max_attempts = count($default_keys);
        $attempt = 0;
        
        while ($attempt < $max_attempts) {
            $key_to_check = $default_keys[$current_index];
            $is_blocked = $this->is_key_403_blocked($key_to_check);
            
            if (!$is_blocked) {
                $selected_key = $key_to_check;
                $selected_index = $current_index;
                break;
            }
            
            // Move to next key
            $current_index = ($current_index + 1) % count($default_keys);
            $attempt++;
        }
        
        if ($selected_key) {
            // Update rotation index for next call
            $next_index = ($selected_index + 1) % count($default_keys);
            update_option($rotation_index_option, $next_index, false);
            
            return $selected_key;
        }
        
        // All default keys are blocked - return first one anyway (will use free endpoint if also fails)
        // Still update rotation index so next time we try a different key first
        $next_index = ($start_index + 1) % count($default_keys);
        update_option($rotation_index_option, $next_index, false);
        
        return $default_keys[0];
    }
    
    /**
     * Get and rotate API key from manager, skipping 403-blocked keys
     * Override base class method to add 403 blocking logic
     * @return string
     */
    protected function get_rotated_key() {
        $keys_manager = AIAutotool_AI_Provider_API_Keys::get_instance();
        
        // Get ALL keys (including disabled) for debugging
        $all_keys_from_db = $keys_manager->get_all_keys('pollinations');
        $active_keys = $keys_manager->get_active_keys('pollinations');
        
        // Log all keys (including disabled) for debugging
        foreach ($all_keys_from_db as $idx => $key_data) {
            $enabled = isset($key_data['enabled']) && $key_data['enabled'] ? 'enabled' : 'DISABLED';
            $key_value = isset($key_data['key']) ? $this->mask_api_key($key_data['key']) : 'no key';
        }
        
        // Also check old single key format
        $old_key = get_option('aiautotool_pollinations_api_key', '');
        $has_old_key = !empty($old_key);
        
        // Collect all available keys (new format + old format)
        $all_keys = array();
        foreach ($active_keys as $key_data) {
            if (isset($key_data['key'])) {
                $all_keys[] = $key_data['key'];
            }
        }
        if ($has_old_key) {
            // Check if old key is different from any active key
            $old_key_in_active = false;
            foreach ($active_keys as $key_data) {
                if (isset($key_data['key']) && $key_data['key'] === $old_key) {
                    $old_key_in_active = true;
                    break;
                }
            }
            if (!$old_key_in_active) {
                $all_keys[] = $old_key;
            }
        }
        
        // If no active keys but we have keys in DB, use disabled keys as fallback
        if (empty($all_keys) && !empty($all_keys_from_db)) {
            // Use disabled keys if no active keys available
            foreach ($all_keys_from_db as $key_data) {
                if (isset($key_data['key'])) {
                    $all_keys[] = $key_data['key'];
                }
            }
        }
        
        // Log all keys found
        foreach ($all_keys as $idx => $key) {
            $blocked_status = $this->is_key_403_blocked($key) ? "BLOCKED" : "unblocked";
        }
        
        // Use manager's rotation to get next key (rotates between ALL keys)
        // Then check if that key is blocked and find next unblocked one if needed
        if (!empty($all_keys)) {
            // Use the same rotation index as manager to stay in sync
            $rotation_index_option = 'aiautotool_pollinations_rotation_index';
            $current_index = get_option($rotation_index_option, 0);
            
            // Ensure index is valid
            if ($current_index >= count($all_keys)) {
                $current_index = 0;
            }
            
            // Try to find an unblocked key starting from current rotation index
            $max_attempts = count($all_keys);
            $attempt = 0;
            $selected_key = null;
            $selected_index = $current_index;
            $skipped_count = 0;
            
            while ($attempt < $max_attempts) {
                $key_to_check = $all_keys[$current_index];
                $is_blocked = $this->is_key_403_blocked($key_to_check);
                
                if (!$is_blocked) {
                    $selected_key = $key_to_check;
                    $selected_index = $current_index;
                    break;
                } else {
                    $skipped_count++;
                }
                
                // Move to next key
                $current_index = ($current_index + 1) % count($all_keys);
                $attempt++;
            }
            
            // Update rotation index for next call (always advance from selected index)
            // This ensures we rotate through all keys, not just unblocked ones
            $next_index = ($selected_index + 1) % count($all_keys);
            update_option($rotation_index_option, $next_index, false);
            
            if ($selected_key) {
                return $selected_key;
            }
        }
        
        // No unblocked keys from user - try default keys with rotation
        $default_key = $this->get_default_key();
        if (!empty($default_key)) {
            return $default_key;
        }
        
        // All default keys are also blocked - return empty to trigger free endpoint fallback
        return '';
    }
    
    public function __construct($api_key = '') {
        parent::__construct($api_key);
        
        // Only get key from manager if not provided in constructor
        if (empty($this->api_key)) {
            $this->api_key = $this->get_rotated_key();
        }
        
        // Only use default API key if user hasn't set any key
        // Check if user has actually set keys in the database
        $user_has_keys = false;
        if (empty($this->api_key)) {
            // Check if user has set any keys in database
            $user_has_keys = $this->user_has_configured_keys();
            
            // Only use default key if user hasn't configured any keys
            if (!$user_has_keys) {
                $default_key = $this->get_default_key();
                // Check if default key is blocked
                if ($this->is_key_403_blocked($default_key)) {
                    $this->api_key = '';
                } else {
                    $this->api_key = $default_key;
                }
            } else {
                // User has keys configured - try to get and rotate key from manager
                $rotated_key = $this->get_rotated_key();
                
                // If we got a valid key, use it
                if (!empty($rotated_key)) {
                    $this->api_key = $rotated_key;
                } else {
                    // Manager returned empty - this means all keys are blocked
                    // Keep as empty to use free endpoint in requests
                    $this->api_key = '';
                }
            }
        } else {
            $user_has_keys = $this->user_has_configured_keys();
        }
    }
    
    /**
     * Check if user has configured any API keys in the database
     * @return bool
     */
    private function user_has_configured_keys() {
        // Check new multiple keys format
        $keys_manager = AIAutotool_AI_Provider_API_Keys::get_instance();
        $all_keys = $keys_manager->get_all_keys('pollinations');
        if (!empty($all_keys)) {
            return true;
        }
        
        // Check old single key format
        $old_key = get_option('aiautotool_pollinations_api_key', '');
        if (!empty($old_key)) {
            return true;
        }
        
        // Check llm7 keys (backward compatibility)
        $llm7_keys = $keys_manager->get_all_keys('llm7');
        if (!empty($llm7_keys)) {
            return true;
        }
        
        $old_llm7_key = get_option('aiautotool_llm7_api_key', '');
        if (!empty($old_llm7_key)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Map model name to valid Pollinations.ai model
     */
    protected function map_model($model) {
        // Check if model needs mapping
        if (isset($this->model_mapping[$model])) {
            $mapped_model = $this->model_mapping[$model];
            return $mapped_model;
        }
        
        // Check if model is in available models
        if (isset($this->available_models[$model])) {
            return $model;
        }
        
        // If model not found, use default
        return $this->default_model;
    }
    
    /**
     * Chat completion - Pollinations.ai API
     */
    public function chat_completion($messages, $options = array()) {
        if (!$this->is_configured()) {
            return array(
                'success' => false,
                'error' => 'Pollinations.ai API key not configured'
            );
        }
        
        $options = $this->parse_options($options);
        
        // Map model to valid Pollinations.ai model
        $original_model = $options['model'];
        $options['model'] = $this->map_model($options['model']);
        
        $url = $this->base_url . '/chat/completions';
        
        $body = array(
            'model' => $options['model'],
            'reasoning_effort' => isset($options['reasoning_effort']) ? $options['reasoning_effort'] : 'high',
            'messages' => $messages
        );
        
        // Only add temperature if model supports it
        // Some models (like openai) only support default temperature (1) and will error if other values are provided
        if (isset($options['temperature']) && !in_array($options['model'], $this->models_without_temperature)) {
            $body['temperature'] = floatval($options['temperature']);
        }
        
        // Only add max_tokens if model supports it
        // Some models may not support max_tokens, so we'll try to add it but handle errors gracefully
        if (isset($options['max_tokens']) && !in_array($options['model'], $this->models_without_max_tokens)) {
            $body['max_tokens'] = intval($options['max_tokens']);
        }
        
        if (isset($options['top_p'])) {
            $body['top_p'] = floatval($options['top_p']);
        }
        
        // Get API key to use - try to get and rotate if needed
        $api_key_to_use = trim($this->api_key); // Trim whitespace
        $key_source = 'stored';
        
        // Check if stored key is blocked
        if (!empty($api_key_to_use) && $this->is_key_403_blocked($api_key_to_use)) {
            $api_key_to_use = ''; // Force refresh
        }
        
        if (empty($api_key_to_use)) {
            // Get rotated key (will skip blocked keys)
            $rotated_key = $this->get_rotated_key();
            
            if (!empty($rotated_key)) {
                $api_key_to_use = trim($rotated_key); // Trim whitespace
                $key_source = 'rotated';
                $this->api_key = $api_key_to_use; // Update instance key
            } else {
                // All keys are blocked - use empty to trigger free endpoint fallback
                $api_key_to_use = '';
                $key_source = 'all_blocked_fallback';
            }
        }
        
        // Validate API key format (should start with plln_sk_)
        if (!empty($api_key_to_use) && !preg_match('/^plln_sk_/i', $api_key_to_use)) {
            // API key format may be invalid
        }
        
        // If key is empty, skip main API and go directly to free endpoint
        if (empty($api_key_to_use)) {
            $response = array('success' => false, 'error' => 'All API keys are 403-blocked, using free endpoint');
        } else {
            // Debug: Log API key being used
            
            // Trim API key to remove any whitespace
            $api_key_to_use = trim($api_key_to_use);
            
            $headers = array(
                'Authorization' => 'Bearer ' . $api_key_to_use,
                'Content-Type' => 'application/json'
            );
            
            // Debug: Log headers being sent (mask API key)
            if (defined('WP_DEBUG') && WP_DEBUG) {
                $debug_headers = $headers;
                $debug_headers['Authorization'] = 'Bearer ' . $this->mask_api_key($api_key_to_use);
            }
            
            $this->log('Chat completion request', array('model' => $options['model'], 'original_model' => $original_model));
            
            $response = $this->make_request($url, $body, 'POST', $headers);
        }
        
        // Check for 401 (Unauthorized) and 403 (Forbidden) errors
        $is_auth_error = false;
        if (!$response['success']) {
            $status_code = isset($response['status_code']) ? intval($response['status_code']) : 0;
            $error_str = isset($response['error']) ? (is_string($response['error']) ? $response['error'] : json_encode($response['error'])) : '';
            
            // Check if it's a 401 Unauthorized error (authentication failed)
            if ($status_code === 401 || 
                stripos($error_str, '401') !== false || 
                stripos($error_str, 'unauthorized') !== false ||
                stripos($error_str, 'need to authenticate') !== false ||
                stripos($error_str, 'authentication') !== false) {
                
                $is_auth_error = true;
                
                // For 401 errors, mark key as blocked (invalid/expired keys should be blocked)
                if (!empty($api_key_to_use)) {
                    $this->mark_key_403_blocked($api_key_to_use);
                }
            }
            
            // Check if it's a 403 Forbidden error
            if ($status_code === 403 || 
                stripos($error_str, '403') !== false || 
                stripos($error_str, 'forbidden') !== false ||
                stripos($error_str, 'permission denied') !== false) {
                
                $is_auth_error = true;
                // Mark key as blocked
                if (!empty($api_key_to_use)) {
                    $this->mark_key_403_blocked($api_key_to_use);
                }
            }
        }
        
        // Log API response for debugging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // Debug logging disabled
        }
        
        // Skip retry for authentication errors (401/403) - go directly to fallback
        // Only retry for parameter errors (unsupported parameters)
        if (!$response['success'] && !$is_auth_error && isset($response['error'])) {
            $error_str = is_string($response['error']) ? $response['error'] : json_encode($response['error']);
            $retry_needed = false;
            
            // Check if error is about max_tokens
            if (stripos($error_str, 'max_tokens') !== false || 
                stripos($error_str, 'max_completion_tokens') !== false) {
                
                unset($body['max_tokens']);
                $retry_needed = true;
            }
            
            // Check if error is about temperature
            if (stripos($error_str, 'temperature') !== false || 
                stripos($error_str, 'does not support') !== false) {
                
                unset($body['temperature']);
                $retry_needed = true;
            }
            
            // Check for generic unsupported parameter errors
            if (stripos($error_str, 'unsupported_parameter') !== false || 
                stripos($error_str, 'unsupported value') !== false) {
                
                // Remove optional parameters that might not be supported
                unset($body['temperature']);
                unset($body['top_p']);
                $retry_needed = true;
            }
            
            if ($retry_needed) {
                $response = $this->make_request($url, $body, 'POST', $headers);
                
                // Check for 401 (Unauthorized) and 403 (Forbidden) errors in retry response
                if (!$response['success']) {
                    $status_code = isset($response['status_code']) ? intval($response['status_code']) : 0;
                    $error_str = isset($response['error']) ? (is_string($response['error']) ? $response['error'] : json_encode($response['error'])) : '';
                    
                    // Check if it's a 401 Unauthorized error (authentication failed)
                    if ($status_code === 401 || 
                        stripos($error_str, '401') !== false || 
                        stripos($error_str, 'unauthorized') !== false ||
                        stripos($error_str, 'need to authenticate') !== false ||
                        stripos($error_str, 'authentication') !== false) {
                        
                        // For 401 errors, mark key as blocked (invalid/expired keys should be blocked)
                        if (!empty($api_key_to_use)) {
                            $this->mark_key_403_blocked($api_key_to_use);
                        }
                    }
                    
                    // Check if it's a 403 Forbidden error
                    if ($status_code === 403 || 
                        stripos($error_str, '403') !== false || 
                        stripos($error_str, 'forbidden') !== false ||
                        stripos($error_str, 'permission denied') !== false) {
                        
                        // Mark key as blocked
                        if (!empty($api_key_to_use)) {
                            $this->mark_key_403_blocked($api_key_to_use);
                        }
                    }
                }
            }
        }
        
        // Fallback to free API endpoint if main API fails or all keys are blocked
        if (!$response['success']) {
            
            // Use free endpoint: https://text.pollinations.ai/openai
            $fallback_url = 'https://text.pollinations.ai/openai';
            
            // Simplified body for free endpoint (only essential parameters)
            $fallback_body = array(
                'model' => 'openai',
                'reasoning_effort' => isset($options['reasoning_effort']) ? $options['reasoning_effort'] : 'high',
                'messages' => $messages
            );
            
            // Free endpoint doesn't need Authorization header
            $fallback_headers = array(
                'Content-Type' => 'application/json'
            );
            
            $this->log('Fallback to free endpoint', array('url' => $fallback_url, 'model' => $options['model']));
            
            $fallback_response = $this->make_request($fallback_url, $fallback_body, 'POST', $fallback_headers);
            
            if ($fallback_response['success']) {
                $response = $fallback_response;
            } else {
                // Ensure error is always set
                if (!isset($response['error'])) {
                    $response['error'] = 'API request failed (main and fallback both failed)';
                }
                return $response;
            }
        }
        
        if (!$response['success']) {
            // Ensure error is always set
            if (!isset($response['error'])) {
                $response['error'] = 'API request failed';
            }
            return $response;
        }
        
        $data = $response['data'];
        
        // Validate response structure
        if (!isset($data['choices']) || !is_array($data['choices']) || empty($data['choices'])) {
            return array(
                'success' => false,
                'error' => 'Invalid response structure: missing choices array'
            );
        }
        
        if (!isset($data['choices'][0]['message']['content'])) {
            return array(
                'success' => false,
                'error' => 'Invalid response structure: missing message content'
            );
        }
        
        return array(
            'success' => true,
            'content' => $data['choices'][0]['message']['content'],
            'usage' => isset($data['usage']) ? $data['usage'] : null,
            'model' => isset($data['model']) ? $data['model'] : $options['model'],
            'finish_reason' => isset($data['choices'][0]['finish_reason']) ? $data['choices'][0]['finish_reason'] : null
        );
    }
    
    /**
     * Test connection
     */
    public function test_connection($model = null) {
        if (!$this->is_configured()) {
            return array(
                'success' => false,
                'message' => 'Pollinations.ai API key not configured'
            );
        }
        
        // Use provided model or default
        $test_model = $model ?: $this->default_model;
        
        // Get model name for display
        $available_models = $this->get_available_models();
        $model_name = isset($available_models[$test_model]) ? $available_models[$test_model] : $test_model;
        
        // Test with prompt asking model to introduce itself
        $test_prompt = sprintf(
            __('Please introduce yourself (AI model) along with your model name (%s) and the last date your data was updated. Reply in English.', 'ai-auto-tool'),
            $model_name
        );
        
        $test_messages = array(
            array(
                'role' => 'user',
                'content' => $test_prompt
            )
        );
        
        $test_result = $this->chat_completion($test_messages, array('model' => $test_model));
        
        // Log for debugging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // Debug logging disabled
        }
        
        if ($test_result['success']) {
            // Get the actual model used in response
            $response_model = isset($test_result['model']) ? $test_result['model'] : $test_model;
            $response_model_name = isset($available_models[$response_model]) ? $available_models[$response_model] : $response_model;
            
            // Get response content - chat_completion returns 'content' key
            $response_content = '';
            if (isset($test_result['content'])) {
                $response_content = is_string($test_result['content']) ? trim($test_result['content']) : '';
            } elseif (isset($test_result['response'])) {
                $response_content = is_string($test_result['response']) ? trim($test_result['response']) : '';
            }
            
            // Log final response content
            if (defined('WP_DEBUG') && WP_DEBUG) {
                // Debug logging disabled
            }
            
            return array(
                'success' => true,
                'message' => 'Pollinations.ai API connection successful!',
                'response' => $response_content, // Always include response, even if empty
                'model' => $response_model,
                'model_name' => $response_model_name,
                'usage' => isset($test_result['usage']) ? $test_result['usage'] : null
            );
        } else {
            return array(
                'success' => false,
                'message' => 'Pollinations.ai API Error: ' . (isset($test_result['error']) ? $test_result['error'] : 'Unknown error'),
                'model' => $test_model,
                'model_name' => $model_name
            );
        }
    }
    
    /**
     * Get models list from Pollinations.ai
     */
    public function get_models() {
        if (!$this->is_configured()) {
            return array(
                'success' => false,
                'error' => 'Pollinations.ai API key not configured'
            );
        }
        
        $url = 'https://enter.pollinations.ai/api/generate/text/models';
        
        // Get API key to use - try to get and rotate if needed
        $api_key_to_use = trim($this->api_key); // Trim whitespace
        $key_source = 'stored';
        
        // Check if stored key is blocked
        if (!empty($api_key_to_use) && $this->is_key_403_blocked($api_key_to_use)) {
            $api_key_to_use = ''; // Force refresh
        }
        
        if (empty($api_key_to_use)) {
            // Get rotated key (will skip blocked keys)
            $rotated_key = $this->get_rotated_key();
            
            if (!empty($rotated_key)) {
                $api_key_to_use = trim($rotated_key); // Trim whitespace
                $key_source = 'rotated';
                $this->api_key = $api_key_to_use; // Update instance key
            } else {
                // All keys are blocked - return error
                return array(
                    'success' => false,
                    'error' => 'All API keys are 403-blocked. Please wait 15 minutes or add new keys.'
                );
            }
        }
        
        // Validate API key format (should start with plln_sk_)
        if (!empty($api_key_to_use) && !preg_match('/^plln_sk_/i', $api_key_to_use)) {
            // API key format may be invalid
        }
        
        $headers = array(
            'Authorization' => 'Bearer ' . $api_key_to_use
        );
        
        $response = $this->make_request($url, null, 'GET', $headers);
        
        // Check for 401 (Unauthorized) and 403 (Forbidden) errors
        if (!$response['success']) {
            $status_code = isset($response['status_code']) ? intval($response['status_code']) : 0;
            $error_str = isset($response['error']) ? (is_string($response['error']) ? $response['error'] : json_encode($response['error'])) : '';
            
            // Check if it's a 401 Unauthorized error (authentication failed)
            if ($status_code === 401 || 
                stripos($error_str, '401') !== false || 
                stripos($error_str, 'unauthorized') !== false ||
                stripos($error_str, 'need to authenticate') !== false ||
                stripos($error_str, 'authentication') !== false) {
                
                // For 401 errors, mark key as blocked (invalid/expired keys should be blocked)
                if (!empty($api_key_to_use)) {
                    $this->mark_key_403_blocked($api_key_to_use);
                }
            }
            
            // Check if it's a 403 Forbidden error
            if ($status_code === 403 || 
                stripos($error_str, '403') !== false || 
                stripos($error_str, 'forbidden') !== false ||
                stripos($error_str, 'permission denied') !== false) {
                
                // Mark key as blocked
                if (!empty($api_key_to_use)) {
                    $this->mark_key_403_blocked($api_key_to_use);
                }
            }
        }
        
        if ($response['success'] && is_array($response['data'])) {
            $models = array();
            foreach ($response['data'] as $model) {
                // Handle different response formats
                if (is_string($model)) {
                    $models[] = $model;
                } elseif (isset($model['id'])) {
                    $models[] = $model['id'];
                } elseif (isset($model['model'])) {
                    $models[] = $model['model'];
                }
            }
            
            // Update available_models if we got models from API
            if (!empty($models)) {
                $this->available_models = array();
                foreach ($models as $model_id) {
                    $this->available_models[$model_id] = ucfirst(str_replace('-', ' ', $model_id));
                }
            }
            
            return array(
                'success' => true,
                'models' => $models
            );
        }
        
        return $response;
    }
    
    /**
     * Check if configured
     * Pollinations provider can use default API key if user hasn't set one
     */
    public function is_configured() {
        // Check if api_key is already set
        if (!empty($this->api_key)) {
            return true;
        }
        
        // Try to get key from manager
        if (empty($this->api_key)) {
            // Check if user has configured keys first
            $user_has_keys = $this->user_has_configured_keys();
            
            if ($user_has_keys) {
                // User has keys - try to get rotated key
                $this->api_key = $this->get_rotated_key();
                
                // If we got a valid key, use it
                if (!empty($this->api_key)) {
                    return true;
                } else {
                    // Manager returned empty - user has keys but they might be disabled or all blocked
                    // Provider is still configured (will use default key in requests)
                    return true;
                }
            } else {
                // User has no keys - get from manager (might return empty or default)
                $this->api_key = $this->get_rotated_key();
            }
        }
        
        // If we got a valid key from manager, use it
        if (!empty($this->api_key)) {
            return true;
        }
        
        // Check if user has configured any keys
        $user_has_keys = $this->user_has_configured_keys();
        
        // If user has configured keys but they're empty, provider is still configured
        // (will use default key in requests)
        if ($user_has_keys) {
            return true;
        }
        
        // If user hasn't set any keys, use default key (provider is always available)
        if (empty($this->api_key)) {
            $this->api_key = $this->get_default_key();
        }
        
        $result = !empty($this->api_key);
        return $result;
    }
}
