<?php

/**
 * AI Magic Article Generator
 *
 * Handles full article generation with one-click functionality
 *
 * @link       https://insya.com/ai-magic
 * @since      2.0.0
 *
 * @package    Ai_Magic
 * @subpackage Ai_Magic/admin
 */
class AI_Magic_Article_Generator
{
    /**
     * Constructor
     *
     * @since    2.0.0
     */
    public function __construct()
    {
        // Register AJAX handlers
        add_action('wp_ajax_ai_magic_generate_full_article', [$this, 'generate_full_article_callback']);
        add_action('wp_ajax_ai_magic_preview_full_article_prompt', [$this, 'preview_full_article_prompt_callback']);
        add_action('wp_ajax_ai_magic_generate_titles', [$this, 'generate_titles_callback']);
        add_action('wp_ajax_ai_magic_generate_outline', array($this, 'generate_outline_callback'));
        add_action('wp_ajax_ai_magic_create_tags', array($this, 'create_tags_callback'));
        add_action('wp_ajax_ai_magic_update_seo_meta', array($this, 'update_seo_meta_callback'));
    }

    /**
     * Get AI provider based on settings
     *
     * @since    2.0.0
     * @param    string    $provider_name    Provider name (openai, anthropic, google)
     * @return   object                      Provider instance
     */
    private function get_provider($provider_name = '')
    {
        if (empty($provider_name)) {
            // Use default provider
            $providers = array('openai', 'anthropic', 'google', 'openrouter');
            foreach ($providers as $p) {
                if (get_option("ai_magic_{$p}_active", 'no') === 'yes') {
                    $provider_name = $p;
                    break;
                }
            }
            if (empty($provider_name)) {
                $provider_name = 'openai';
            }
        }

        switch ($provider_name) {
            case 'anthropic':
                require_once plugin_dir_path(__FILE__) . 'class-ai-magic-anthropic.php';
                return new AI_Magic_Anthropic();
            case 'google':
                require_once plugin_dir_path(__FILE__) . 'class-ai-magic-google.php';
                return new AI_Magic_Google();
            default:
                require_once plugin_dir_path(__FILE__) . 'class-ai-magic-openai.php';
                return new AI_Magic_OpenAI();
        }
    }

    /**
     * Generate full article with all fields
     *
     * @since    2.0.0
     * @param    string    $topic      Article topic
     * @param    array     $options    Generation options
     * @return   array                 Article data
     */
    public function generate_full_article($topic, $options = array())
    {
        $defaults = array(
            'provider' => '',
            'word_count' => 1500,
            'language' => 'en',
            'style' => 'informative',
            'tone' => 'professional',
            'current_content' => '',
            'builder' => 'classic',
            'post_id' => 0
        );
        $options = wp_parse_args($options, $defaults);

        // Check if this is Elementor content - use widget-based processing for 100% structure preservation
        $is_elementor = ($options['builder'] === 'elementor' || $this->is_json($options['current_content']));

        if ($is_elementor && !empty($options['current_content']) && $this->is_json($options['current_content'])) {
            return $this->process_elementor_widget_based($topic, $options);
        }

        $provider = $this->get_provider($options['provider']);

        // Build enhanced prompt for full article with SEO fields
        $prompt = $this->build_full_article_prompt(
            $topic,
            $options['current_content'],
            $options['word_count'],
            $options['language'],
            $options['style'],
            $options['tone'],
            $options['builder']
        );

        // Generate content
        if (method_exists($provider, 'generateText')) {
            $response = $provider->generateText($prompt);
        } else {
            return array(
                'success' => false,
                'message' => __('Provider does not support text generation', 'ai_magic')
            );
        }

        if (isset($response['type']) && $response['type'] === 'success') {
            // Parse the generated content
            $article_data = $this->parse_full_article($response);
            return array(
                'success' => true,
                'data' => $article_data,
                'raw_response' => $response
            );
        }

        return array(
            'success' => false,
            'message' => isset($response['message']) ? $response['message'] : __('Failed to generate article', 'ai_magic')
        );
    }

    /**
     * Process Elementor content using widget-based approach (like Theme Wizard)
     * This ensures 100% structure preservation by processing each widget individually.
     *
     * @since    2.1.3
     * @param    string    $topic      Article topic
     * @param    array     $options    Generation options
     * @return   array                 Article data
     */
    private function process_elementor_widget_based($topic, $options)
    {
        // Load Elementor Processor
        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/processors/class-ai-magic-elementor-processor.php';
        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-ai-magic-layout-ai.php';

        $processor = new AI_Magic_Elementor_Processor();
        $ai = new AI_Magic_Layout_AI();

        // Decode Elementor JSON
        $elements = json_decode($options['current_content'], true);
        if (json_last_error() !== JSON_ERROR_NONE || empty($elements)) {
            return array(
                'success' => false,
                'message' => __('Invalid Elementor JSON data', 'ai_magic')
            );
        }

        // Extract all text nodes from Elementor structure
        $texts = array();
        $texts = $processor->extract_texts($elements, $texts);

        if (empty($texts)) {
            return array(
                'success' => false,
                'message' => __('No text content found in Elementor structure', 'ai_magic')
            );
        }

        // Build enhanced prompt with company info and topic
        $plugin_settings = get_option('ai_magic_settings', array());
        $company_info = isset($plugin_settings['base_prompt']) ? $plugin_settings['base_prompt'] : '';

        $enhanced_prompt = "[TOPIC/CONTEXT]\n" . $topic . "\n\n";
        $enhanced_prompt .= "[LANGUAGE]\n" . $this->get_language_name($options['language']) . "\n\n";
        $enhanced_prompt .= "[STYLE]\n" . $options['style'] . "\n\n";
        $enhanced_prompt .= "[TONE]\n" . $options['tone'] . "\n\n";

        if (!empty($company_info)) {
            $enhanced_prompt .= "[COMPANY INFORMATION]\n" . $company_info . "\n";
        }

        // Rewrite each text individually using Layout AI (same as Theme Wizard)
        $new_values = $ai->rewrite_texts($texts, $enhanced_prompt, $topic);

        if (empty($new_values)) {
            return array(
                'success' => false,
                'message' => __('Failed to rewrite content', 'ai_magic')
            );
        }

        // Apply new texts back to Elementor structure
        $processor->apply_new_texts($texts, $new_values);

        // Encode back to JSON
        $updated_json = wp_json_encode($elements);

        // Extract title from first heading widget or use topic as fallback
        $title = '';
        foreach ($texts as $idx => $text) {
            if ($text['type'] === 'title' && isset($new_values[$idx]) && !empty($new_values[$idx])) {
                $title = $new_values[$idx];
                break;
            }
        }
        // Fallback to topic if no title found
        if (empty($title)) {
            $title = $topic;
        }

        // Build plain text content for SEO/search
        $plain_content = '';
        foreach ($new_values as $idx => $value) {
            if (!empty($value)) {
                $type = isset($texts[$idx]['type']) ? $texts[$idx]['type'] : 'text';
                if (in_array($type, array('title'))) {
                    $plain_content .= "<h2>" . esc_html($value) . "</h2>\n";
                } else {
                    $plain_content .= "<p>" . wp_kses_post($value) . "</p>\n";
                }
            }
        }

        return array(
            'success' => true,
            'data' => array(
                'title' => $title,
                'slug' => '',
                'excerpt' => '',
                'focus_keyword' => '',
                'secondary_keywords' => '',
                'tags' => '',
                'meta_title' => $title,
                'meta_description' => '',
                'content' => $plain_content,
                'elementor_json' => $updated_json,
                'widget_based' => true // Flag to indicate widget-based processing was used
            ),
            'raw_response' => array('type' => 'success')
        );
    }

