<?php
/**
 * FajaedeSEO AI Endpoints Module
 * Specific AI endpoint handlers for SEO, Keywords, SERP, and Text Generation
 */

defined('ABSPATH') or exit;

/**
 * SEO Module - Handle title/description generation
 */
class FajaedeSEO_Module_SEO {
    
    public static function init() {
        add_action('rest_api_init', [__CLASS__, 'register_routes']);
    }

    public static function register_routes() {
        register_rest_route('fajaedeseo/v1', '/generate-meta', [
            'methods' => 'POST',
            'callback' => function($request) {
                $params = $request->get_json_params();
                $content = isset($params['content']) ? wp_kses_post($params['content']) : '';
                $type = isset($params['type']) ? sanitize_key($params['type']) : 'desc';
                $post_title = isset($params['postTitle']) ? sanitize_text_field((string)$params['postTitle']) : 'Post';
                $site_name = isset($params['siteName']) ? sanitize_text_field((string)$params['siteName']) : get_bloginfo('name');
                $focus_keyword = isset($params['focus_keyword']) ? sanitize_text_field((string)$params['focus_keyword']) : '';
                
                if ($type === 'title') {
                    $result = self::generate_title($content, $post_title, $site_name, $focus_keyword);
                } else {
                    $result = self::generate_description($content, $post_title, $site_name, $focus_keyword);
                }
                return ['generated' => $result];
            },
            'permission_callback' => fn() => current_user_can('edit_posts')
        ]);

        register_rest_route('fajaedeseo/v1', '/bulk-generate-meta', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'bulk_generate_meta_callback'],
            'permission_callback' => fn() => current_user_can('manage_options') && \FajaedeSEO_Pro_License::has_feature('bulk_actions')
        ]);


        register_rest_route('fajaedeseo/v1', '/generate-post-title', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'generate_post_title_callback'],
            'permission_callback' => fn() => current_user_can('edit_posts')
        ]);

        register_rest_route('fajaedeseo/v1', '/generate-image', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'generate_image_callback'],
            'permission_callback' => function(\WP_REST_Request $request) {
                if (!\FajaedeSEO_Pro_License::is_pro()) return false;
                if (!current_user_can('edit_posts')) return false;
                $params = $request->get_json_params() ?: [];
                $post_id = isset($params['post_id']) ? intval($params['post_id']) : 0;
                return $post_id ? current_user_can('edit_post', $post_id) : true;
            }
        ]);

        register_rest_route('fajaedeseo/v1', '/generate-faq', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'generate_faq_callback'],
            'permission_callback' => fn() => current_user_can('edit_posts') && class_exists('\FajaedeSEO_Pro_License') && in_array(\FajaedeSEO_Pro_License::get_plan_level(), ['premium', 'agency'])
        ]);

        register_rest_route('fajaedeseo/v1', '/optimize-extraction', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'optimize_extraction_callback'],
            'permission_callback' => fn() => current_user_can('edit_posts') && class_exists('\FajaedeSEO_Pro_License') && in_array(\FajaedeSEO_Pro_License::get_plan_level(), ['premium', 'agency'])
        ]);
    }

    public static function bulk_generate_meta_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $post_ids = isset($params['post_ids']) ? array_filter(array_map('intval', (array)$params['post_ids'])) : [];
        
        // Valideer meta types: 'title', 'description' en 'keywords' toestaan
        $allowed_types = ['title', 'description', 'keywords'];
        $meta_types = isset($params['meta_types']) ? array_intersect((array)$params['meta_types'], $allowed_types) : $allowed_types;
        
        $results = [];

        if (empty($post_ids) || empty($meta_types)) {
            return new \WP_Error('no_ids', 'Geen geldige Post ID\'s opgegeven.', ['status' => 400]);
        }

        foreach ($post_ids as $post_id) {
            if (!current_user_can('edit_post', $post_id)) {
                $results[$post_id] = ['success' => false, 'message' => __('Unauthorized for this post.', 'fajaedeseo-ai')];
                continue;
            }
            
            $post = get_post($post_id);
            if (!$post) {
                $results[$post_id] = ['success' => false, 'message' => 'Post bestaat niet.'];
                continue;
            }

            $content = $post->post_content;
            $post_title = $post->post_title;
            $site_name = get_bloginfo('name');
            $results[$post_id] = self::generate_meta_for_post($post_id, $content, $post_title, $site_name, $meta_types);
        }

        return new \WP_REST_Response($results, 200);
    }

    private static function generate_meta_for_post(int $post_id, string $content, string $post_title, string $site_name, array $meta_types) {
        $post_results = ['success' => false, 'title' => '', 'description' => '', 'keywords' => ''];
        $focus_keyword = get_post_meta($post_id, '_fajaedeseo_focus_keyword', true);

        // 1. Genereer keywords als eerste, zodat we het primaire zoekwoord in titel/desc kunnen dwingen
        if (in_array('keywords', $meta_types)) {
            $generated_keywords = \FajaedeSEO_Module_Keywords::extract_focus_keywords($content, 5);
            if (!is_wp_error($generated_keywords) && !empty($generated_keywords)) {
                $keywords_string = implode(', ', $generated_keywords);
                update_post_meta($post_id, '_fajaedeseo_focus_keyword', $keywords_string);
                $post_results['keywords'] = $keywords_string;
                $post_results['success'] = true;
                $focus_keyword = $generated_keywords[0]; // Gebruik de belangrijkste
            } else {
                $post_results['keywords'] = is_wp_error($generated_keywords) ? $generated_keywords->get_error_message() : 'Geen keywords gevonden.';
            }
        }

        $primary_keyword = '';
        if (!empty($focus_keyword)) {
            $keyword_parts = array_map('trim', explode(',', $focus_keyword));
            $primary_keyword = $keyword_parts[0];
        }

        if (in_array('title', $meta_types)) {
            $generated_title = self::generate_title($content, $post_title, $site_name, $primary_keyword);
            if (!is_wp_error($generated_title)) {
                update_post_meta($post_id, '_fajaedeseo_meta_title', $generated_title);
                $post_results['title'] = $generated_title;
                $post_results['success'] = true;
            } else {
                $post_results['title'] = $generated_title->get_error_message();
            }
        }

        if (in_array('description', $meta_types)) {
            $generated_description = self::generate_description($content, $post_title, $site_name, $primary_keyword);
            if (!is_wp_error($generated_description)) {
                update_post_meta($post_id, '_fajaedeseo_meta_desc', $generated_description);
                $post_results['description'] = $generated_description;
                $post_results['success'] = true;
            } else {
                $post_results['description'] = $generated_description->get_error_message();
            }
        }

        return $post_results;
    }

    public static function generate_image_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $prompt = isset($params['prompt']) ? sanitize_text_field((string)$params['prompt']) : '';
        $post_id = isset($params['post_id']) ? intval($params['post_id']) : 0;

        if (empty($prompt)) {
            return new \WP_Error('empty_prompt', 'Please provide a prompt.', ['status' => 400]);
        }

        if (!class_exists('\FajaedeSEO_AI_Service_Manager')) {
            return new \WP_Error('service_error', 'AI Service Manager not found.', ['status' => 500]);
        }

        // Generate image URL
        $result = \FajaedeSEO_AI_Service_Manager::generate_image($prompt);
        
        if (is_wp_error($result)) {
            return $result;
        }

        // Null safe fallback to prevent PHP 8.1+ strlen() deprecation warnings
        $result = $result ?? '';
        if (empty($result)) {
            return new \WP_Error('invalid_image_url', 'A valid URL was not provided.');
        }

        // Sideload the image into WordPress Media Library
        require_once(ABSPATH . 'wp-admin/includes/media.php');
        require_once(ABSPATH . 'wp-admin/includes/file.php');
        require_once(ABSPATH . 'wp-admin/includes/image.php');

        // Handle base64 image data or download external URL
        if (strpos($result, 'data:image/') === 0) {
            $image_data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $result));
            $tmp = wp_tempnam('ai-image.png');
            file_put_contents($tmp, $image_data);
        } else {
            // Workaround for media_sideload_image rejecting gpt-image-2-2026-04-21 (.webp) URLs
            $tmp = download_url( $result );
            if ( is_wp_error( $tmp ) ) {
                return $tmp;
            }
        }

        $mime_type = mime_content_type( $tmp );
        $extension = 'png';
        if ( $mime_type === 'image/webp' ) {
            $extension = 'webp';
        } elseif ( $mime_type === 'image/jpeg' ) {
            $extension = 'jpg';
        } elseif ( $mime_type === 'image/gif' ) {
            $extension = 'gif';
        }

        $file_array = [
            'name'     => 'ai-image-' . time() . '.' . $extension,
            'tmp_name' => $tmp
        ];

        $attachment_id = media_handle_sideload( $file_array, $post_id, $prompt );

        if (is_wp_error($attachment_id)) {
            wp_delete_file($tmp);
            return $attachment_id;
        }

        $local_url = wp_get_attachment_url($attachment_id);

        return new \WP_REST_Response([
            'success' => true,
            'image_url' => $local_url,
            'attachment_id' => $attachment_id
        ], 200);
    }

    public static function generate_post_title_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $content = isset($params['content']) ? wp_kses_post($params['content']) : '';
        $focus_keyword = isset($params['focus_keyword']) ? sanitize_text_field((string)$params['focus_keyword']) : '';

        $ai_prompt = "You are an SEO Expert. Generate a single, catchy, and highly clickable blog post title (H1) for an article. Return ONLY the title text. No quotes. No intro.";
        if (!empty($focus_keyword)) $ai_prompt .= "\nThe title MUST include the keyword: '" . $focus_keyword . "'.";
        if (!empty($content)) $ai_prompt .= "\nArticle content summary: " . mb_substr(wp_strip_all_tags($content), 0, 1000);

        $result = \FajaedeSEO_AI_Service_Manager::call_openai($ai_prompt, ['max_tokens' => 100]);
        if (is_wp_error($result)) return $result;
        return ['title' => trim(str_replace(['"', "'", "*", "#"], '', $result))];
    }

    public static function generate_faq_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $content = isset($params['content']) ? wp_kses_post($params['content']) : '';
        $focus_keyword = isset($params['focus_keyword']) ? sanitize_text_field((string)$params['focus_keyword']) : '';

        if (empty($content)) {
            return new \WP_Error('empty_content', 'Please write some content first.', ['status' => 400]);
        }

        $ai_prompt = "You are an AI Search Optimizer (AEO). Based on the following content, generate 3 to 5 FAQ questions and answers. \n";
        $ai_prompt .= "CRITICAL RULES:\n";
        $ai_prompt .= "- Formulate questions based on common user intent ('how', 'what', 'why').\n";
        $ai_prompt .= "- Answers MUST be exactly 2 to 4 sentences long (highly optimized for Google AI Overview extraction).\n";
        $ai_prompt .= "- Start the answer directly with the core information (no fluff or long introductions).\n";
        if (!empty($focus_keyword)) $ai_prompt .= "- Use the focus keyword '{$focus_keyword}' naturally.\n";
        $ai_prompt .= "Format the output strictly as HTML with <h3> for questions and <p> for answers. Content context: " . mb_substr(wp_strip_all_tags($content), 0, 1500);

        $result = \FajaedeSEO_AI_Service_Manager::call_openai($ai_prompt, ['max_tokens' => 600, 'temperature' => 0.5]);
        if (is_wp_error($result)) return $result;
        
        return ['faq_html' => trim(str_replace(['```html', '```'], '', $result))];
    }

    public static function optimize_extraction_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $text = isset($params['text']) ? sanitize_textarea_field($params['text']) : '';

        if (empty($text)) {
            return new \WP_Error('empty_text', 'No text provided.', ['status' => 400]);
        }

        $ai_prompt = "You are an AI Overview Extractor. Rewrite the following text to maximize the chance of being cited by Google's AI Overview or Gemini.\n";
        $ai_prompt .= "Rule 1: The very first sentence MUST directly answer the implied question.\n";
        $ai_prompt .= "Rule 2: Remove all fluff, unnecessary context, and conversational filler.\n";
        $ai_prompt .= "Rule 3: Keep it under 5 sentences. Return ONLY the rewritten text without quotes.\n\nText:\n" . $text;

        $result = \FajaedeSEO_AI_Service_Manager::call_openai($ai_prompt, ['max_tokens' => 300, 'temperature' => 0.3]);
        if (is_wp_error($result)) return $result;
        
        return ['optimized_text' => trim($result)];
    }

    /**
     * Generate meta title
     */
    public static function generate_title(string $content, string $post_title, string $site_name = '', string $focus_keyword = '') {
        if (empty($site_name)) {
            $site_name = get_bloginfo('name');
        }
        
        $result = \FajaedeSEO_AI_Service_Manager::generate_seo_meta(
            'title',
            $content,
            $post_title,
            $site_name,
            $focus_keyword
        );
        
        if (is_wp_error($result)) {
            // Fallback to rule-based generation
            return self::fallback_title($post_title, $site_name);
        }
        
        return mb_substr(trim($result), 0, 60, 'UTF-8');
    }
    
    /**
     * Generate meta description
     */
    public static function generate_description(string $content, string $post_title, string $site_name = '', string $focus_keyword = '') {
        if (empty($site_name)) {
            $site_name = get_bloginfo('name');
        }
        
        $result = \FajaedeSEO_AI_Service_Manager::generate_seo_meta(
            'description',
            $content,
            $post_title,
            $site_name,
            $focus_keyword
        );
        
        if (is_wp_error($result)) {
            // Fallback to rule-based generation
            return self::fallback_description($content, $post_title);
        }
        
        return mb_substr(trim($result), 0, 160, 'UTF-8');
    }

    /**
     * Fallback: Rule-based title generation
     */
    private static function fallback_title(string $post_title, string $site_name) {
        $title = sanitize_text_field($post_title);
        $title = preg_replace('/Edit\s+(?:Page|Post)\s+/i', '', $title);
        
        if (strlen($title) > 50) {
            $title = mb_substr($title, 0, 50, 'UTF-8') . '...';
        }
        
        if (!empty($site_name) && strlen($title) + strlen($site_name) < 60) {
            $title .= ' | ' . $site_name;
        }
        
        return $title;
    }
    
    /**
     * Fallback: Rule-based description generation
     */
    private static function fallback_description(string $content, string $post_title) {
        $text = wp_strip_all_tags($content);
        $text = preg_replace('/\s+/', ' ', $text);
        $desc = mb_substr($text, 0, 150, 'UTF-8');
        
        if (strlen($desc) < 80 && !empty($post_title)) {
            $desc = sanitize_text_field($post_title) . ' - ' . $desc;
        }
        
        return mb_substr($desc, 0, 160, 'UTF-8');
    }
}

