<?php
/**
 * FajaedeSEO AI Service Manager
 * Centralized handler for all AI service integrations
 * Routes SEO, Keywords, SERP, and Text Generation requests through OpenAI and ValueSERP
 */

defined('ABSPATH') or exit;

class FajaedeSEO_AI_Service_Manager {
    
    const DEFAULT_TIMEOUT = 60; 
    const CACHE_TTL = 3600; // 1 hour
    
    // Error codes
    const ERROR_SERVICE_UNAVAILABLE = 'service_unavailable';
    const ERROR_INVALID_RESPONSE = 'invalid_response';
    const ERROR_TIMEOUT = 'timeout';
    const ERROR_NETWORK = 'network_error';
    
    public static function get_base_url() {
        // Deprecated: Maintained to prevent breaking old settings pages
        return 'https://api.openai.com/v1';
    }
    
    public static function check_health($force = false) {
        $openai_key = get_option('fajaedeseo_openai_api_key');
        
        if (empty($openai_key)) {
            update_option('fajaedeseo_last_ai_error', __('No OpenAI API Key configured.', 'fajaedeseo-ai'));
            return false;
        }
        
        delete_option('fajaedeseo_last_ai_error');
        return true;
    }
    
    /**
     * Generate SEO meta (title/description) via OpenAI
     * 
     * @param string $type The meta type ('title' or 'description').
     * @param string $content The post content.
     * @param string $post_title The post title.
     * @param string $site_name The site name.
     * @param string $focus_keyword Optional focus keyword.
     * @return string|\WP_Error
     */
    public static function generate_seo_meta($type, $content, $post_title, $site_name, $focus_keyword = '') {
        if (!in_array($type, ['title', 'description'])) {
            return new \WP_Error('invalid_type', 'Type must be "title" or "description"');
        }
        
        $cache_key = 'fajaedeseo_seo_' . md5($type . $content . $post_title . $focus_keyword);
        $cached = get_transient($cache_key);
        
        if (false !== $cached) {
            return $cached;
        }
        
        $max_length = ($type === 'title') ? 60 : 160;
        $prompt = self::build_seo_prompt($type, $content, $post_title, $site_name, $max_length, $focus_keyword);
        
        $result = self::call_openai($prompt, [
            'max_tokens' => 150,
        ]);
        
        if (is_wp_error($result)) {
            return $result;
        }
        
        $output = self::extract_text_from_response($result);
        set_transient($cache_key, $output, self::CACHE_TTL);
        
        return $output;
    }
    
    /**
     * Extract keywords from content via OpenAI
     * 
     * @param string $content The text content.
     * @param int $count Number of keywords to extract.
     * @return array
     */
    public static function extract_keywords($content, $count = 5) {
        $count = max(1, min($count, 10)); // Limit 1-10
        $cache_key = 'fajaedeseo_keywords_' . md5($content . $count);
        $cached = get_transient($cache_key);
        
        if (false !== $cached) {
            return $cached;
        }
        
        $prompt = "Extract the top {$count} SEO keywords from the following content. Output format MUST be ONLY a raw comma-separated list. DO NOT include any conversational text, introductory sentences like 'Here are...', numbers, or bullet points. Just the keywords separated by commas.\n\n" . wp_strip_all_tags($content);
        
        $result = self::call_openai($prompt, [
            'max_tokens' => 50,
        ]);
        
        if (is_wp_error($result)) {
            return [];
        }
        
        $text = self::extract_text_from_response($result);
        
        // Clean up common AI prefixes just in case the AI ignores the strict prompt
        $text = preg_replace('/^(?:Here are|These are|The top).*?:/i', '', $text);
        // Remove line breaks or bullet points if it formatted as a list anyway
        $text = str_replace(["\r", "\n", "- ", "* "], '', $text);
        
        $keywords = array_filter(array_map('trim', explode(',', $text)));
        
        set_transient($cache_key, $keywords, self::CACHE_TTL);
        
        return array_slice($keywords, 0, $count);
    }
    