    /**
     * AJAX Callback: Preview Full Article Prompt
     */
    public function preview_full_article_prompt_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('You do not have permission to perform this action.', 'ai_magic'));
        }

        $topic = isset($_POST['topic']) ? sanitize_text_field(wp_unslash($_POST['topic'])) : '';
        $current_content = isset($_POST['current_content']) ? wp_kses_post(wp_unslash($_POST['current_content'])) : '';
        $word_count = isset($_POST['word_count']) ? intval($_POST['word_count']) : 1500;
        $language = isset($_POST['language']) ? sanitize_text_field(wp_unslash($_POST['language'])) : 'en';
        $style = isset($_POST['style']) ? sanitize_text_field(wp_unslash($_POST['style'])) : 'informative';
        $tone = isset($_POST['tone']) ? sanitize_text_field(wp_unslash($_POST['tone'])) : 'neutral';
        $builder = isset($_POST['builder']) ? sanitize_text_field(wp_unslash($_POST['builder'])) : 'classic';

        $prompt = $this->build_full_article_prompt($topic, $current_content, $word_count, $language, $style, $tone, $builder);

        wp_send_json_success(['prompt' => $prompt]);
    }

    /**
     * Build the prompt for full article generation
     */
    private function build_full_article_prompt($topic, $current_content, $word_count, $language, $style, $tone, $builder = 'classic')
    {
        $lang_name = $this->get_language_name($language);

        // Builder specific instructions
        $builder_instructions = "";
        $builder_extracted = null;

        // Try to extract structure from current content using generic router
        if (!empty($current_content)) {
            $builder_extracted = $this->extract_builder_content($current_content, $builder);

            if (!empty($builder_extracted['sections'])) {
                // Count section types
                $heading_count = 0;
                $content_count = 0;
                foreach ($builder_extracted['sections'] as $section) {
                    if ($section['type'] === 'heading')
                        $heading_count++;
                    else
                        $content_count++;
                }

                $builder_name = ucfirst($builder);
                $builder_instructions = "IMPORTANT: The user is using {$builder_name}. The existing page has {$heading_count} headings and {$content_count} content sections. You must generate content that matches this structure EXACTLY - the same number of headings and content blocks. Just provide clean, structured HTML content that follows the same section outline.";
            }
        }

        // Fallback instructions if no structure extracted
        if (empty($builder_instructions)) {
            switch ($builder) {
                case 'elementor':
                    $builder_instructions = "IMPORTANT: The user is using Elementor. Format the content to be easily pasted into Elementor text editor widgets. Use standard HTML tags (h2, h3, p, ul, ol). Do NOT generate Elementor JSON code directly. Just provide clean, structured HTML content.";
                    break;
                case 'gutenberg':
                    $builder_instructions = "IMPORTANT: The user is using Gutenberg. Use standard HTML tags. The content will be converted to blocks automatically.";
                    break;
                case 'divi':
                    $builder_instructions = "IMPORTANT: The user is using Divi. Provide clean HTML content without shortcodes.";
                    break;
                case 'wpbakery':
                    $builder_instructions = "IMPORTANT: The user is using WPBakery. Provide clean HTML content without shortcodes.";
                    break;
                default:
                    $builder_instructions = "Use clean, semantic HTML5 structure.";
            }
        }

        $prompt = "ROLE: You are an elite senior content strategist and SEO specialist with 15+ years of experience. You are tasked with creating a masterpiece article that ranks #1 on Google.
        IMPORTANT: The output MUST be in the '{$language}' language. All titles, headings, content, and meta descriptions must be written in '{$language}'.
        
        {$builder_instructions}

INPUT TOPIC/CONTEXT: \"{$topic}\"";

        // Check if we have an existing HTML structure to preserve
        // Also check for Elementor JSON format
        $is_elementor_json = !empty($current_content) && $this->is_json($current_content);

        $has_existing_structure = !empty($current_content) && (
            $is_elementor_json ||
            strpos($current_content, '<section') !== false ||
            strpos($current_content, '<div') !== false ||
            strpos($current_content, '<h2') !== false ||
            strpos($current_content, '<h3') !== false ||
            strpos($current_content, '<h5') !== false ||
            strpos($current_content, '<img') !== false
        );

        // For Elementor JSON, extract text content to create a template
        $template_content = $current_content;
        $elementor_extracted = null;

        if ($is_elementor_json && $has_existing_structure) {
            $elementor_extracted = $this->extract_elementor_text_content($current_content);

            // Build a structured template from extracted sections
            if (!empty($elementor_extracted['sections'])) {
                $template_parts = [];
                foreach ($elementor_extracted['sections'] as $idx => $section) {
                    $type = $section['type'];
                    $text = $section['text'];

                    if ($type === 'heading') {
                        $template_parts[] = "[HEADING #{$idx}]\n{$text}";
                    } elseif ($type === 'text-editor') {
                        $template_parts[] = "[TEXT BLOCK #{$idx}]\n{$text}";
                    } elseif ($type === 'post-content') {
                        $template_parts[] = "[CONTENT #{$idx}]\n{$text}";
                    } else {
                        $template_parts[] = "[ELEMENT #{$idx} (type: {$type})]\n{$text}";
                    }
                }
                $template_content = implode("\n\n", $template_parts);
            }
        }

        if ($has_existing_structure) {
            // MODE: PRESERVE EXISTING STRUCTURE

            if ($is_elementor_json && !empty($elementor_extracted['sections'])) {
                // ELEMENTOR SECTION-BASED MODE
                $prompt .= "

=================================================================
CRITICAL: ELEMENTOR SECTION REWRITE MODE
=================================================================

You are rewriting content for an Elementor page. Each section below is a separate widget.
ONLY replace the text content. Keep the EXACT same number of sections in your output.

EXISTING SECTIONS:
{$template_content}

RULES:
1. Return EXACTLY the same number of sections as shown above
2. Keep the section markers: [HEADING #X], [TEXT BLOCK #X], etc.
3. ONLY replace the text AFTER each section marker
4. Keep the language: {$language}
5. Match the style to: {$style}
6. Match the tone to: {$tone}
7. Do NOT add HTML tags or formatting
8. Do NOT add JSON, markdown, or code blocks

OUTPUT FORMAT (EXACTLY):
[HEADING #0]
Your new heading text here

[TEXT BLOCK #1]
Your new paragraph text here

[HEADING #2]
Another heading text

Return ONLY the sections with new text, nothing else.";

            } else {
                // STANDARD HTML STRUCTURE PRESERVATION MODE
                $prompt .= "

=================================================================
CRITICAL: STRUCTURE PRESERVATION MODE - EXACT HTML REPRODUCTION
=================================================================

You are a precise HTML text replacer. Your ONLY job is to translate/rewrite the TEXT CONTENT inside HTML elements. You must NOT modify the HTML structure in any way.

EXISTING HTML TO MODIFY:
[TEMPLATE_START]
{$template_content}
[TEMPLATE_END]

ABSOLUTE RULES - VIOLATION MEANS COMPLETE FAILURE:

1. COPY ALL HTML TAGS EXACTLY AS THEY APPEAR:
   - <section>, <div>, <article>, <header>, <footer>
   - <p>, <span>, <strong>, <em>, <b>, <i>
   - <h1>, <h2>, <h3>, <h4>, <h5>, <h6>
   - <img>, <a>, <figure>, <figcaption>
   - <input>, <form>, <button>, <textarea>, <select>
   - <ul>, <ol>, <li>, <table>, <tr>, <td>, <th>
   - ALL other HTML tags must be preserved exactly

2. PRESERVE ALL ATTRIBUTES EXACTLY:
   - src, href, alt, title, class, id, style
   - data-*, type, value, disabled, readonly
   - width, height, border, colspan, rowspan
   - ALL attributes with their exact values

3. DO NOT:
   - Remove ANY HTML tag (even if it seems unnecessary)
   - Add ANY new HTML tag
   - Use markdown formatting (**bold**, *italic*, etc.) - use HTML only
   - Change whitespace or indentation significantly
   - Add ```code blocks``` or JSON wrappers
   - Change self-closing tag style (keep /> or > as original)

4. ONLY REPLACE:
   - Human-readable text between tags
   - Text that is meant to be displayed to users

EXAMPLE:
INPUT:
<section>
  <input type=\"text\" value=\"0\" data-rel=\"78\" disabled />
  <h3>78%</h3>
  <p>Project done</p>
</section>

OUTPUT:
<section>
  <input type=\"text\" value=\"0\" data-rel=\"78\" disabled />
  <h3>78%</h3>
  <p>Tamamlanan Proje</p>
</section>

Notice: <input> tag is EXACTLY the same. Only <p> text changed.

FINAL OUTPUT: Return the COMPLETE modified HTML. Nothing else.";
            }

        } else {
            // MODE: GENERATE NEW CONTENT
            $prompt .= "

ARTICLE CONFIGURATION:
- Target Language: {$language} (Native-level proficiency required)
- Writing Style: {$style}
- Tone of Voice: {$tone}
- Target Length: ~{$word_count} words

CONTENT STRATEGY:
1.  **Hook**: Start with a magnetic opening that addresses the reader's pain point or curiosity immediately.
2.  **Value First**: Deliver actionable insights, not just fluff. Every paragraph must add value.
3.  **Engagement**: Use rhetorical questions, analogies, and a conversational flow to keep the reader glued.
4.  **Authority**: Cite data points, trends, or expert consensus where applicable (simulate this if needed for the context).
5.  **SEO Mastery**: Naturally weave in the focus keyword and semantic variations without keyword stuffing.

OUTPUT FORMAT (Strictly follow this JSON structure):
Return ONLY a valid JSON object. Do not include any markdown formatting (like ```json ... ```) or extra text.
{
    \"title\": \"Click-worthy, SEO-optimized H1 Title\",
    \"slug\": \"short-seo-friendly-url-slug\",
    \"excerpt\": \"Compelling meta description/excerpt under 160 chars\",
    \"focus_keyword\": \"Main target keyword\",
    \"secondary_keywords\": \"3-5 LSI/Semantic keywords, comma separated\",
    \"tags\": \"5-8 relevant tags, comma separated\",
    \"meta_title\": \"SEO Title Tag (max 60 chars)\",
    \"meta_description\": \"SEO Meta Description (max 160 chars)\",
    \"content\": \"The Full Article HTML Body\"
}

HTML STRUCTURE GUIDELINES (For 'content' field):
- Use proper hierarchy: <h1> for title, <h2> for main sections, <h3> for subsections.
- Use <ul> and <ol> for lists to break up text.
- Use <strong> for emphasis on key takeaways.
- Use <blockquote> for key quotes or highlights.
- Ensure the content is ready to publish.";
        }

        return $prompt;
    }

    /**
     * Helper to get language name from code
     *
     * @since 2.0.0
     * @param string $code Language code (e.g., 'en', 'es')
     * @return string Language name (e.g., 'English', 'Spanish')
     */
    private function get_language_name($code)
    {
        $languages = array(
            'en' => 'English',
            'es' => 'Spanish',
            'fr' => 'French',
            'de' => 'German',
            'it' => 'Italian',
            'pt' => 'Portuguese',
            'nl' => 'Dutch',
            'ja' => 'Japanese',
            'zh' => 'Chinese',
            'ar' => 'Arabic',
            'ru' => 'Russian',
            'ko' => 'Korean',
            'hi' => 'Hindi',
            'bn' => 'Bengali',
            'pa' => 'Punjabi',
            'ur' => 'Urdu',
            'id' => 'Indonesian',
            'tr' => 'Turkish',
            'pl' => 'Polish',
            'sv' => 'Swedish',
            'da' => 'Danish',
            'fi' => 'Finnish',
            'no' => 'Norwegian',
            'el' => 'Greek',
            'cs' => 'Czech',
            'hu' => 'Hungarian',
            'ro' => 'Romanian',
            'th' => 'Thai',
            'vi' => 'Vietnamese',
            'he' => 'Hebrew',
            'uk' => 'Ukrainian',
            'fa' => 'Persian',
            'ms' => 'Malay',
            'tl' => 'Tagalog',
            'bg' => 'Bulgarian',
            'hr' => 'Croatian',
            'sr' => 'Serbian',
            'sk' => 'Slovak',
            'sl' => 'Slovenian',
            'et' => 'Estonian',
            'lv' => 'Latvian',
            'lt' => 'Lithuanian',
            'ga' => 'Irish',
            'cy' => 'Welsh',
            'mt' => 'Maltese',
            'is' => 'Icelandic',
            'sq' => 'Albanian',
            'mk' => 'Macedonian',
            'bs' => 'Bosnian',
            'ka' => 'Georgian',
            'hy' => 'Armenian',
            'az' => 'Azerbaijani',
            'uz' => 'Uzbek',
            'kk' => 'Kazakh',
            'ky' => 'Kyrgyz',
            'tg' => 'Tajik',
            'tk' => 'Turkmen',
            'mn' => 'Mongolian',
            'lo' => 'Lao',
            'km' => 'Khmer',
            'my' => 'Burmese',
            'si' => 'Sinhala',
            'am' => 'Amharic',
            'so' => 'Somali',
            'sw' => 'Swahili',
            'yo' => 'Yoruba',
            'zu' => 'Zulu',
            'ha' => 'Hausa',
            'ig' => 'Igbo',
            'mg' => 'Malagasy',
            'ne' => 'Nepali',
            'si' => 'Sinhala',
            'ur' => 'Urdu',
            'ps' => 'Pashto',
            'sd' => 'Sindhi',
            'ug' => 'Uyghur',
            'fy' => 'Frisian',
            'gd' => 'Scottish Gaelic',
            'gv' => 'Manx',
            'kw' => 'Cornish',
            'mi' => 'Maori',
            'sm' => 'Samoan',
            'to' => 'Tongan',
            'fj' => 'Fijian',
            'ht' => 'Haitian Creole',
            'lb' => 'Luxembourgish',
            'oc' => 'Occitan',
            'rm' => 'Romansh',
            'wa' => 'Walloon',
            'yi' => 'Yiddish',
            'eo' => 'Esperanto',
            'ia' => 'Interlingua',
            'ie' => 'Interlingue',
            'io' => 'Ido',
            'li' => 'Limburgish',
            'na' => 'Nauru',
            'nv' => 'Navajo',
            'oj' => 'Ojibwe',
            'om' => 'Oromo',
            'pi' => 'Pali',
            'qu' => 'Quechua',
            'sa' => 'Sanskrit',
            'sc' => 'Sardinian',
            'sn' => 'Shona',
            'st' => 'Southern Sotho',
            'su' => 'Sundanese',
            'ty' => 'Tahitian',
            'vo' => 'Volapük',
            'wo' => 'Wolof',
            'xh' => 'Xhosa',
            'zu' => 'Zulu',
        );
        return isset($languages[$code]) ? $languages[$code] : $code;
    }

    /**
     * Parse generated content into structured article data
     *
     * @since    2.0.0
     * @param    array     $response    Provider response
     * @return   array                  Parsed article data
     */
    private function parse_full_article($response)
    {
        $text = '';
        if (isset($response['data']['post_content'])) {
            $text = $response['data']['post_content'];
        } elseif (isset($response['data']['text'])) {
            $text = $response['data']['text'];
        } elseif (isset($response['raw_text'])) {
            $text = $response['raw_text'];
        }

        // Clean up any leading/trailing whitespace
        $text = trim($text);

        // Strip markdown code blocks (```html ... ```, ```json ... ```, ``` ... ```)
        if (preg_match('/^```(?:html|json|xml)?\s*\n?([\s\S]*?)\n?```$/s', $text, $code_match)) {
            $text = trim($code_match[1]);
        }

        // Check if this is Elementor section-based format [HEADING #X], [TEXT BLOCK #X], etc.
        $is_elementor_sections = preg_match('/\[(HEADING|TEXT BLOCK|CONTENT|ELEMENT)\s*#\d+/', $text);

        if ($is_elementor_sections) {
            // Parse Elementor section format
            $sections = [];

            // Match all sections with their content
            preg_match_all('/\[(HEADING|TEXT BLOCK|CONTENT|ELEMENT)[^\]]*#(\d+)[^\]]*\]\s*([\s\S]*?)(?=\[(?:HEADING|TEXT BLOCK|CONTENT|ELEMENT)|$)/i', $text, $matches, PREG_SET_ORDER);

            foreach ($matches as $match) {
                $type = strtolower(str_replace(' ', '-', $match[1]));
                $index = intval($match[2]);
                $content = trim($match[3]);

                $sections[$index] = [
                    'type' => $type,
                    'content' => $content
                ];
            }

            // Store sections in parsed data for injection
            $parsed_data = array(
                'title' => '',
                'slug' => '',
                'excerpt' => '',
                'focus_keyword' => '',
                'secondary_keywords' => '',
                'tags' => '',
                'meta_title' => '',
                'meta_description' => '',
                'content' => $text,
                'elementor_sections' => $sections
            );

            // Extract title from first heading section
            foreach ($sections as $section) {
                if ($section['type'] === 'heading') {
                    $parsed_data['title'] = $section['content'];
                    $parsed_data['meta_title'] = $section['content'];
                    break;
                }
            }

            return $parsed_data;
        }

        // Check if this is pure HTML (structure preservation mode)
        // AI returns plain HTML when preserving existing structure
        $is_pure_html = (
            strpos($text, '<section') === 0 ||
            strpos($text, '<div') === 0 ||
            (strpos($text, '<') === 0 && strpos($text, '{') === false)
        );

        if ($is_pure_html) {
            // Post-process: Clean up AI output
            $text = $this->clean_preserved_html($text);

            // Structure preservation mode - AI returned modified HTML template
            $parsed_data = array(
                'title' => '',
                'slug' => '',
                'excerpt' => '',
                'focus_keyword' => '',
                'secondary_keywords' => '',
                'tags' => '',
                'meta_title' => '',
                'meta_description' => '',
                'content' => $text
            );

            // Try to extract title from first heading
            if (preg_match('/<h[12][^>]*>([^<]+)<\/h[12]>/i', $text, $title_match)) {
                $parsed_data['title'] = strip_tags($title_match[1]);
                $parsed_data['meta_title'] = $parsed_data['title'];
            }

            return $parsed_data;
        }

        // Try to parse as JSON directly
        $json_data = json_decode($text, true);

        // If not valid JSON, try to extract JSON from markdown code blocks
        if (json_last_error() !== JSON_ERROR_NONE) {
            if (preg_match('/```json\s*([\s\S]*?)\s*```/', $text, $matches)) {
                $json_data = json_decode($matches[1], true);
            } elseif (preg_match('/\{[\s\S]*\}/', $text, $matches)) {
                // Try to find the first JSON object in the text
                $json_data = json_decode($matches[0], true);
            }
        }

        $parsed_data = array();
        $fields = array(
            'title',
            'slug',
            'excerpt',
            'focus_keyword',
            'secondary_keywords',
            'tags',
            'meta_title',
            'meta_description',
            'content'
        );

        if (json_last_error() === JSON_ERROR_NONE && is_array($json_data)) {
            // Successfully parsed JSON
            foreach ($fields as $field) {
                $parsed_data[$field] = isset($json_data[$field]) ? $json_data[$field] : "";
            }
        } else {
            // Fallback to Regex parsing (for backward compatibility or if AI fails JSON)
            foreach ($fields as $field) {
                preg_match("/#\s*{$field}\s*:\s*(.*?)(?=#\s*\w+\s*:|$)/si", $text, $matches);
                $parsed_data[$field] = isset($matches[1]) ? trim($matches[1]) : "";
            }
        }

        // Fallback: If content is empty, use the whole text (if it wasn't JSON)
        if (empty($parsed_data['content']) && empty($parsed_data['title']) && json_last_error() !== JSON_ERROR_NONE) {
            $parsed_data['content'] = $text;
        }

        return $parsed_data;
    }

    /**
     * Insert article data to editor
     *
     * @since    2.0.0
     * @param    int       $post_id              Post ID
     * @param    array     $article_data         Article data
     * @param    string    $original_elementor   Original Elementor JSON for structure preservation
     * @return   bool                            Success status
     */
    public function insert_to_editor($post_id, $article_data, $original_elementor = '')
    {
        if (empty($post_id) || empty($article_data)) {
            return false;
        }

        $update_data = array('ID' => $post_id);

        if (!empty($article_data['title'])) {
            $update_data['post_title'] = wp_strip_all_tags($article_data['title']);
        }

        if (!empty($article_data['content'])) {
            $content = $article_data['content'];
            $is_elementor = false;

            // Check if this was processed via widget-based approach (already has updated JSON)
            if (!empty($article_data['widget_based']) && !empty($article_data['elementor_json'])) {
                // Widget-based processing already built the Elementor JSON - just save it
                $this->save_elementor_data($post_id, $article_data['elementor_json']);
                $is_elementor = true;

                // Also update post_content with clean HTML for search/SEO
                $update_data['post_content'] = wp_kses_post($content);
            }
            // Check if we have original Elementor data for structure-preserving injection
            elseif (!empty($original_elementor) && $this->is_json($original_elementor)) {
                // Extract sections from original Elementor structure
                $extracted = $this->extract_elementor_text_content($original_elementor);

                if (!empty($extracted['sections'])) {
                    // Check if AI returned section-based format (elementor_sections in parsed data)
                    if (!empty($article_data['elementor_sections'])) {
                        // Direct section-based injection - more accurate
                        $ai_sections = $article_data['elementor_sections'];
                        $matched_content = [];

                        foreach ($extracted['sections'] as $idx => $orig_section) {
                            if (isset($ai_sections[$idx])) {
                                $matched_content[$idx] = $ai_sections[$idx]['content'];
                            } else {
                                // Keep original text if no match
                                $matched_content[$idx] = $orig_section['text'];
                            }
                        }
                    } else {
                        // Fallback: Match AI-generated HTML content to the extracted sections
                        $matched_content = $this->match_content_to_sections($content, $extracted['sections']);
                    }

                    // Inject matched content back into original structure
                    $updated_elementor = $this->inject_content_to_elementor(
                        $original_elementor,
                        $matched_content,
                        $extracted['map']
                    );

                    // Save the updated Elementor data using Document API (safe method)
                    $this->save_elementor_data($post_id, $updated_elementor);
                    $is_elementor = true;

                    // Also update post_content with clean HTML for search/SEO
                    $update_data['post_content'] = wp_kses_post($content);
                }
            }

            // If AI returned Elementor JSON directly (rare case)
            if (!$is_elementor) {
                $is_json = $this->is_json($content);

                if ($is_json) {
                    $decoded = json_decode($content, true);
                    if (is_array($decoded)) {
                        // It's JSON, likely Elementor data - save via Document API
                        $this->save_elementor_data($post_id, $content);
                        $is_elementor = true;
                    }
                }
            }

            if (!$is_elementor) {
                // Standard content
                $update_data['post_content'] = wp_kses_post($content);
            }
        }

        if (!empty($article_data['excerpt'])) {
            $update_data['post_excerpt'] = sanitize_textarea_field($article_data['excerpt']);
        }

        if (!empty($article_data['slug'])) {
            $update_data['post_name'] = sanitize_title($article_data['slug']);
        }

        // Update the post
        $result = wp_update_post($update_data, true);

        if (is_wp_error($result)) {
            return false;
        }

        // Handle tags
        if (!empty($article_data['tags'])) {
            $tags = array_map('trim', explode(',', $article_data['tags']));
            wp_set_post_tags($post_id, $tags);
        }

        // Handle SEO meta
        if (!empty($article_data['focus_keyword']) || !empty($article_data['meta_description']) || !empty($article_data['meta_title'])) {
            require_once plugin_dir_path(__FILE__) . 'class-ai-magic-seo.php';

            if (!empty($article_data['meta_description'])) {
                Ai_Magic_SEO::update_meta_description($post_id, $article_data['meta_description']);
            }

            if (!empty($article_data['focus_keyword'])) {
                Ai_Magic_SEO::update_focus_keyword($post_id, $article_data['focus_keyword']);
            }

            if (!empty($article_data['meta_title'])) {
                Ai_Magic_SEO::update_seo_title($post_id, $article_data['meta_title']);
            }
        }

        return true;
    }

    /**
     * AJAX: Generate full article
     *
     * @since    2.0.0
     */
    public function generate_full_article_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('Insufficient permissions', 'ai_magic'));
        }

        $topic = isset($_POST['topic']) ? sanitize_text_field(wp_unslash($_POST['topic'])) : '';
        if (empty($topic)) {
            wp_send_json_error(__('Topic is required', 'ai_magic'));
        }

        $provider_name = isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : '';
        $word_count = isset($_POST['word_count']) ? intval($_POST['word_count']) : 1500;
        $language = isset($_POST['language']) ? sanitize_text_field(wp_unslash($_POST['language'])) : 'en';
        $style = isset($_POST['style']) ? sanitize_text_field(wp_unslash($_POST['style'])) : 'informative';
        $tone = isset($_POST['tone']) ? sanitize_text_field(wp_unslash($_POST['tone'])) : 'neutral';
        $current_content = isset($_POST['current_content']) ? wp_kses_post(wp_unslash($_POST['current_content'])) : '';
        $builder = isset($_POST['builder']) ? sanitize_text_field(wp_unslash($_POST['builder'])) : 'classic';
        $auto_insert = isset($_POST['auto_insert']) && $_POST['auto_insert'] === '1';
        $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
        $original_elementor_data = ''; // Store original Elementor data for structure preservation

        // If current_content is empty and we have a post_id, try to fetch content from DB (including Elementor)
        if (empty($current_content) && $post_id > 0) {
            $elementor_data = get_post_meta($post_id, '_elementor_data', true);
            if (!empty($elementor_data)) {
                $current_content = $elementor_data;
                $original_elementor_data = $elementor_data; // Keep original for injection
                $builder = 'elementor';
            } else {
                // Fallback to post_content
                $post = get_post($post_id);
                if ($post) {
                    $current_content = $post->post_content;
                }
            }
        } elseif ($builder === 'elementor' && !empty($current_content) && $this->is_json($current_content)) {
            // If Elementor content was sent from frontend
            $original_elementor_data = $current_content;
        }

        // Save topic to history
        $user_id = get_current_user_id();
        $topic_history = get_user_meta($user_id, 'ai_magic_topic_history', true);
        if (!is_array($topic_history)) {
            $topic_history = array();
        }

        // Add new topic if not exists
        if (!in_array($topic, $topic_history)) {
            array_unshift($topic_history, $topic); // Add to beginning
            $topic_history = array_slice($topic_history, 0, 50); // Keep last 50
            update_user_meta($user_id, 'ai_magic_topic_history', $topic_history);
        }

        $options = array(
            'provider' => $provider_name,
            'word_count' => $word_count,
            'language' => $language,
            'style' => $style,
            'tone' => $tone,
            'current_content' => $current_content,
            'builder' => $builder
        );

        $result = $this->generate_full_article($topic, $options);

        if ($result['success']) {
            $response_data = $result['data'];
            $response_data['auto_inserted'] = false;

            // Handle Auto Insert
            if ($auto_insert && $post_id > 0) {
                // Pass original Elementor data for structure-preserving injection
                $inserted = $this->insert_to_editor($post_id, $response_data, $original_elementor_data);
                if ($inserted) {
                    // With structure preservation, Elementor auto-insert now works properly
                    $response_data['auto_inserted'] = true;
                }
            }

            wp_send_json_success($response_data);
        } else {
            wp_send_json_error($result['message']);
        }
    }

    /**
     * AJAX: Generate title suggestions
     *
     * @since    2.0.0
     */
    public function generate_titles_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('Insufficient permissions', 'ai_magic'));
        }

        $topic = isset($_POST['topic']) ? sanitize_text_field(wp_unslash($_POST['topic'])) : '';
        if (empty($topic)) {
            wp_send_json_error(__('Topic is required', 'ai_magic'));
        }

        $provider = $this->get_provider(isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : '');

        // Get content if provided
        $content = isset($_POST['content']) ? wp_kses_post(wp_unslash($_POST['content'])) : '';

        // Get language from request or fallback to WordPress locale
        $language = isset($_POST['language']) ? sanitize_text_field(wp_unslash($_POST['language'])) : 'en';
        $language_names = array(
            'en' => 'English',
            'tr' => 'Turkish',
            'de' => 'German',
            'fr' => 'French',
            'es' => 'Spanish',
            'it' => 'Italian',
            'pt' => 'Portuguese',
            'nl' => 'Dutch',
            'ru' => 'Russian',
            'ar' => 'Arabic',
            'zh' => 'Chinese',
            'ja' => 'Japanese',
            'ko' => 'Korean'
        );
        $lang_name = isset($language_names[$language]) ? $language_names[$language] : 'English';

        // Extract content snippet for context
        $content_snippet = '';
        if (!empty($content)) {
            $content_snippet = wp_strip_all_tags($content);
            $content_snippet = substr($content_snippet, 0, 500);
        }

        if (!empty($content_snippet)) {
            $prompt = "You are a headline writer. Write in {$lang_name} ONLY.

Generate 10 SHORT article titles based on this content:
\"{$content_snippet}\"

CRITICAL RULES:
- LANGUAGE: Write ALL titles in {$lang_name} language only!
- LENGTH: Each title MUST be under 50 characters (5-8 words max)
- FORMAT: One title per line, numbered 1-10
- STYLE: Short, punchy newspaper headlines
- NO explanations, NO descriptions, NO colons

Output ONLY the numbered titles, nothing else.";
        } else {
            $prompt = "You are a headline writer. Write in {$lang_name} ONLY.

Generate 10 SHORT article titles for: \"{$topic}\"

CRITICAL RULES:
- LANGUAGE: Write ALL titles in {$lang_name} language only!
- LENGTH: Each title MUST be under 50 characters (5-8 words max)
- FORMAT: One title per line, numbered 1-10
- STYLE: Short, punchy newspaper headlines
- NO explanations, NO descriptions, NO colons

Output ONLY the numbered titles, nothing else.";
        }

        $response = $provider->generateText($prompt);

        if (isset($response['type']) && $response['type'] === 'success') {
            $text = isset($response['data']['text']) ? $response['data']['text'] : '';
            if (isset($response['data']['post_content'])) {
                $text = $response['data']['post_content'];
            }

            // Parse titles from response
            $lines = preg_split('/\r\n|\r|\n/', $text);
            $titles = array();
            foreach ($lines as $line) {
                $line = trim($line);
                // Remove numbering
                $line = preg_replace('/^[\d]+[.\)]\s*/', '', $line);
                if (!empty($line)) {
                    $titles[] = $line;
                }
            }

            wp_send_json_success(array('titles' => array_slice($titles, 0, 10)));
        } else {
            wp_send_json_error(isset($response['message']) ? $response['message'] : __('Failed to generate titles', 'ai_magic'));
        }
    }

    /**
     * AJAX: Generate content outline
     *
     * @since    2.0.0
     */
    public function generate_outline_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('Insufficient permissions', 'ai_magic'));
        }

        $topic = isset($_POST['topic']) ? sanitize_text_field(wp_unslash($_POST['topic'])) : '';
        if (empty($topic)) {
            wp_send_json_error(__('Topic is required', 'ai_magic'));
        }

        $provider = $this->get_provider(isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : '');

        $prompt = "Create a detailed content outline for an article about: \"{$topic}\".