/**
 * Keywords Module
 */
class FajaedeSEO_Module_Keywords {
    public static function init() {
        add_action('rest_api_init', [__CLASS__, 'register_routes']);
    }

    public static function register_routes() {
        register_rest_route('fajaedeseo/v1', '/suggest-keywords', [
            'methods' => 'POST',
            'callback' => function($request) {
                $params = $request->get_json_params();
                $content = isset($params['content']) ? wp_kses_post($params['content']) : '';
                $count = isset($params['count']) ? intval($params['count']) : 5;

                $keywords = self::extract_focus_keywords($content, $count);
                return ['keywords' => $keywords];
            },
            'permission_callback' => function($request) {
                $params = $request->get_json_params();
                $post_id = isset($params['postId']) ? intval($params['postId']) : 0;
                return $post_id ? current_user_can('edit_post', $post_id) : current_user_can('edit_posts');
            }
        ]);
    }

    public static function extract_focus_keywords(string $content, int $count = 5) {
        if (!class_exists('\FajaedeSEO_AI_Service_Manager')) {
            return new \WP_Error('ai_service_unavailable', 'AI Service Manager not found.', ['status' => 500]);
        }
        return \FajaedeSEO_AI_Service_Manager::extract_keywords($content, $count);
    }
}

/**
 * SERP Analysis Module
 */