    /**
     * Generate Schema.org attributes for a specific type using OpenAI
     * 
     * @param string $type The Schema type (e.g., 'Recipe', 'Event').
     * @param string $content The post content to extract data from.
     * @return array
     */
    public static function generate_schema_attributes($type, $content) {
        $complex_types = ['Recipe', 'Event', 'JobPosting', 'LocalBusiness', 'SoftwareApplication', 'Product', 'Course', 'Movie', 'Book', 'PodcastEpisode'];
        if (!in_array($type, $complex_types)) {
            return [];
        }

        $content_summary = wp_strip_all_tags($content);
        $content_summary = mb_substr($content_summary, 0, 2000, 'UTF-8');

        $cache_key = 'fajaedeseo_schema_attrs_' . md5($type . $content_summary);
        $cached = get_transient($cache_key);

        if (false !== $cached) {
            return $cached;
        }

        $prompt = "You are an expert in Schema.org JSON-LD. Extract structured data properties for the '@type': '{$type}' from the following text.\n";
        $prompt .= "Return ONLY a valid JSON object containing the specific properties relevant to this type (e.g., for Recipe: prepTime, cookTime, recipeIngredient, recipeInstructions; for Event: startDate, endDate, location; for JobPosting: baseSalary, jobLocation).\n";
        $prompt .= "Do NOT include basic properties like @type, name, description, author, or publisher as they are already set. Omit empty properties. Do not wrap in markdown tags like ```json.\n";
        $prompt .= "Text: " . $content_summary;

        $result = self::call_openai($prompt, [
            'max_tokens' => 800,
            'temperature' => 0.1, // Zeer lage temperatuur voor strikte feitelijke extractie
        ]);

        if (is_wp_error($result)) {
            return [];
        }

        $text = self::extract_text_from_response($result);
        $text = preg_replace('/```json\s*/', '', $text);
        $text = preg_replace('/```\s*$/', '', $text);

        $decoded = json_decode(trim($text), true);
        
        if (is_array($decoded)) {
            // Cache voor 30 dagen (cache wordt automatisch ongeldig als de tekst verandert dankzij de md5 hash)
            set_transient($cache_key, $decoded, DAY_IN_SECONDS * 30);
            return $decoded;
        }

        return [];
    }