Include:
- Introduction hook idea
- 4-6 main sections with H2 headings
- 2-3 subsections (H3) under each main section
- Key points to cover in each section
- Suggested examples or case studies
- Conclusion with call-to-action idea

Format the outline clearly with proper hierarchy.";

        $response = $provider->generateText($prompt);

        if (isset($response['type']) && $response['type'] === 'success') {
            $text = isset($response['data']['text']) ? $response['data']['text'] : '';
            if (isset($response['data']['post_content'])) {
                $text = $response['data']['post_content'];
            }
            wp_send_json_success(array('outline' => $text));
        } else {
            wp_send_json_error(isset($response['message']) ? $response['message'] : __('Failed to generate outline', 'ai_magic'));
        }
    }

    /**
     * AJAX: Create tags and return IDs for Gutenberg
     *
     * @since    2.0.0
     */
    public function create_tags_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('Insufficient permissions', 'ai_magic'));
        }

        $tags_string = isset($_POST['tags']) ? sanitize_text_field(wp_unslash($_POST['tags'])) : '';
        if (empty($tags_string)) {
            wp_send_json_error(__('Tags are required', 'ai_magic'));
        }

        $tags = array_map('trim', explode(',', $tags_string));
        $tag_ids = array();
        $tag_objects = array();

        foreach ($tags as $tag_name) {
            if (empty($tag_name)) {
                continue;
            }

            // Check if tag exists
            $term = get_term_by('name', $tag_name, 'post_tag');

            if ($term) {
                $tag_ids[] = $term->term_id;
                $tag_objects[] = array(
                    'id' => $term->term_id,
                    'name' => $term->name,
                    'slug' => $term->slug
                );
            } else {
                // Create new tag
                $new_term = wp_insert_term($tag_name, 'post_tag');
                if (!is_wp_error($new_term)) {
                    $tag_ids[] = $new_term['term_id'];
                    $tag_objects[] = array(
                        'id' => $new_term['term_id'],
                        'name' => $tag_name,
                        'slug' => sanitize_title($tag_name)
                    );
                }
            }
        }

        wp_send_json_success(array(
            'tag_ids' => $tag_ids,
            'tags' => $tag_objects
        ));
    }

    /**
     * AJAX: Update SEO meta fields
     *
     * @since    2.0.0
     */
    public function update_seo_meta_callback()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ai_magic_metabox_nonce')) {
            wp_send_json_error(__('Security check failed', 'ai_magic'));
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(__('Insufficient permissions', 'ai_magic'));
        }

        $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
        if ($post_id <= 0) {
            wp_send_json_error(__('Invalid post ID', 'ai_magic'));
        }

        require_once plugin_dir_path(__FILE__) . 'class-ai-magic-seo.php';

        $updated = array();

        if (isset($_POST['meta_title'])) {
            Ai_Magic_SEO::update_seo_title($post_id, sanitize_text_field(wp_unslash($_POST['meta_title'])));
            $updated[] = 'meta_title';
        }

        if (isset($_POST['meta_description'])) {
            Ai_Magic_SEO::update_meta_description($post_id, sanitize_textarea_field(wp_unslash($_POST['meta_description'])));
            $updated[] = 'meta_description';
        }

        if (isset($_POST['focus_keyword'])) {
            Ai_Magic_SEO::update_focus_keyword($post_id, sanitize_text_field(wp_unslash($_POST['focus_keyword'])));
            $updated[] = 'focus_keyword';
        }

        wp_send_json_success(array(
            'message' => __('SEO meta updated successfully', 'ai_magic'),
            'updated_fields' => $updated
        ));
    }

    /**
     * Extract text content from Elementor JSON structure
     * Returns an array of text sections with their widget IDs for later injection
     *
     * @since 2.1.0
     * @param string|array $elementor_data Elementor JSON data
     * @return array Array with 'sections' (text content) and 'map' (widget ID to index mapping)
     */
    private function extract_elementor_text_content($elementor_data)
    {
        if (is_string($elementor_data)) {
            $elementor_data = json_decode($elementor_data, true);
        }

        if (!is_array($elementor_data)) {
            return ['sections' => [], 'map' => [], 'outline' => ''];
        }

        $sections = [];
        $map = [];
        $outline = '';
        $section_index = 0;

        $this->extract_text_recursive($elementor_data, $sections, $map, $outline, $section_index);

        return [
            'sections' => $sections,
            'map' => $map,
            'outline' => $outline
        ];
    }

    /**
     * Recursively extract text from Elementor elements
     *
     * @since 2.1.0
     */
    private function extract_text_recursive(&$elements, &$sections, &$map, &$outline, &$section_index)
    {
        if (!is_array($elements)) {
            return;
        }

        foreach ($elements as &$element) {
            $widget_type = isset($element['widgetType']) ? $element['widgetType'] : '';
            $element_id = isset($element['id']) ? $element['id'] : '';
            $settings = isset($element['settings']) ? $element['settings'] : [];

            // Extract text from text-based widgets
            if ($widget_type === 'heading' && !empty($settings['title'])) {
                $text = wp_strip_all_tags($settings['title']);
                $sections[] = [
                    'type' => 'heading',
                    'id' => $element_id,
                    'text' => $text,
                    'index' => $section_index
                ];
                $map[$element_id] = $section_index;
                $outline .= "## " . $text . "\n";
                $section_index++;
            } elseif ($widget_type === 'text-editor' && !empty($settings['editor'])) {
                $text = wp_strip_all_tags($settings['editor']);
                // Only include if there's meaningful content
                if (strlen(trim($text)) > 10) {
                    $sections[] = [
                        'type' => 'text-editor',
                        'id' => $element_id,
                        'text' => $text,
                        'index' => $section_index
                    ];
                    $map[$element_id] = $section_index;
                    // Truncate for outline
                    $outline .= substr($text, 0, 200) . (strlen($text) > 200 ? '...' : '') . "\n\n";
                    $section_index++;
                }
            } elseif ($widget_type === 'theme-post-content') {
                // This widget pulls content from post_content, mark it for content injection
                $sections[] = [
                    'type' => 'post-content',
                    'id' => $element_id,
                    'text' => '[MAIN CONTENT AREA]',
                    'index' => $section_index
                ];
                $map[$element_id] = $section_index;
                $outline .= "[Main Article Content Here]\n\n";
                $section_index++;
            }

            // Recursively process child elements
            if (!empty($element['elements']) && is_array($element['elements'])) {
                $this->extract_text_recursive($element['elements'], $sections, $map, $outline, $section_index);
            }
        }
    }

    /**
     * Inject generated content back into Elementor structure
     *
     * @since 2.1.0
     * @param string|array $elementor_data Original Elementor JSON
     * @param array $generated_sections Generated content sections
     * @param array $map Widget ID to section index mapping
     * @return string Updated Elementor JSON
     */
    private function inject_content_to_elementor($elementor_data, $generated_sections, $map)
    {
        if (is_string($elementor_data)) {
            $elementor_data = json_decode($elementor_data, true);
        }

        if (!is_array($elementor_data)) {
            return json_encode([]);
        }

        $this->inject_content_recursive($elementor_data, $generated_sections, $map);

        return wp_json_encode($elementor_data);
    }

    /**
     * Recursively inject content into Elementor elements
     *
     * @since 2.1.0
     */
    private function inject_content_recursive(&$elements, $generated_sections, $map)
    {
        if (!is_array($elements)) {
            return;
        }

        foreach ($elements as &$element) {
            $widget_type = isset($element['widgetType']) ? $element['widgetType'] : '';
            $element_id = isset($element['id']) ? $element['id'] : '';

            // Check if this element has a mapped content section
            if (isset($map[$element_id]) && isset($generated_sections[$map[$element_id]])) {
                $new_content = $generated_sections[$map[$element_id]];

                if ($widget_type === 'heading') {
                    $element['settings']['title'] = sanitize_text_field($new_content);
                } elseif ($widget_type === 'text-editor') {
                    // Allow HTML in text-editor widgets
                    $element['settings']['editor'] = wp_kses_post($new_content);
                }
            }

            // Recursively process child elements
            if (!empty($element['elements']) && is_array($element['elements'])) {
                $this->inject_content_recursive($element['elements'], $generated_sections, $map);
            }
        }
    }

    /**
     * Parse AI-generated content and match it to Elementor sections
     *
     * @since 2.1.0
     * @param string $generated_content AI-generated HTML content
     * @param array $original_sections Original extracted sections
     * @return array Matched content array indexed same as original sections
     */
    private function match_content_to_sections($generated_content, $original_sections)
    {
        $matched = [];

        // Parse the generated content
        $dom = new \DOMDocument();
        @$dom->loadHTML('<?xml encoding="UTF-8">' . $generated_content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        $headings = [];
        $paragraphs = [];

        // Extract headings
        foreach (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as $tag) {
            $elements = $dom->getElementsByTagName($tag);
            foreach ($elements as $el) {
                $headings[] = $el->textContent;
            }
        }

        // Extract paragraphs and other content
        $p_elements = $dom->getElementsByTagName('p');
        foreach ($p_elements as $el) {
            $paragraphs[] = $dom->saveHTML($el);
        }

        // Match content to original sections
        $heading_index = 0;
        $para_index = 0;

        foreach ($original_sections as $index => $section) {
            if ($section['type'] === 'heading') {
                if (isset($headings[$heading_index])) {
                    $matched[$index] = $headings[$heading_index];
                    $heading_index++;
                } else {
                    // Keep original if no match
                    $matched[$index] = $section['text'];
                }
            } elseif ($section['type'] === 'text-editor') {
                // Collect paragraphs until next heading or end
                $content_parts = [];
                while ($para_index < count($paragraphs)) {
                    $content_parts[] = $paragraphs[$para_index];
                    $para_index++;

                    // Stop if we've collected enough for this section
                    if (count($content_parts) >= 3) {
                        break;
                    }
                }
                $matched[$index] = implode("\n", $content_parts);
            } elseif ($section['type'] === 'post-content') {
                // For post-content widgets, use the full generated content
                $matched[$index] = $generated_content;
            }
        }

        return $matched;
    }

    // ========================================
    // Multi-Builder Template Preservation
    // ========================================

    /**
     * Generic router: Extract text content from any builder format
     *
     * @since 2.1.0
     * @param string $content The content to extract from
     * @param string $builder The builder type
     * @return array Array with 'sections', 'map', and 'outline'
     */
    private function extract_builder_content($content, $builder)
    {
        switch ($builder) {
            case 'elementor':
                return $this->extract_elementor_text_content($content);
            case 'gutenberg':
                return $this->extract_gutenberg_text_content($content);
            case 'divi':
                return $this->extract_shortcode_text_content($content, 'et_pb_');
            case 'wpbakery':
                return $this->extract_shortcode_text_content($content, 'vc_');
            case 'classic':
            default:
                return $this->extract_html_text_content($content);
        }
    }

    /**
     * Generic router: Inject content back into builder format
     *
     * @since 2.1.0
     * @param string $original_content Original content
     * @param array $generated_sections Generated content sections
     * @param string $builder Builder type
     * @param array $map Section mapping
     * @return string Updated content
     */
    private function inject_builder_content($original_content, $generated_sections, $builder, $map)
    {
        switch ($builder) {
            case 'elementor':
                return $this->inject_content_to_elementor($original_content, $generated_sections, $map);
            case 'gutenberg':
                return $this->inject_gutenberg_content($original_content, $generated_sections, $map);
            case 'divi':
            case 'wpbakery':
                return $this->inject_shortcode_content($original_content, $generated_sections, $map);
            case 'classic':
            default:
                return $this->inject_html_content($original_content, $generated_sections, $map);
        }
    }

    /**
     * Extract text content from Gutenberg blocks
     *
     * @since 2.1.0
     * @param string $content Gutenberg block content
     * @return array
     */
    private function extract_gutenberg_text_content($content)
    {
        $sections = [];
        $map = [];
        $outline = '';
        $section_index = 0;

        // Match Gutenberg blocks: <!-- wp:blockname {"attrs"} --> content <!-- /wp:blockname -->
        $block_pattern = '/<!-- wp:(\w+(?:\/\w+)?)(?: ({[^}]*}))? -->\s*(.*?)\s*<!-- \/wp:\1 -->/s';

        if (preg_match_all($block_pattern, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
            foreach ($matches as $match) {
                $block_type = $match[1][0];
                $block_content = isset($match[4]) ? $match[4][0] : (isset($match[3]) ? $match[3][0] : '');
                $position = $match[0][1];
                $full_match = $match[0][0];

                // Handle heading blocks
                if ($block_type === 'heading') {
                    $text = strip_tags($block_content);
                    if (!empty(trim($text))) {
                        $sections[] = [
                            'type' => 'heading',
                            'text' => $text,
                            'index' => $section_index,
                            'position' => $position,
                            'original' => $full_match
                        ];
                        $map[$position] = $section_index;
                        $outline .= "## " . $text . "\n";
                        $section_index++;
                    }
                }
                // Handle paragraph blocks
                elseif ($block_type === 'paragraph') {
                    $text = strip_tags($block_content);
                    if (strlen(trim($text)) > 10) {
                        $sections[] = [
                            'type' => 'paragraph',
                            'text' => $text,
                            'index' => $section_index,
                            'position' => $position,
                            'original' => $full_match
                        ];
                        $map[$position] = $section_index;
                        $outline .= "- " . substr($text, 0, 80) . "...\n";
                        $section_index++;
                    }
                }
                // Handle list blocks
                elseif ($block_type === 'list') {
                    $text = strip_tags($block_content);
                    if (!empty(trim($text))) {
                        $sections[] = [
                            'type' => 'list',
                            'text' => $text,
                            'index' => $section_index,
                            'position' => $position,
                            'original' => $full_match
                        ];
                        $map[$position] = $section_index;
                        $section_index++;
                    }
                }
            }
        }

        return [
            'sections' => $sections,
            'map' => $map,
            'outline' => $outline
        ];
    }

    /**
     * Extract text content from shortcode-based builders (Divi, WPBakery)
     *
     * @since 2.1.0
     * @param string $content Shortcode content
     * @param string $prefix Shortcode prefix (et_pb_ for Divi, vc_ for WPBakery)
     * @return array
     */
    private function extract_shortcode_text_content($content, $prefix = 'et_pb_')
    {
        $sections = [];
        $map = [];
        $outline = '';
        $section_index = 0;

        // Text content shortcodes for each builder
        $text_shortcodes = [];
        if ($prefix === 'et_pb_') {
            // Divi shortcodes
            $text_shortcodes = ['text', 'blurb', 'cta', 'toggle', 'accordion'];
        } else {
            // WPBakery shortcodes
            $text_shortcodes = ['column_text', 'custom_heading', 'text_block'];
        }

        foreach ($text_shortcodes as $shortcode) {
            $tag = $prefix . $shortcode;
            // Match shortcode with attributes and content
            $pattern = '/\[' . preg_quote($tag, '/') . '([^\]]*)\](.*?)\[\/' . preg_quote($tag, '/') . '\]/s';

            if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
                foreach ($matches as $match) {
                    $attrs = $match[1][0];
                    $inner_content = $match[2][0];
                    $position = $match[0][1];
                    $full_match = $match[0][0];

                    $text = strip_tags($inner_content);
                    if (strlen(trim($text)) > 5) {
                        $type = (strpos($shortcode, 'heading') !== false || strpos($shortcode, 'cta') !== false)
                            ? 'heading' : 'text';

                        $sections[] = [
                            'type' => $type,
                            'text' => $text,
                            'index' => $section_index,
                            'position' => $position,
                            'original' => $full_match,
                            'shortcode' => $tag,
                            'attrs' => $attrs
                        ];
                        $map[$position] = $section_index;

                        if ($type === 'heading') {
                            $outline .= "## " . $text . "\n";
                        } else {
                            $outline .= "- " . substr($text, 0, 80) . "...\n";
                        }
                        $section_index++;
                    }
                }
            }
        }

        // Sort by position
        usort($sections, function ($a, $b) {
            return $a['position'] - $b['position'];
        });

        return [
            'sections' => $sections,
            'map' => $map,
            'outline' => $outline
        ];
    }

    /**
     * Extract text content from classic HTML
     *
     * @since 2.1.0
     * @param string $content HTML content
     * @return array
     */
    private function extract_html_text_content($content)
    {
        $sections = [];
        $map = [];
        $outline = '';
        $section_index = 0;

        $dom = new \DOMDocument();
        @$dom->loadHTML('<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        // Extract headings
        foreach (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as $tag) {
            $elements = $dom->getElementsByTagName($tag);
            foreach ($elements as $el) {
                $text = $el->textContent;
                if (!empty(trim($text))) {
                    $sections[] = [
                        'type' => 'heading',
                        'tag' => $tag,
                        'text' => $text,
                        'index' => $section_index
                    ];
                    $map['h_' . $section_index] = $section_index;
                    $outline .= "## " . $text . "\n";
                    $section_index++;
                }
            }
        }

        // Extract paragraphs
        $paragraphs = $dom->getElementsByTagName('p');
        foreach ($paragraphs as $el) {
            $text = $el->textContent;
            if (strlen(trim($text)) > 10) {
                $sections[] = [
                    'type' => 'paragraph',
                    'text' => $text,
                    'index' => $section_index
                ];
                $map['p_' . $section_index] = $section_index;
                $outline .= "- " . substr($text, 0, 80) . "...\n";
                $section_index++;
            }
        }

        return [
            'sections' => $sections,
            'map' => $map,
            'outline' => $outline
        ];
    }

    /**
     * Inject content back into Gutenberg blocks
     *
     * @since 2.1.0
     */
    private function inject_gutenberg_content($original_content, $generated_sections, $map)
    {
        $result = $original_content;

        // Sort by position descending to replace from end to start
        $positions = array_keys($map);
        rsort($positions);

        // Match and get generated content
        $matched = $this->match_content_to_sections($generated_sections, []);

        // For now, return the generated content directly for Gutenberg
        // as block structure is simpler to regenerate
        return $generated_sections;
    }

    /**
     * Inject content back into shortcode-based content
     *
     * @since 2.1.0
     */
    private function inject_shortcode_content($original_content, $generated_sections, $map)
    {
        $result = $original_content;

        // Parse generated content
        $matched = $this->match_content_to_sections(
            is_array($generated_sections) ? implode("\n", $generated_sections) : $generated_sections,
            []
        );

        // For shortcodes, we replace the inner content while preserving attributes
        // This is a simplified approach - full implementation would track each shortcode
        return is_array($generated_sections) ? implode("\n", $generated_sections) : $generated_sections;
    }

    /**
     * Inject content back into HTML while preserving structure
     *
     * @since 2.1.0
     */
    private function inject_html_content($original_content, $generated_sections, $map)
    {
        // For classic HTML, we use the generated content directly
        // as there's no complex structure to preserve
        return is_array($generated_sections) ? implode("\n", $generated_sections) : $generated_sections;
    }

    /**
     * Check if string is valid JSON
     *
     * @since 2.0.0
     * @param string $string
     * @return bool
     */
    private function is_json($string)
    {
        if (!is_string($string)) {
            return false;
        }
        json_decode($string);
        return (json_last_error() == JSON_ERROR_NONE);
    }

    /**
     * Clean up AI-generated HTML to match original structure better
     * Removes unwanted <p> tags and normalizes whitespace
     *
     * @since 2.0.0
     * @param string $html AI-generated HTML
     * @return string Cleaned HTML
     */
    private function clean_preserved_html($html)
    {
        // Step 1: Remove <p> tags but keep their content
        // This handles cases where AI wraps plain text in <p> tags
        $html = preg_replace('/<p[^>]*>(.*?)<\/p>/is', '$1', $html);

        // Step 2: Normalize excessive whitespace
        // Replace multiple spaces/tabs with single space
        $html = preg_replace('/[ \t]+/', ' ', $html);

        // Step 3: Normalize line breaks
        // Remove excessive blank lines (more than 1 consecutive)
        $html = preg_replace('/\n\s*\n\s*\n/', "\n\n", $html);

        // Step 4: Clean up whitespace around tags
        // Remove leading whitespace on lines
        $lines = explode("\n", $html);
        $cleaned_lines = array();
        foreach ($lines as $line) {
            $trimmed = trim($line);
            if (!empty($trimmed)) {
                $cleaned_lines[] = $trimmed;
            }
        }
        $html = implode("\n", $cleaned_lines);

        // Step 5: Restore proper formatting for section tags
        // Add newline after closing section tags
        $html = preg_replace('/<\/section>/', "</section>\n", $html);
        $html = preg_replace('/<section>/', "<section>", $html);

        // Step 6: Trim the result
        $html = trim($html);

        return $html;
    }

    /**
     * Save Elementor data using Document API when available.
     *
     * This is the safe method to save Elementor content per Elementor guidelines.
     * Falls back to direct meta update when Elementor is not active.
     *
     * @since 2.1.3
     * @param int          $post_id        Post ID
     * @param string|array $elementor_data Elementor JSON data (string or decoded array)
     * @return bool Success status
     */
    private function save_elementor_data($post_id, $elementor_data)
    {
        // Decode if string
        if (is_string($elementor_data)) {
            $elements = json_decode($elementor_data, true);
        } else {
            $elements = $elementor_data;
        }

        if (!is_array($elements)) {
            return false;
        }

        // Try Elementor Document API first (official, safe method)
        if (class_exists('\Elementor\Plugin') && isset(\Elementor\Plugin::$instance->documents) && \Elementor\Plugin::$instance->documents) {
            $document = \Elementor\Plugin::$instance->documents->get($post_id);
            if ($document) {
                // Document save handles:
                // - JSON encoding
                // - Meta update with proper slashing
                // - post_content sync
                // - Cache clearing
                $document->save(array('elements' => $elements));
                return true;
            }
        }

        // Fallback: Direct meta update (when Elementor not active)
        $json = is_string($elementor_data) ? $elementor_data : wp_json_encode($elements);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return false;
        }

        // wp_slash is required for update_post_meta with JSON
        update_post_meta($post_id, '_elementor_data', wp_slash($json));
        update_post_meta($post_id, '_elementor_edit_mode', 'builder');

        return true;
    }
}

// Initialize the article generator
new AI_Magic_Article_Generator();