class FajaedeSEO_Module_SERP {
    public function init() {
        add_action('rest_api_init', [$this, 'register_routes']);
    }

    public function register_routes() {
        register_rest_route('fajaedeseo/v1', '/competitor-analysis', [
            'methods' => 'POST',
            'callback' => [$this, 'analyze_serp_callback'],
            // Beveiliging: Alleen voor ingelogde auteurs EN vereist de 'advanced_serp' Premium feature!
            'permission_callback' => function() {
                if (!current_user_can('edit_posts')) {
                    return false;
                }
                if (!\FajaedeSEO_Pro_License::has_feature('advanced_serp')) {
                    return new \WP_Error('premium_locked', __('Get Premium to unlock SERP Competitor Analysis.', 'fajaedeseo-ai'), ['status' => 403]);
                }
                return true;
            }
        ]);
    }

    public function analyze_serp_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $keyword = isset($params['keyword']) ? sanitize_text_field((string)$params['keyword']) : '';
        $gl = isset($params['gl']) ? sanitize_text_field((string)$params['gl']) : '';
        $hl = isset($params['hl']) ? sanitize_text_field((string)$params['hl']) : '';

        if (empty($keyword)) {
            return new \WP_Error('missing_keyword', 'Geef een zoekwoord op voor de SERP-analyse.', ['status' => 400]);
        }

        $serp_results = \FajaedeSEO_AI_Service_Manager::analyze_serp($keyword, '', $gl, $hl);
        return is_wp_error($serp_results) ? $serp_results : ['serp' => $serp_results];
    }
}

/**
 * Site Audit Module
 */
class FajaedeSEO_Module_Audit {
    public static function init() {
        add_action('rest_api_init', [__CLASS__, 'register_routes']);
    }

    public static function register_routes() {
        register_rest_route('fajaedeseo/v1', '/site-audit', [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'get_site_audit_data'],
            'permission_callback' => fn() => current_user_can('edit_posts')
        ]);