    /**
     * Analyze SERP data via ValueSERP
     * 
     * @param string $keyword The search keyword.
     * @param string $url Optional URL to compare against.
     * @return array|\WP_Error
     */
    public static function analyze_serp($keyword, $url = '', $gl = '', $hl = '') {
        if (empty($keyword)) {
            return new \WP_Error('empty_keyword', 'Keyword cannot be empty');
        }

        $api_key = get_option('fajaedeseo_serp_api_key');
        if (empty($api_key)) {
            return new \WP_Error('missing_api_key', 'SERP API key is missing. Please configure your ValueSERP key in the FajaedeSEO AI Settings.');
        }
        
        $cache_key = 'fajaedeseo_serp_' . md5($keyword . $url);
        $cached = get_transient($cache_key);
        
        if (false !== $cached) {
            return $cached;
        }

        // Bepaal de taal en het land dynamisch op basis van WordPress instellingen
        $locale = get_locale(); // Bijv: 'nl_NL', 'en_US', 'de_DE'
        $lang = !empty($hl) ? $hl : substr($locale, 0, 2); 
        $country = !empty($gl) ? $gl : strtolower(substr($locale, 3, 2) ?: $lang); 
        
        // Fetch from ValueSERP
        $api_url = add_query_arg([
            'api_key' => $api_key,
            'q'       => $keyword,
            'gl'      => $country,
            'hl'      => $lang,
            'num'     => 5     // Only fetch top 5 to speed up response
        ], 'https://api.valueserp.com/search');

        $response = wp_remote_get($api_url, ['timeout' => 15, 'sslverify' => true]);

        if (is_wp_error($response)) {
            return new \WP_Error('serp_api_error', 'Connection to SERP API failed: ' . $response->get_error_message());
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (isset($data['request_info']['success']) && $data['request_info']['success'] === false) {
            return new \WP_Error('serp_api_error', 'SERP API error: ' . ($data['request_info']['message'] ?? 'Unknown'));
        }

        $result = [
            'results' => []
        ];

        if (!empty($data['organic_results'])) {
            foreach (array_slice($data['organic_results'], 0, 5) as $res) {
                $result['results'][] = [
                    'title' => $res['title'] ?? '',
                    'url'   => $res['link'] ?? '',
                    'snippet' => $res['snippet'] ?? ''
                ];
            }
        }
        
        // Cache for 24 hours to save the user's API credits
        set_transient($cache_key, $result, DAY_IN_SECONDS); 
        
        return $result;
    }
    
    /**
     * Generate a PDF report using Dompdf
     * @param string $html The HTML to convert
     */
    public static function generate_pdf($html) {
        // Check if Dompdf is loaded
        if (!class_exists('\Dompdf\Dompdf')) {
            // Fallback error, Dompdf should be loaded globally via Composer in fajaedeseo.php
            return new \WP_Error('pdf_error', 'Dompdf library is missing. Please make sure Composer dependencies are installed.');
        }

        try {
            // Gebruik strings voor de class names om Intelephense "Undefined type" errors te voorkomen 
            // als de vendor map nog niet is gedownload of ge-indexeerd.
            $options_class = '\Dompdf\Options';
            $options = new $options_class();
            $options->set('isRemoteEnabled', true); // Allow loading remote images (e.g., logo's in the report)
            $options->set('defaultFont', 'Helvetica');
            
            $dompdf_class = '\Dompdf\Dompdf';
            $dompdf = new $dompdf_class($options);
            $dompdf->loadHtml($html);
            $dompdf->setPaper('A4', 'portrait');
            $dompdf->render();
            
            $output = $dompdf->output();
            return base64_encode($output);
        } catch (\Exception $e) {
            return new \WP_Error('pdf_error', 'Failed to generate PDF: ' . $e->getMessage());
        }
    }

    /**
     * Call OpenAI directly for advanced AI tasks
     * 
     * @param string $prompt The prompt to send.
     * @param array $options Additional generation options.
     * @return string|\WP_Error
     */
    public static function call_openai($prompt, $options = []) {
        // Bepaal het beste AI model op basis van het actieve abonnement
        $plan = class_exists('\FajaedeSEO_Pro_License') ? \FajaedeSEO_Pro_License::get_plan_level() : 'free';
        $assigned_model = 'gpt-4o-mini-2024-07-18'; // Standaard model voor 'Pro'
        if ($plan === 'premium' || $plan === 'agency') {
            $assigned_model = 'gpt-4o-2024-08-06'; // Geavanceerd model voor dure abonnementen
        }

        $options = wp_parse_args($options, [
            'model' => $assigned_model,
            'temperature' => 0.7,
            'max_tokens' => 500,
        ]);
        
        $cache_key = 'fajaedeseo_openai_' . md5($prompt . wp_json_encode($options));
        $cached = get_transient($cache_key);
        
        if (false !== $cached) {
            return $cached;
        }
        
        $openai_key = get_option('fajaedeseo_openai_api_key');
        if (empty($openai_key)) {
            return new \WP_Error('missing_key', __('Vul je OpenAI API sleutel in via de instellingen.', 'fajaedeseo-ai'));
        }
        
        $url = 'https://api.openai.com/v1/chat/completions';
        $payload = [
            'model'       => $options['model'],
            'messages'    => [
                ['role' => 'system', 'content' => 'You are an expert SEO copywriter and assistant. Always return formatting in HTML without markdown backticks.'],
                ['role' => 'user', 'content' => $prompt]
            ],
            'temperature' => floatval($options['temperature']),
            'max_tokens'  => intval($options['max_tokens']),
        ];
        
        $response = wp_remote_post($url, [
            'body' => wp_json_encode($payload),
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . sanitize_text_field((string)$openai_key),
            ],
            'timeout' => self::DEFAULT_TIMEOUT,
            'sslverify' => true,
        ]);
        
        // Network error
        if (is_wp_error($response)) {
            return new \WP_Error(
                self::ERROR_NETWORK,
                sprintf('OpenAI service unavailable: %s', $response->get_error_message())
            );
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        $data = json_decode($body, true);
        
        if ($status_code !== 200) {
            $error_msg = $data['error']['message'] ?? 'Unknown OpenAI Error';
            return new \WP_Error(
                self::ERROR_SERVICE_UNAVAILABLE,
                sprintf('OpenAI Error (HTTP %d): %s', $status_code, $error_msg)
            );
        }
        
        if (isset($data['choices'][0]['message']['content'])) {
            $output = trim($data['choices'][0]['message']['content']);
            set_transient($cache_key, $output, self::CACHE_TTL);
            return $output;
        }
        
        return new \WP_Error('invalid_response', 'Invalid response from OpenAI');
    }
    
    /**
     * Build SEO meta prompt
     * 
     * @param string $type The meta type ('title' or 'description').
     * @param string $content The post content.
     * @param string $post_title The post title.
     * @param string $site_name The site name.
     * @param int $max_length The maximum length in characters.
     * @param string $focus_keyword Optional focus keyword.
     * @return string
     */
    private static function build_seo_prompt($type, $content, $post_title, $site_name, $max_length, $focus_keyword = '') {
        $content_summary = wp_strip_all_tags($content);
        $content_summary = mb_substr($content_summary, 0, 500, 'UTF-8');
        
        $keyword_instruction = !empty($focus_keyword) ? " You MUST include the exact focus keyword '" . sanitize_text_field((string)$focus_keyword) . "' naturally in the text." : "";
        
        // Bepaal de kwaliteit van de meta-data op basis van het abonnement (Upsell voor meer vertoningen)
        $plan = class_exists('\FajaedeSEO_Pro_License') ? \FajaedeSEO_Pro_License::get_plan_level() : 'free';
        $advanced_seo_instruction = '';
        if ($plan === 'premium' || $plan === 'agency') {
            $advanced_seo_instruction = " Apply advanced copywriting techniques: use psychological triggers, a strong Call-To-Action (CTA), and naturally mix in LSI (Latent Semantic Indexing) keywords to maximize CTR and search impressions.";
        } elseif ($plan === 'pro') {
            $advanced_seo_instruction = " Ensure the text is engaging and click-worthy to improve CTR.";
        }
        
        if ($type === 'title') {
            return sprintf(
                "You are an SEO Expert. Generate a compelling, SEO-optimized title for a post titled '%s' on '%s' website. Max %d characters.%s%s Return ONLY the title text. Content: %s",
                sanitize_text_field($post_title),
                sanitize_text_field($site_name),
                $max_length,
                $keyword_instruction,
                $advanced_seo_instruction,
                $content_summary
            );
        }
        
        return sprintf(
            "You are an SEO Expert. Generate a compelling, SEO-optimized meta description for a post titled '%s' on '%s' website. Max %d characters.%s%s Return ONLY the description text. Content: %s",
            sanitize_text_field($post_title),
            sanitize_text_field($site_name),
            $max_length,
            $keyword_instruction,
            $advanced_seo_instruction,
            $content_summary
        );
    }
    
    /**
     * Extract text from AI response
     * Handles different response formats
     * 
     * @param mixed $response The response from the API.
     * @return string
     */
    public static function extract_text_from_response($response) {
        if (is_array($response)) {
            // Try common response keys
            if (isset($response['text'])) {
                return sanitize_text_field((string)$response['text']);
            }
            if (isset($response['result'])) {
                return sanitize_text_field((string)$response['result']);
            }
            if (isset($response['output'])) {
                return sanitize_text_field((string)$response['output']);
            }
            if (isset($response['response'])) {
                return sanitize_text_field((string)$response['response']);
            }
            // If it's just a string in the array
            if (isset($response[0]) && is_string($response[0])) {
                return sanitize_text_field((string)$response[0]);
            }
        }
        
        return sanitize_text_field((string)$response);
    }
    
    /**
     * Generate an image via OpenAI gpt-image-2-2026-04-21
     * 
     * @param string $prompt The prompt for the image.
     * @param array $options Additional options.
     * @return string|\WP_Error
     */
    public static function generate_image($prompt, $options = []) {
        // Haal het ingestelde image model op
        $saved_model = get_option('fajaedeseo_openai_image_model', 'gpt-image-2-2026-04-21');
        $clean_model = strtolower(str_replace(' ', '-', trim($saved_model)));
        
        $options = wp_parse_args($options, [
            'model' => $clean_model,
            'n' => 1,
            'size' => '1024x1024',
        ]);
        
        $openai_key = get_option('fajaedeseo_openai_api_key');
        if (empty($openai_key)) {
            return new \WP_Error('missing_key', __('Vul je OpenAI API sleutel in via de instellingen.', 'fajaedeseo-ai'));
        }
        
        $url = 'https://api.openai.com/v1/images/generations';
        $payload = [
            'model'           => $options['model'],
            'prompt'          => $prompt,
            'n'               => intval($options['n']),
            'size'            => $options['size'],
        ];

        $args = [
            'body' => wp_json_encode($payload),
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . sanitize_text_field((string)$openai_key),
            ],
            'timeout' => 120, // Images kunnen lang duren, geef OpenAI genoeg tijd!
            'sslverify' => true,
        ];
        
        $response = wp_remote_post($url, $args);
        
        if (is_wp_error($response)) {
            return new \WP_Error(
                self::ERROR_NETWORK,
                sprintf('OpenAI service unavailable: %s', $response->get_error_message())
            );
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        $data = json_decode($body, true);
        
        if ($status_code !== 200) {
            $error_msg = $data['error']['message'] ?? 'Unknown OpenAI Error';
            
            return new \WP_Error(
                self::ERROR_SERVICE_UNAVAILABLE,
                sprintf('OpenAI Error (HTTP %d): %s', $status_code, $error_msg)
            );
        }
        
        if (isset($data['data'][0]['url'])) {
            return (string) $data['data'][0]['url'];
        }
        
        if (isset($data['data'][0]['b64_json'])) {
            return 'data:image/png;base64,' . (string) $data['data'][0]['b64_json'];
        }
        
        return new \WP_Error('invalid_response', 'Invalid response from OpenAI');
    }

    /**
     * Clear all AI service caches
     */
    public static function clear_cache() {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk deletion using wildcard patterns requires a direct query as there is no core wrapper for this.
        $wpdb->query(
            "DELETE FROM $wpdb->options WHERE option_name LIKE '%fajaedeseo_seo_%'
             OR option_name LIKE '%fajaedeseo_keywords_%'
             OR option_name LIKE '%fajaedeseo_serp_%'
             OR option_name LIKE '%fajaedeseo_openai_%'
             OR option_name LIKE '%fajaedeseo_ai_health_%'"
        );
    }
    
    /**
     * Get service status and diagnostics
     */
    public static function get_diagnostics() {
        return [
            'base_url' => self::get_base_url(),
            'is_healthy' => self::check_health(),
            'last_check' => current_time('mysql'),
            'last_error' => get_option('fajaedeseo_last_ai_error', 'None'),
            'version' => defined('FAJAEDESEO_VERSION') ? FAJAEDESEO_VERSION : 'unknown',
        ];
    }
}