        register_rest_route('fajaedeseo/v1', '/generate-pdf', [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'generate_pdf_report_callback'],
            'permission_callback' => fn() => current_user_can('edit_posts') && \FajaedeSEO_Pro_License::has_feature('white_label_pdf')
        ]);

        register_rest_route('fajaedeseo/v1', '/agency-status', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'agency_status_callback'],
            'permission_callback' => '__return_true' // De daadwerkelijke authenticatie doen we veilig en lokaal in de callback
        ]);

        register_rest_route('fajaedeseo/v1', '/agency-generate-pdf', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'agency_generate_pdf_callback'],
            'permission_callback' => fn() => current_user_can('manage_options') && defined('FAJAEDE_MASTER_SERVER') && FAJAEDE_MASTER_SERVER && \FajaedeSEO_Pro_License::has_feature('white_label_pdf')
        ]);

        register_rest_route('fajaedeseo/v1', '/agency-login', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'agency_login_callback'],
            'permission_callback' => '__return_true' // Authenticatie gebeurt lokaal in de callback
        ]);
    }

    private static function get_pdf_html(string $site_name, string $site_url, int $overall_score, int $pages_indexed, int $page_speed, array $orphan_pages, int $orphan_pages_count, array $geo_data, $serp_data = null, string $type = 'audit', string $keyword = '') {
        $primary_color = get_option('fajaedeseo_agency_color', '#0073aa');
        
        $html = '<style>
            body { font-family: "Helvetica", sans-serif; color: #333; line-height: 1.6; font-size: 14px; }
            .cover { text-align: center; margin-top: 150px; }
            .cover h1 { font-size: 42px; color: ' . esc_attr($primary_color) . '; margin-bottom: 10px; }
            .cover h2 { font-size: 24px; color: #666; font-weight: normal; }
            .cover p { margin-top: 50px; font-size: 16px; color: #999; }
            .page-break { page-break-after: always; }
            .header { text-align: right; margin-bottom: 30px; }
            h2.section { color: ' . esc_attr($primary_color) . '; border-bottom: 2px solid #eee; padding-bottom: 8px; margin-top: 40px; }
            table { width: 100%; border-collapse: collapse; margin-top: 15px; }
            th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
            th { background-color: #f8f9fa; font-weight: bold; }
            .box { padding: 12px; margin-bottom: 10px; border-radius: 4px; }
            .box-warning { background-color: #fff3cd; border-left: 4px solid #ffc107; color: #856404; }
            .box-success { background-color: #d4edda; border-left: 4px solid #28a745; color: #155724; }
            .box-danger { background-color: #f8d7da; border-left: 4px solid #dc3545; color: #721c24; }
        </style>';
        
        $html .= '<body>';
        
        $logo_html = '';
        $agency_logo_url = get_option('fajaedeseo_agency_logo');
        $logo_url = '';
        
        if (!empty($agency_logo_url)) {
            $logo_url = $agency_logo_url;
        } else {
            // Fallback naar Site Icon
            $custom_logo_id = get_theme_mod('custom_logo');
            if ($custom_logo_id) {
                $logo_url = wp_get_attachment_image_url($custom_logo_id, 'full');
            }
        }

        if (!empty($logo_url)) {
            // Base64 encoding garandeert dat Dompdf de foto altijd kan renderen zonder CORS/loopback restricties
            $img_response = wp_remote_get($logo_url, ['timeout' => 5, 'sslverify' => false]);
            if (!is_wp_error($img_response) && wp_remote_retrieve_response_code($img_response) === 200) {
                $img_data = wp_remote_retrieve_body($img_response);
                $content_type = wp_remote_retrieve_header($img_response, 'content-type') ?: 'image/png';
                $logo_html = '<img src="data:' . esc_attr($content_type) . ';base64,' . base64_encode($img_data) . '" style="max-height: 80px; max-width: 250px;">';
            } else {
                $logo_html = '<img src="' . esc_url($logo_url) . '" style="max-height: 80px; max-width: 250px;">';
            }
        }

        if ($type === 'serp') {
            $html .= '<div class="cover">';
            if ($logo_html) $html .= '<div style="margin-bottom: 40px;">' . $logo_html . '</div>';
            $html .= '<h1>SERP Analysis Report</h1>';
            $html .= '<h2>Target Keyword: <strong>' . esc_html($keyword) . '</strong></h2>';
            $html .= '<p>Generated on ' . gmdate('F j, Y') . ' for ' . esc_html($site_name) . '</p>';
            $html .= '</div><div class="page-break"></div>';
            
            if (is_wp_error($serp_data)) {
                $html .= '<p style="color: red;">Error fetching SERP data.</p>';
            } else {
                $html .= '<h3 style="margin-top: 20px;">Top Results:</h3>';
                $html .= '<ol style="padding-left: 20px; line-height: 1.6;">';
                if (!empty($serp_data['results'])) {
                    foreach ($serp_data['results'] as $res) {
                        $html .= '<li style="margin-bottom: 15px;"><strong>' . esc_html($res['title']) . '</strong><br><a href="' . esc_url($res['url']) . '" style="color: ' . esc_attr($primary_color) . '; text-decoration: none; font-size: 12px;">' . esc_html($res['url']) . '</a>';
                        if (!empty($res['snippet'])) {
                            $html .= '<p style="font-size: 12px; color: #555; margin: 0; padding-top: 4px;">' . esc_html($res['snippet']) . '</p>';
                        }
                        $html .= '</li>';
                    }
                } else {
                    $html .= '<li>No results found.</li>';
                }
                $html .= '</ol>';
            }
        } else {
            // Cover Page
            $html .= '<div class="cover">';
            if ($logo_html) $html .= '<div style="margin-bottom: 40px;">' . $logo_html . '</div>';
            $html .= '<h1>Technical SEO Audit</h1>';
            $html .= '<h2>Comprehensive Site Analysis</h2>';
            $html .= '<p>Prepared for: <strong>' . esc_html($site_name) . '</strong><br>' . esc_url($site_url) . '<br><br>Date: ' . gmdate('F j, Y') . '</p>';
            $html .= '</div><div class="page-break"></div>';

            // Header on inner pages
            if ($logo_html) $html .= '<div class="header">' . $logo_html . '</div>';

            $html .= '<h2 class="section">1. Executive Summary</h2>';
            $html .= '<p>This report provides an overview of the technical SEO health of your website. We analyzed indexing, performance, and internal linking structures to identify opportunities for growth.</p>';
            
            $scoreColor = $overall_score >= 80 ? '#28a745' : ($overall_score >= 60 ? '#fd7e14' : '#dc3545');
            $speedColor = $page_speed >= 80 ? '#28a745' : ($page_speed >= 60 ? '#fd7e14' : '#dc3545');

            $html .= '<table style="width: 100%; border: none; margin-top: 15px;"><tr>';
            $html .= '<td style="width: 33%; text-align: center; border: 1px solid #ddd; background: #f8f9fa; padding: 15px;">';
            $html .= '<h3 style="margin-top:0; margin-bottom: 10px; color: #333;">SEO Health</h3>';
            $html .= '<div style="background: #e2e8f0; height: 12px; margin: 10px 0;"><div style="width: ' . $overall_score . '%; background: ' . $scoreColor . '; height: 100%;"></div></div>';
            $html .= '<strong style="color: ' . $scoreColor . '; font-size: 18px;">' . $overall_score . '/100</strong></td>';
            $html .= '<td style="width: 33%; text-align: center; border: 1px solid #ddd; background: #f8f9fa; padding: 15px;">';
            $html .= '<h3 style="margin-top:0; margin-bottom: 10px; color: #333;">Page Speed</h3>';
            $html .= '<div style="background: #e2e8f0; height: 12px; margin: 10px 0;"><div style="width: ' . $page_speed . '%; background: ' . $speedColor . '; height: 100%;"></div></div>';
            $html .= '<strong style="color: ' . $speedColor . '; font-size: 18px;">' . $page_speed . '/100</strong></td>';
            $html .= '<td style="width: 33%; text-align: center; border: 1px solid #ddd; background: #f8f9fa; padding: 15px;"><h3 style="margin-top:0; margin-bottom: 10px; color: #333;">Pages Indexed</h3><div style="font-size: 24px; margin: 5px 0; color: #888;">#</div><strong style="font-size: 18px; color: ' . esc_attr($primary_color) . ';">' . $pages_indexed . '</strong></td>';
            $html .= '</tr></table>';

            $html .= '<h2 class="section">2. Actionable Recommendations</h2>';
            if ($overall_score < 80) {
                $html .= '<div class="box box-warning"><strong>Recommendation:</strong> Improve meta titles, descriptions, and keyword density on your core pages to increase the overall health score.</div>';
            }
            if ($page_speed < 80) {
                $html .= '<div class="box box-danger"><strong>Critical Issue:</strong> Your page speed is sub-optimal. Enable advanced caching, lazy loading, and asset optimization to improve loading times.</div>';
            } else {
                $html .= '<div class="box box-success"><strong>Great:</strong> No critical performance or technical issues found on the surface level.</div>';
            }

            $html .= '<h2 class="section">3. Orphan Pages Detection</h2>';
            $html .= '<p>Orphan pages are URLs on your website that have <strong>zero internal links</strong> pointing to them. Search engines struggle to find these pages, and they receive no "link equity" from your other content.</p>';

            if (!empty($orphan_pages)) {
                $html .= '<div class="box box-warning">We found <strong>' . $orphan_pages_count . '</strong> orphan pages that need your attention.</div>';
                $html .= '<table><tr><th>Page Title</th><th>URL</th></tr>';
                foreach (array_slice($orphan_pages, 0, 15) as $page) {
                    $html .= '<tr><td>' . esc_html($page['title'] ?: 'No Title') . '</td><td><a href="' . esc_url($page['url']) . '" style="color:' . esc_attr($primary_color) . '; text-decoration:none;">' . esc_url($page['url']) . '</a></td></tr>';
                }
                $html .= '</table>';
                if ($orphan_pages_count > 15) {
                    $html .= '<p style="font-size: 12px; color: #666; text-align:center;">... and ' . ($orphan_pages_count - 15) . ' more.</p>';
                }
            } else {
                $html .= '<div class="box box-success"><strong>Excellent!</strong> No orphan pages were detected. Your internal linking structure is solid.</div>';
            }

            $html .= '<h2 class="section">4. Visitor Geography</h2>';
            $html .= '<p>Understanding where your visitors are coming from is crucial for local SEO and content targeting. Below is an overview of the top locations driving traffic to your website.</p>';

            if (!empty($geo_data['countries']) || !empty($geo_data['cities'])) {
                $html .= '<table style="width: 100%; border: none; margin-top: 10px;">';
                $html .= '<tr>';
                $html .= '<th style="width: 33%; background-color: #f8f9fa;">Top Countries</th>';
                $html .= '<th style="width: 33%; background-color: #f8f9fa;">Top Regions</th>';
                $html .= '<th style="width: 33%; background-color: #f8f9fa;">Top Cities</th>';
                $html .= '</tr>';
                $html .= '<tr>';
                
                $print_list = function($items) {
                    $list = '<ul style="margin: 0; padding-left: 15px; font-size: 13px;">';
                    if (!empty($items)) {
                        foreach (array_slice($items, 0, 5) as $item) {
                            $list .= '<li style="margin-bottom: 4px;">' . esc_html($item['name']) . ' <span style="color:#888;">(' . intval($item['count']) . ')</span></li>';
                        }
                    } else {
                        $list .= '<li style="color:#888; font-style: italic;">No data</li>';
                    }
                    $list .= '</ul>';
                    return $list;
                };

                $html .= '<td style="vertical-align: top; padding: 15px 10px;">' . $print_list($geo_data['countries']) . '</td>';
                $html .= '<td style="vertical-align: top; padding: 15px 10px;">' . $print_list($geo_data['regions']) . '</td>';
                $html .= '<td style="vertical-align: top; padding: 15px 10px;">' . $print_list($geo_data['cities']) . '</td>';

                $html .= '</tr>';
                $html .= '</table>';
            } else {
                $html .= '<div class="box box-warning"><strong>Data Gathering:</strong> No geographical data collected yet. Once visitors arrive on your website, their locations will be displayed here.</div>';
            }
            
            if (!empty($serp_data) && !empty($serp_data['results'])) {
                $html .= '<div class="page-break"></div>';
                $html .= '<h2 class="section">5. SERP Competitor Analysis</h2>';
                $html .= '<p>Top 5 Google results for the focus keyword: <strong>' . esc_html($serp_data['keyword']) . '</strong></p>';
                $html .= '<table><tr><th style="width: 30px;">Pos</th><th>Competitor</th></tr>';
                foreach ($serp_data['results'] as $index => $res) {
                    $rankColor = $index === 0 ? '#ffd700' : ($index === 1 ? '#c0c0c0' : ($index === 2 ? '#cd7f32' : $primary_color));
                    $html .= '<tr>';
                    $html .= '<td style="text-align: center;"><div style="background: ' . $rankColor . '; color: #fff; width: 24px; height: 24px; line-height: 24px; border-radius: 12px; font-weight: bold; margin: 0 auto;">' . ($index + 1) . '</div></td>';
                    $html .= '<td><strong style="color: ' . esc_attr($primary_color) . ';">' . esc_html($res['title']) . '</strong><br>';
                    $html .= '<a href="' . esc_url($res['url']) . '" style="color: #28a745; text-decoration: none; font-size: 12px;">' . esc_html($res['url']) . '</a>';
                    $html .= '<p style="font-size: 11px; color: #555; margin: 4px 0 0 0;">' . esc_html($res['snippet']) . '</p></td>';
                    $html .= '</tr>';
                }
                $html .= '</table>';
            }
        }

        $html .= '</body>';
        return $html;
    }

    public static function get_site_audit_data() {
        $is_pro = \FajaedeSEO_Pro_License::is_pro();

        $overall_score = $is_pro ? fajaedeseo_get_overall_seo_score() : 0;
        $pages_indexed = fajaedeseo_get_indexed_pages_count();
        $page_speed = $is_pro ? fajaedeseo_get_page_speed_score() : 0;
        $total_visitors = function_exists('fajaedeseo_get_total_visitors') ? fajaedeseo_get_total_visitors() : 0;
        $geo_data = fajaedeseo_get_geo_data();

        // Basic SEO Issues and Recommendations (can be expanded with more logic)
        $seo_issues = [];
        $recommendations = [];

        $has_advanced_links = \FajaedeSEO_Pro_License::has_feature('advanced_links');

        $orphan_pages = [];
        if ($has_advanced_links) {
            $orphan_pages = \FajaedeSEO_Module_Links::get_orphan_pages();
            if (count($orphan_pages) > 0) {
                $seo_issues[] = ['issue' => 'Orphan Pages (No incoming links)', 'count' => count($orphan_pages)];
                $recommendations[] = 'Add internal links pointing to your orphan pages to improve crawlability.';
            }
        }

        if ($overall_score < 70) {
            $seo_issues[] = ['issue' => 'Overall SEO Score Low', 'count' => 1];
            $recommendations[] = 'Improve meta titles and descriptions for better click-through rates.';
        }
        if ($pages_indexed < 10 && $pages_indexed > 0) { // Assuming a small site might have few pages
            $seo_issues[] = ['issue' => 'Low Number of Indexed Pages', 'count' => 1];
            $recommendations[] = 'Ensure all important pages are indexed by search engines.';
        }
        if ($page_speed < 60) {
            $seo_issues[] = ['issue' => 'Page Speed Low', 'count' => 1];
            $recommendations[] = 'Optimize images and leverage browser caching to improve page speed.';
        }

        $extra_tips = 'Keep creating high-quality content relevant to your audience.';

        return new \WP_REST_Response([
            'overall_score' => $overall_score,
            'pages_indexed' => $pages_indexed,
            'total_visitors' => $total_visitors,
            'page_speed' => $page_speed,
            'is_pro' => $is_pro,
            'has_advanced_links' => $has_advanced_links,
            'seo_issues' => $seo_issues,
            'recommendations' => $recommendations,
            'orphan_pages' => $orphan_pages,
            'geo_data' => $geo_data ? json_decode($geo_data, true) : ['countries' => [], 'regions' => [], 'cities' => []],
            'extra_tips' => $extra_tips
        ], 200);
    }

    public static function agency_status_callback(\WP_REST_Request $request) {
        $provided_key = sanitize_text_field((string)$request->get_param('license_key'));
        $saved_key = get_option('fajaedeseo_pro_license_key');
        $saved_hash = class_exists('\FajaedeSEO_Pro_License') ? \FajaedeSEO_Pro_License::hash_code($saved_key) : '';

        // Bypass voor de eigenaar/admin bij lokaal testen (voorkomt 401 als test-licenties kruisen)
        $is_local_admin = current_user_can('manage_options');

        // 1. Strenge Beveiligingscheck: Komt de sleutel of de master-hash overeen?
        if (!$is_local_admin && (empty($provided_key) || ($provided_key !== $saved_key && $provided_key !== $saved_hash))) {
            return new \WP_Error('unauthorized', 'Toegang geweigerd: Ongeldige of ontbrekende Agency licentiesleutel.', ['status' => 401]);
        }

        // 2. Beveiligingscheck: Is het daadwerkelijk een Agency abonnement?
        if (!class_exists('\FajaedeSEO_Pro_License') || \FajaedeSEO_Pro_License::get_plan_level() !== 'agency') {
            return new \WP_Error('forbidden', 'Toegang geweigerd: Deze website heeft geen actief Agency abonnement.', ['status' => 403]);
        }

        // 3. Verzamel alle waardevolle SEO en Health data
        $orphan_pages = \FajaedeSEO_Module_Links::get_orphan_pages();
        $geo_data = fajaedeseo_get_geo_data();

        // Geef de data veilig en gestructureerd terug aan het Master Dashboard
        $data = [
            'site_info' => [
                'name' => get_bloginfo('name'),
                'url' => home_url(),
                'wp_version' => get_bloginfo('version'),
            ],
            'seo_health' => [
                'overall_score' => fajaedeseo_get_overall_seo_score(),
                'pages_indexed' => fajaedeseo_get_indexed_pages_count(),
                'page_speed' => fajaedeseo_get_page_speed_score(),
            ],
            'traffic' => [
                'total_visitors' => function_exists('fajaedeseo_get_total_visitors') ? fajaedeseo_get_total_visitors() : 0,
                'geo_data' => $geo_data ? json_decode($geo_data, true) : ['countries' => [], 'regions' => [], 'cities' => []],
            ],
            'technical' => [
                'orphan_pages_count' => count($orphan_pages),
                'orphan_pages_sample' => array_slice($orphan_pages, 0, 15), // Beperk tot 15 in de API voor PDF
            ],
            'timestamp' => current_time('mysql')
        ];

        $response = new \WP_REST_Response($data, 200);
        $response->header('Access-Control-Allow-Origin', '*'); // Garandeert dat het Dashboard de data mag inladen
        
        return $response;
    }

    public static function agency_login_callback(\WP_REST_Request $request) {
        $provided_key = sanitize_text_field((string)$request->get_param('license_key'));
        $saved_key = get_option('fajaedeseo_pro_license_key');
        $saved_hash = class_exists('\FajaedeSEO_Pro_License') ? \FajaedeSEO_Pro_License::hash_code($saved_key) : '';

        // 1. Strenge beveiligingscheck
        if (empty($provided_key) || ($provided_key !== $saved_key && $provided_key !== $saved_hash)) {
            return new \WP_Error('unauthorized', 'Toegang geweigerd: Ongeldige licentiesleutel.', ['status' => 401]);
        }

        // 2. Alleen toegestaan voor actieve Agency accounts
        if (!class_exists('\FajaedeSEO_Pro_License') || \FajaedeSEO_Pro_License::get_plan_level() !== 'agency') {
            return new \WP_Error('forbidden', 'Toegang geweigerd: Geen actief Agency abonnement.', ['status' => 403]);
        }

        // 3. Genereer een eenmalig, veilig token dat 60 seconden geldig is
        $token = wp_generate_password(32, false);
        set_transient('fajaedeseo_quick_login_' . $token, true, 60);

        // 4. Stuur de magische login URL terug
        $login_url = add_query_arg(['fseo_login' => $token], home_url());

        return new \WP_REST_Response(['login_url' => $login_url], 200);
    }

    public static function generate_pdf_report_callback(\WP_REST_Request $request) {
        $type = $request->get_param('type') ?: 'audit';
        $site_name = get_bloginfo('name');
        $site_url = home_url();
        $keyword = sanitize_text_field($request->get_param('keyword'));
        
        if ($type === 'serp') {
            $serp_data = \FajaedeSEO_AI_Service_Manager::analyze_serp($keyword);
            $html = self::get_pdf_html($site_name, $site_url, 0, 0, 0, [], 0, [], $serp_data, 'serp', $keyword);
        } else {
            $overall_score = fajaedeseo_get_overall_seo_score();
            $pages_indexed = fajaedeseo_get_indexed_pages_count();
            $page_speed = fajaedeseo_get_page_speed_score();
            $orphan_pages = \FajaedeSEO_Pro_License::has_feature('advanced_links') ? \FajaedeSEO_Module_Links::get_orphan_pages() : [];
            $geo_data_json = get_transient('fajaedeseo_globe_visitor_data');
            $geo_data = $geo_data_json ? json_decode($geo_data_json, true) : ['countries' => [], 'regions' => [], 'cities' => []];
            
            $html = self::get_pdf_html($site_name, $site_url, $overall_score, $pages_indexed, $page_speed, $orphan_pages, count($orphan_pages), $geo_data, null, 'audit', '');
        }
        
        if (!class_exists('\FajaedeSEO_AI_Service_Manager')) {
            return new \WP_Error('ai_service_unavailable', 'AI Service Manager not found.', ['status' => 500]);
        }
        
        $pdf_base64 = \FajaedeSEO_AI_Service_Manager::generate_pdf($html);
        if (is_wp_error($pdf_base64)) return $pdf_base64;
        
        return new \WP_REST_Response(['pdf' => $pdf_base64], 200);
    }

    public static function agency_generate_pdf_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        if (empty($params['client_data'])) {
            return new \WP_Error('no_data', 'No client data provided.', ['status' => 400]);
        }
        $client_data = $params['client_data'];
        
        $site_name = sanitize_text_field($client_data['site_info']['name'] ?? 'Client Site');
        $site_url = esc_url_raw($client_data['site_info']['url'] ?? '');
        $overall_score = intval($client_data['seo_health']['overall_score'] ?? 0);
        $pages_indexed = intval($client_data['seo_health']['pages_indexed'] ?? 0);
        $page_speed = intval($client_data['seo_health']['page_speed'] ?? 0);
        $orphan_pages = isset($client_data['technical']['orphan_pages_sample']) && is_array($client_data['technical']['orphan_pages_sample']) ? $client_data['technical']['orphan_pages_sample'] : [];
        $orphan_pages_count = intval($client_data['technical']['orphan_pages_count'] ?? count($orphan_pages));
        $geo_data = isset($client_data['traffic']['geo_data']) && is_array($client_data['traffic']['geo_data']) ? $client_data['traffic']['geo_data'] : ['countries' => [], 'regions' => [], 'cities' => []];
        $serp_data = $client_data['serp_data'] ?? null;
        
        $html = self::get_pdf_html($site_name, $site_url, $overall_score, $pages_indexed, $page_speed, $orphan_pages, $orphan_pages_count, $geo_data, $serp_data, 'audit', '');
        
        if (!class_exists('\FajaedeSEO_AI_Service_Manager')) {
            return new \WP_Error('ai_service_unavailable', 'AI Service Manager not found.', ['status' => 500]);
        }
        
        $pdf_base64 = \FajaedeSEO_AI_Service_Manager::generate_pdf($html);
        if (is_wp_error($pdf_base64)) return $pdf_base64;
        
        return new \WP_REST_Response(['pdf' => $pdf_base64], 200);
    }
}

/**
 * Payment Module - Stub to resolve undefined type errors
 */
class FajaedeSEO_Module_Payment {
    public function init() {
        add_action('rest_api_init', [$this, 'register_routes']);
    }

    public function register_routes() {
        register_rest_route('fajaedeseo/v1', '/checkout', [
            'methods' => 'POST',
            'callback' => [$this, 'checkout_callback'],
            'permission_callback' => '__return_true'
        ]);

        // Generieke webhook-routering
        register_rest_route('fajaedeseo/v1', '/webhook/(?P<gateway_id>[a-zA-Z0-9_-]+)', [
            'methods' => 'POST',
            'callback' => [$this, 'webhook_callback'],
            'permission_callback' => '__return_true'
        ]);

        // Legacy webhook voor Lemon Squeezy (voor het geval de URL al is ingesteld)
        register_rest_route('fajaedeseo/v1', '/webhook', [
            'methods' => 'POST',
            'callback' => [$this, 'webhook_callback'],
            'permission_callback' => '__return_true'
        ]);

        register_rest_route('fajaedeseo/v1', '/accounting-export', [
            'methods' => 'GET',
            'callback' => [$this, 'accounting_export_callback'],
            'permission_callback' => fn() => current_user_can('manage_options') && class_exists('\FajaedeSEO_Pro_License') && \FajaedeSEO_Pro_License::get_plan_level() === 'agency'
        ]);

        register_rest_route('fajaedeseo/v1', '/verify-license', [
            'methods' => 'POST',
            'callback' => [$this, 'verify_license_callback'],
            'permission_callback' => '__return_true'
        ]);

        register_rest_route('fajaedeseo/v1', '/accounting-test-email', [
            'methods' => 'POST',
            'callback' => [$this, 'accounting_test_email_callback'],
            'permission_callback' => fn() => current_user_can('manage_options') && class_exists('\FajaedeSEO_Pro_License') && \FajaedeSEO_Pro_License::get_plan_level() === 'agency'
        ]);

        register_rest_route('fajaedeseo/v1', '/clean-test-data', [
            'methods' => 'POST',
            'callback' => [$this, 'clean_test_data_callback'],
            'permission_callback' => fn() => current_user_can('manage_options')
        ]);
    }

    public function checkout_callback(\WP_REST_Request $request) {
        $fajaedeseo = \FajaedeSEO::get_instance();
        $gateway = $fajaedeseo->get_service('payment_gateway');
        $referer = wp_get_referer() ?: home_url('/checkout/');

        if (!$gateway) {
            wp_safe_redirect(add_query_arg('error', 'gateway_not_configured', $referer));
            exit;
        }

        // Delegeer de checkout-logica naar de actieve gateway.
        return $gateway->process_checkout($request);
    }

    public function accounting_export_callback(\WP_REST_Request $request) {
        if (!class_exists('\FajaedeSEO_Pro_License')) {
            return new \WP_Error('no_license_class', 'License module missing.', ['status' => 500]);
        }

        $csv_content = fajaedeseo_generate_sales_csv_string();
        if (false === $csv_content) {
            return new \WP_Error('no_data', 'Geen financiële data gevonden.', ['status' => 404]);
        }

        return new \WP_REST_Response(['csv' => base64_encode($csv_content), 'filename' => 'fajaedeseo-accounting-export-' . gmdate('Y-m-d') . '.csv'], 200);
    }

    public function webhook_callback(\WP_REST_Request $request) {
        $gateway_id = $request->get_param('gateway_id');
        
        // Fallback voor de oude, niet-specifieke Lemon Squeezy webhook URL
        if (empty($gateway_id)) {
            $gateway_id = 'lemonsqueezy';
        }

        $fajaedeseo = \FajaedeSEO::get_instance();
        $gateway = $fajaedeseo->get_service('payment_gateway');

        // Controleer of de actieve gateway overeenkomt met de webhook-URL
        if ($gateway && $gateway->get_id() === $gateway_id) {
            return $gateway->handle_webhook($request);
        }

        return new \WP_REST_Response('Webhook received for inactive or unknown gateway.', 404);
    }

    public function verify_license_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        if (empty($params)) {
            $params = $request->get_body_params();
        }
        
        $license_code = isset($params['license_code']) ? sanitize_text_field($params['license_code']) : '';
        $site_url = isset($params['site_url']) ? esc_url_raw($params['site_url']) : '';

        if (empty($license_code) || empty($site_url)) {
            return new \WP_REST_Response(['success' => false, 'message' => 'Missing parameters.'], 400);
        }

        if (!class_exists('\FajaedeSEO_Pro_License')) {
            return new \WP_REST_Response(['success' => false, 'message' => 'License module missing.'], 500);
        }

        $result = \FajaedeSEO_Pro_License::master_server_redeem_license($license_code, $site_url);

        return new \WP_REST_Response($result, 200);
    }

    public function accounting_test_email_callback(\WP_REST_Request $request) {
        $accountant_email = get_option('fajaedeseo_accountant_email');
        if (empty($accountant_email)) {
            return new \WP_REST_Response(['success' => false, 'message' => 'Vul eerst een accountant e-mailadres in en sla de instellingen op.'], 400);
        }
        
        if (!function_exists('wp_tempnam')) {
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        }

        // Genereer een echt .csv bestand in plaats van een .tmp bestand om spamfilters te vermijden
        $upload_dir = wp_upload_dir();
        $csv_file = wp_normalize_path(trailingslashit($upload_dir['basedir']) . 'fseo_test_sales_' . time() . '.csv');
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
        $output = fopen($csv_file, 'w');
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
        fwrite($output, "\xEF\xBB\xBF"); // UTF-8 BOM voor Excel
        fputcsv($output, ['Factuurnummer', 'Transactie ID', 'Testbericht'], ';');
        fputcsv($output, ['TEST-123', 'TR-456', 'Succesvol ontvangen!'], ';');
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        fclose($output);
        
        $sent = wp_mail($accountant_email, 'FajaedeSEO - TEST Accounting Report', 'Dit is een test e-mail om te verifiëren dat jouw webhost e-mails met CSV-bijlagen correct verstuurt.', ['Content-Type: text/html; charset=UTF-8'], [$csv_file]);
        wp_delete_file($csv_file);
        
        if ($sent) {
            return new \WP_REST_Response(['success' => true, 'message' => 'Test e-mail is succesvol overgedragen aan de mailserver voor verzending naar ' . $accountant_email], 200);
        } else {
            return new \WP_REST_Response(['success' => false, 'message' => 'WordPress wp_mail() faalde. De webhost blokkeert de verzending.'], 500);
        }
    }

    public function clean_test_data_callback(\WP_REST_Request $request) {
        global $wpdb;
        $table = $wpdb->prefix . 'fajaedeseo_licenses';
        
        // Verwijder testverkopen (0.00 bedrag) uit de database
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $deleted = $wpdb->query("DELETE FROM `" . esc_sql($table) . "` WHERE amount = 0 OR amount IS NULL");
        
        // Wis de cache zodat de downloadknop direct de juiste info pakt
        wp_cache_delete('fajaedeseo_export_sales_data', 'fajaedeseo');
        
        return new \WP_REST_Response(['success' => true, 'message' => "Er zijn {$deleted} testverkopen (€0,00) definitief verwijderd uit de database!"], 200);
    }
}

/**
 * Internal Links & Orphan Pages Module
 */
class FajaedeSEO_Module_Links {
    public static function init() {
        add_action('rest_api_init', [__CLASS__, 'register_routes']);
    }

    public static function register_routes() {
        register_rest_route('fajaedeseo/v1', '/suggest-internal-links', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'suggest_internal_links_callback'],
            'permission_callback' => function(\WP_REST_Request $request) {
                if (!\FajaedeSEO_Pro_License::has_feature('advanced_links')) return false;
                $params = $request->get_json_params() ?: [];
                $post_id = isset($params['post_id']) ? intval($params['post_id']) : 0;
                return $post_id ? current_user_can('edit_post', $post_id) : current_user_can('edit_posts');
            }
        ]);
    }

    public static function suggest_internal_links_callback(\WP_REST_Request $request) {
        $params = $request->get_json_params();
        $content = isset($params['content']) ? wp_kses_post($params['content']) : '';
        $post_id = isset($params['post_id']) ? intval($params['post_id']) : 0;

        if (empty($content)) {
            return new \WP_Error('empty_content', 'Content is empty.', ['status' => 400]);
        }

        $posts = get_posts([
            'numberposts' => 25,
            'post_type' => ['post', 'page'],
            'post_status' => 'publish',
            'exclude' => [$post_id] // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude
        ]);

        $available_links = [];
        foreach ($posts as $p) {
            $available_links[] = "- Title: " . $p->post_title . " | URL: " . get_permalink($p->ID);
        }

        if (empty($available_links)) return ['suggestions' => []];

        $links_text = implode("\n", $available_links);
        $text_content = str_replace(['<', '>'], [' <', '> '], $content);
        $text_content = wp_strip_all_tags($text_content);
        $text_content = preg_replace('/\s+/', ' ', $text_content);
        $prompt = "You are an SEO expert assisting with internal link building. Analyze this text:\n\n" . mb_substr(trim($text_content), 0, 3000) . "\n\n";
        $prompt .= "Available destination pages:\n" . $links_text . "\n\n";
        $prompt .= "Suggest up to 4 highly relevant internal links. For each, give the exact 'anchor' text from the text block and the matching 'url'. Return ONLY a valid JSON array of objects with keys 'anchor' and 'url'. Example: [{\"anchor\": \"SEO practices\", \"url\": \"https://...\"}]";

        $result = \FajaedeSEO_AI_Service_Manager::call_openai($prompt, ['max_tokens' => 400, 'temperature' => 0.2]);
        if (is_wp_error($result)) return $result;

        $text = \FajaedeSEO_AI_Service_Manager::extract_text_from_response($result);
        if (preg_match('/\[\s*\{.*\}\s*\]/s', $text, $matches)) {
            $text = $matches[0];
        } else {
            $text = preg_replace('/```(?:json)?\s*/i', '', $text);
            $text = preg_replace('/```\s*$/', '', $text);
        }

        $suggestions = json_decode(trim($text), true);
        return ['suggestions' => is_array($suggestions) ? $suggestions : []];
    }

    public static function get_orphan_pages() {
        $cached = get_transient('fajaedeseo_orphan_pages');
        if (false !== $cached) return $cached;

        $all_posts = get_posts(['numberposts' => 200, 'post_type' => ['post', 'page'], 'post_status' => 'publish', 'fields' => 'ids']);
        $linked_urls = [];
        foreach ($all_posts as $pid) {
            preg_match_all('/<a\s[^>]*?href=["\']([^"\']+)["\']/i', get_post($pid)->post_content, $matches);
            if (!empty($matches[1])) foreach ($matches[1] as $url) $linked_urls[] = untrailingslashit((string)$url);
        }
        $linked_urls = array_unique($linked_urls);
        $orphan_pages = [];
        $site_url = untrailingslashit(home_url());

        foreach ($all_posts as $pid) {
            $permalink = untrailingslashit(get_permalink($pid));
            $relative_permalink = str_replace($site_url, '', $permalink);
            if (!in_array($permalink, $linked_urls) && !in_array($relative_permalink, $linked_urls)) {
                $orphan_pages[] = ['id' => $pid, 'title' => get_the_title($pid), 'url' => get_permalink($pid)];
            }
        }
        set_transient('fajaedeseo_orphan_pages', $orphan_pages, 12 * HOUR_IN_SECONDS);
        return $orphan_pages;
    }
}

/**
 * Tracking Module - Registers live visitors for the Globe and Stats
 */
class FajaedeSEO_Module_Tracking {
    public static function init() {
        add_action('rest_api_init', [__CLASS__, 'register_routes']);
    }

    public static function register_routes() {
        register_rest_route('fajaedeseo/v1', '/track-visitor', [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'track_visitor'],
            'permission_callback' => '__return_true'
        ]);

        register_rest_route('fajaedeseo/v1', '/full-geo-data', [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'get_full_geo_data'],
            'permission_callback' => fn() => current_user_can('manage_options')
        ]);
    }

    public static function get_full_geo_data(\WP_REST_Request $request) {
        global $wpdb;
        $table = $wpdb->prefix . 'fajaedeseo_visitors';
        
        // Filter op 7 of 30 dagen
        $days = (int) $request->get_param('days');
        if (!in_array($days, [7, 30], true)) {
            $days = 30; // Default naar 30
        }

        $date_limit = gmdate('Y-m-d H:i:s', strtotime("-{$days} days"));

        $get_all = function($col) use ($wpdb, $table, $date_limit) {
            $allowed = ['country', 'region', 'city'];
            if (!in_array($col, $allowed, true)) return [];
            
            // Gratis gebruikers zien alleen landen (Premium upsell in stand houden)
            if ($col !== 'country' && (!class_exists('\FajaedeSEO_Pro_License') || !\FajaedeSEO_Pro_License::is_pro())) return [];

            $safe_col = esc_sql($col);
            $query = sprintf("SELECT `%1\$s` as name, COUNT(*) as count FROM `%2\$s` WHERE `%1\$s` IS NOT NULL AND `%1\$s` != '' AND `timestamp` >= %%s GROUP BY `%1\$s` ORDER BY count DESC", $safe_col, $table);
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
            return $wpdb->get_results($wpdb->prepare($query, $date_limit), ARRAY_A) ?: [];
        };

        return new \WP_REST_Response([
            'countries' => $get_all('country'),
            'regions' => $get_all('region'),
            'cities' => $get_all('city'),
            'period' => $days
        ], 200);
    }

    public static function track_visitor(\WP_REST_Request $request) {
        $ip = function_exists('fajaedeseo_get_client_ip') ? fajaedeseo_get_client_ip() : '';
        
        // Lokale test IPs overslaan
        if (empty($ip) || in_array($ip, ['127.0.0.1', '::1'])) {
            return new \WP_REST_Response(['status' => 'ignored', 'reason' => 'localhost'], 200);
        }

        // Bot detectie: Negeer zoekmachines en crawlers om de statistieken zuiver te houden
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
        $bots = ['bot', 'crawl', 'spider', 'slurp', 'google', 'bing', 'yandex', 'baidu', 'facebook', 'openai', 'lighthouse', 'gtmetrix', 'pingdom', 'semrush', 'ahrefs', 'headless'];
        foreach ($bots as $bot) {
            if (strpos($user_agent, $bot) !== false) {
                return new \WP_REST_Response(['status' => 'ignored', 'reason' => 'bot_detected'], 200);
            }
        }

        // Privacy-vriendelijke GeoIP check (We slaan het IP NIET op in de DB!)
        $response = wp_remote_get("http://ip-api.com/json/{$ip}?fields=status,country,regionName,city,lat,lon");
        if (is_wp_error($response)) return new \WP_REST_Response('GeoIP failed', 200);

        $data = json_decode(wp_remote_retrieve_body($response), true);
        if (!isset($data['status']) || $data['status'] !== 'success') {
            return new \WP_REST_Response('GeoIP invalid', 200);
        }

        global $wpdb;
        $table = $wpdb->prefix . 'fajaedeseo_visitors';
        
        // Veilige fallbacks voor SQL strict mode
        $lat = isset($data['lat']) ? (float) $data['lat'] : 0.0;
        $lng = isset($data['lon']) ? (float) $data['lon'] : 0.0;
        $country = isset($data['country']) ? sanitize_text_field($data['country']) : '';
        $region = isset($data['regionName']) ? sanitize_text_field($data['regionName']) : '';
        $city = isset($data['city']) ? sanitize_text_field($data['city']) : '';

        // Normaliseer afwijkende landnamen om dubbele statistieken te voorkomen
        $country_aliases = [
            'The Netherlands'          => 'Netherlands',
            'United States of America' => 'United States',
            'Great Britain'            => 'United Kingdom',
            'Russian Federation'       => 'Russia'
        ];
        if (array_key_exists($country, $country_aliases)) {
            $country = $country_aliases[$country];
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $inserted = $wpdb->insert($table, [
            'lat' => $lat,
            'lng' => $lng,
            'country' => $country,
            'region' => $region,
            'city' => $city
        ], ['%f', '%f', '%s', '%s', '%s']);

        // Alleen tellen als de database insert succesvol was
        if ($inserted) {
            // Wis de 30-dagen cache zodat de statistieken live updaten
            delete_transient('fajaedeseo_total_visitors_30d');
            
            // Wis ook de geo data cache, deze wordt bij de volgende dashboard-weergave 
            // automatisch en gecentraliseerd opnieuw opgebouwd via fajaedeseo_get_geo_data()
            delete_transient('fajaedeseo_globe_visitor_data');
        }

        return new \WP_REST_Response(['status' => 'tracked'], 200);
    }
}