<?php

declare(strict_types=1);
if (!defined('WPINC')) {
    die();
}

/**
 * Content Plan Posts Service
 *
 * Handles operations related to WordPress posts and pages
 */
class CPEPAI_Posts_Service
{
    /**
     * WordPress database instance
     */
    private $wpdb;

    /**
     * The logging service instance.
     *
     * @var CPEPAI_Logging_Service|null
     */
    private ?CPEPAI_Logging_Service $logging_service = null;

    /**
     * Get the logging service instance.
     *
     * @return CPEPAI_Logging_Service
     */
    private function get_logging_service(): CPEPAI_Logging_Service
    {
        if ($this->logging_service === null) {
            $database_service = new CPEPAI_Database_Service();
            $this->logging_service = new CPEPAI_Logging_Service($database_service);
        }
        return $this->logging_service;
    }

    /**
     * Constructor
     */
    public function __construct()
    {
        global $wpdb;
        $this->wpdb = $wpdb;
    }

    /**
     * Get existing posts as array of objects
     *
     * @param array $fields Fields to return (default: ['id', 'title'])
     * @param array $post_types Post types to include (default: ['post', 'page'])
     * @param array $args Additional WP_Query arguments
     * @return array Array of post objects
     */
    public function get_posts(
        array $fields = ['id', 'title'],
        array $post_types = ['post', 'page'],
        array $args = []
    ): array {
        // Validate fields parameter
        $valid_fields = ['id', 'title', 'slug', 'content', 'excerpt', 'status', 'date', 'modified', 'author'];
        $fields = array_intersect($fields, $valid_fields);

        // If no valid fields, default to id and title
        if (empty($fields)) {
            $fields = ['id', 'title'];
        }

        // Default query arguments
        $default_args = [
            'post_type' => $post_types,
            'post_status' => 'publish',
            'posts_per_page' => -1, // Get all posts
            'orderby' => 'date',
            'order' => 'DESC'
        ];

        // Merge with provided arguments
        $query_args = array_merge($default_args, $args);

        // Execute query
        $query = new WP_Query($query_args);

        if (!$query->have_posts()) {
            return [];
        }

        $posts = [];

        while ($query->have_posts()) {
            $query->the_post();
            $post_data = [];

            // Get current post object
            $post = get_post();

            // Build response array based on requested fields
            foreach ($fields as $field) {
                switch ($field) {
                    case 'id':
                        $post_data['id'] = $post->ID;
                        break;
                    case 'title':
                        $post_data['title'] = $post->post_title;
                        break;
                    case 'slug':
                        $post_data['slug'] = $post->post_name;
                        break;
                    case 'content':
                        $post_data['content'] = $post->post_content;
                        break;
                    case 'excerpt':
                        $post_data['excerpt'] = $post->post_excerpt;
                        break;
                    case 'status':
                        $post_data['status'] = $post->post_status;
                        break;
                    case 'date':
                        $post_data['date'] = $post->post_date;
                        break;
                    case 'modified':
                        $post_data['modified'] = $post->post_modified;
                        break;
                    case 'author':
                        $post_data['author'] = $post->post_author;
                        break;
                }
            }

            // Add post type for reference
            $post_data['post_type'] = $post->post_type;

            $posts[] = $post_data;
        }

        // Reset post data
        wp_reset_postdata();

        return $posts;
    }

    /**
     * Get posts by specific criteria
     *
     * @param array $fields Fields to return
     * @param array $post_types Post types to include
     * @param string $search Search term
     * @param int $limit Number of posts to return
     * @param string $orderby Order by field
     * @param string $order Order direction
     * @return array Array of post objects
     */
    public function get_posts_by_criteria(
        array $fields = ['id', 'title'],
        array $post_types = ['post', 'page'],
        ?string $search = '',
        int $limit = -1,
        string $orderby = 'date',
        string $order = 'DESC'
    ): array {
        $args = [];

        // Ensure search is a string
        $search = $search ?? '';

        if (!empty($search)) {
            $args['s'] = sanitize_text_field($search);
        }

        if ($limit > 0) {
            $args['posts_per_page'] = $limit;
        }

        $args['orderby'] = sanitize_text_field($orderby);
        $args['order'] = in_array(strtoupper($order), ['ASC', 'DESC']) ? strtoupper($order) : 'DESC';

        return $this->get_posts($fields, $post_types, $args);
    }

    /**
     * Get a single post by ID
     *
     * @param int $post_id Post ID
     * @param array $fields Fields to return
     * @return array|null Post object or null if not found
     */
    public function get_post_by_id(int $post_id, array $fields = ['id', 'title']): ?array
    {
        $posts = $this->get_posts($fields, [], ['p' => $post_id]);

        return !empty($posts) ? $posts[0] : null;
    }

    /**
     * Get posts by author
     *
     * @param int $author_id Author ID
     * @param array $fields Fields to return
     * @param array $post_types Post types to include
     * @return array Array of post objects
     */
    public function get_posts_by_author(
        int $author_id,
        array $fields = ['id', 'title'],
        array $post_types = ['post', 'page']
    ): array {
        return $this->get_posts($fields, $post_types, ['author' => $author_id]);
    }

    /**
     * Get recent posts
     *
     * @param int $limit Number of recent posts to return
     * @param array $fields Fields to return
     * @param array $post_types Post types to include
     * @return array Array of post objects
     */
    public function get_recent_posts(
        int $limit = 10,
        array $fields = ['id', 'title'],
        array $post_types = ['post', 'page']
    ): array {
        return $this->get_posts($fields, $post_types, ['posts_per_page' => $limit]);
    }

    /**
     * Get titles of the latest N posts
     *
     * @param int $limit Number of latest posts to return (default: 10)
     * @param array $post_types Post types to include (default: ['post', 'page'])
     * @return array Array of post titles
     */
    public function get_latest_post_titles(int $limit = 10, array $post_types = ['post', 'page']): array
    {
        // Get posts with only title field
        $posts = $this->get_posts(['title'], $post_types, ['posts_per_page' => $limit]);

        // Extract just the titles into a simple array
        $titles = [];
        foreach ($posts as $post) {
            $titles[] = $post['title'];
        }

        return $titles;
    }

    /**
     * Get existing posts for prompt preparation.
     *
     * @return string Formatted string of existing post titles.
     */
    public function get_existing_posts_for_prompt(): string
    {
        try {
            // Get up to 50 most recent posts
            $posts = $this->get_posts_by_criteria(['title'], ['post'], '', 50, 'date', 'DESC');

            if (empty($posts)) {
                return 'No existing posts found.';
            }

            $titles = array_map(function ($post) {
                return $post['title'] ?? 'Untitled';
            }, $posts);

            return implode("\n", $titles);
        } catch (Exception $e) {
            return 'Error retrieving existing posts.';
        }
    }

    /**
     * Get previously generated content plan items formatted for prompt.
     *
     * @param CPEPAI_Database_Service $database_service The database service instance.
     * @return string
     */
    public function get_previously_generated_posts_for_prompt(CPEPAI_Database_Service $database_service): string
    {
        try {
            // Get all content plan items from the database
            $content_plan_items = $database_service->get_content_plan_items();

            // If no items found, return the fallback message
            if (empty($content_plan_items)) {
                return 'No previously generated content plans found.';
            }

            // Format the items for the prompt
            $formatted_items = [];
            foreach ($content_plan_items as $item) {
                $formatted_item = [];

                // Add title if available
                if (!empty($item['title'])) {
                    $formatted_item['title'] = $item['title'];
                }

                // Add structure if available
                if (!empty($item['structure'])) {
                    $formatted_item['structure'] = $item['structure'];
                }

                // Add keywords if available
                if (!empty($item['keywords'])) {
                    $formatted_item['keywords'] = $item['keywords'];
                }

                // Add status if available
                if (!empty($item['status'])) {
                    $formatted_item['status'] = $item['status'];
                }

                // Only add item if it has at least a title
                if (!empty($formatted_item['title'])) {
                    $formatted_items[] = $formatted_item;
                }
            }

            // If no valid items after formatting, return fallback message
            if (empty($formatted_items)) {
                return 'No previously generated content plans found.';
            }

            // Convert to JSON string for the prompt
            return json_encode($formatted_items, JSON_PRETTY_PRINT);
        } catch (Exception $e) {
            return 'No previously generated content plans found.';
        }
    }

    /**
     * Save a WordPress post from AI-generated content.
     *
     * @param array $content_plan_item The content plan item.
     * @param array $ai_response The parsed AI response with post sections.
     * @return array|WP_Error Array with post_id, post_url, and post_edit_url on success, WP_Error on failure.
     */
    public function save_post_from_ai_response(array $content_plan_item, array $ai_response)
    {
        try {
            // Validate inputs
            if (empty($content_plan_item['title'])) {
                return new WP_Error('missing_title', 'Content plan item must have a title');
            }

            if (empty($ai_response) || !is_array($ai_response)) {
                return new WP_Error('invalid_ai_response', 'AI response is empty or invalid');
            }

            // Get the post editor format from options (default to 'auto')
            $settings = get_option('cpepai_settings', []);
            $post_editor_format = $settings['post_editor_format'] ?? 'auto';

            // Auto-detect editor format if set to 'auto'
            if ($post_editor_format === 'auto') {
                // Include plugin.php if needed for is_plugin_active()
                if (!function_exists('is_plugin_active')) {
                    require_once ABSPATH . 'wp-admin/includes/plugin.php';
                }

                // Check if Elementor plugin is installed and active (Pro or Free)
                // Elementor Pro requires Free, so checking Pro first covers both
                if (
                    is_plugin_active('elementor-pro/elementor-pro.php') ||
                    is_plugin_active('elementor/elementor.php')
                ) {
                    $type = 'elementor';
                }
                // Check if WordPress block editor (Gutenberg) is available
                elseif (function_exists('use_block_editor_for_post_type') && use_block_editor_for_post_type('post')) {
                    $type = 'gutenberg';
                }
                // Fallback to classic editor
                else {
                    $type = 'classic';
                }
            } else {
                // Use the explicitly set editor format
                $type = $post_editor_format;
            }
            // Add error logging for detected editor type (for debugging and transparency)
            // Order AI response sections according to the post structure
            $ordered_ai_response = $this->order_ai_response_by_structure($ai_response, $content_plan_item);

            // Log ordered response keys for debugging
            // Convert AI response to post content based on type
            $post_content = '';
            $elementor_data = null;

            switch ($type) {
                case 'gutenberg':
                    $post_content = $this->convert_ai_response_to_gutenberg($ordered_ai_response);
                    break;

                case 'classic':
                    $post_content = $this->convert_ai_response_to_classic($ordered_ai_response);
                    break;

                case 'elementor':
                    $elementor_data = $this->convert_ai_response_to_elementor($ordered_ai_response);
                    // For Elementor, post_content can be empty or minimal
                    // Elementor uses _elementor_data meta field for content
                    $post_content = '';
                    break;

                default:
                    return new WP_Error('invalid_type', 'Invalid content type specified: ' . $type);
            }

            // Validate content (skip for Elementor as it uses separate data structure)
            if ($type !== 'elementor' && empty($post_content)) {
                return new WP_Error('empty_content', 'Failed to convert AI response to post content');
            }

            // Validate Elementor data
            if ($type === 'elementor' && empty($elementor_data)) {
                return new WP_Error('empty_elementor_data', 'Failed to convert AI response to Elementor data');
            }

            // Extract excerpt from ordered AI response if available
            $excerpt = '';
            if (isset($ordered_ai_response['excerpt_tldr'])) {
                $excerpt = wp_strip_all_tags($ordered_ai_response['excerpt_tldr']);
            }

            // Check if this is a regeneration (content plan item already has a post ID)
            $existing_post_id = null;
            $is_regeneration = false;
            if (!empty($content_plan_item['post'])) {
                $existing_post_id = (int) $content_plan_item['post'];
                // Verify the post exists and is not in trash
                $existing_post = get_post($existing_post_id);
                if ($existing_post && $existing_post->post_status !== 'trash') {
                    $is_regeneration = true;
                } else {
                    $existing_post_id = null; // Reset to null so we create a new post
                }
            }

            // Determine post status and date based on scheduled_on
            $post_status = 'draft'; // Default status
            $post_date = null;
            $post_date_gmt = null;

            // Set publishing date from content plan item's scheduled_on if available
            if (!empty($content_plan_item['scheduled_on'])) {
                $scheduled_date = $content_plan_item['scheduled_on'];

                // Parse the scheduled date - extract date part and set time to 10:00:00 AM
                // scheduled_on is stored in WordPress timezone format (Y-m-d H:i:s)
                if (preg_match('/^(\d{4}-\d{2}-\d{2})(?:\s+\d{2}:\d{2}:\d{2})?$/', $scheduled_date, $matches)) {
                    $date_part = $matches[1];

                    // Build date string with 10:00:00 AM in WordPress timezone
                    $post_date = $date_part . ' 10:00:00';

                    // Convert to timestamp using WordPress timezone
                    // We need to parse this as if it's in WordPress timezone
                    // Use current_time() to get WordPress timezone offset
                    $wp_timezone = wp_timezone();
                    $datetime = new DateTime($post_date, $wp_timezone);
                    $timestamp = $datetime->getTimestamp();

                    // Convert to GMT for post_date_gmt
                    $post_date_gmt = get_gmt_from_date($post_date);

                    $current_timestamp = current_time('timestamp');

                    if ($timestamp !== false) {
                        // Determine status: 'future' if date is in the future, 'draft' otherwise
                        // WordPress uses 'future' status for scheduled posts
                        if ($timestamp > $current_timestamp) {
                            $post_status = 'future';
                        } else {
                            $post_status = 'draft';
                        }
                    } else {
                    }
                } else {
                }
            } else {
            }

            // Prepare post data
            $post_data = [
                'post_title' => sanitize_text_field($content_plan_item['title']),
                'post_content' => $post_content,
                'post_excerpt' => $excerpt,
                'post_status' => $post_status,
                'post_type' => 'post',
                'post_author' => get_current_user_id()
            ];

            // Add dates if set
            if ($post_date !== null) {
                $post_data['post_date'] = $post_date;
                $post_data['post_date_gmt'] = $post_date_gmt;
                $post_data['edit_date'] = true; // Tell WordPress we're explicitly setting the date
            }

            // Update existing post or insert new post
            if ($is_regeneration && $existing_post_id) {
                // Update existing post
                $post_data['ID'] = $existing_post_id;
                $existing_post = get_post($existing_post_id);

                // Preserve existing post status if it's published
                // Otherwise use the status we determined (future or draft)
                if ($existing_post && $existing_post->post_status === 'publish') {
                    $post_data['post_status'] = 'publish';
                }

                $post_id = wp_update_post($post_data, true);
            } else {
                // Insert new post
                $post_id = wp_insert_post($post_data, true);

                if (!is_wp_error($post_id)) {
                    // Verify what WordPress saved and fix if needed
                    $saved_post = get_post($post_id);
                    if ($saved_post && isset($post_data['post_date'])) {
                        // If dates don't match, update them directly in database
                        if ($saved_post->post_date !== $post_data['post_date']) {
                            $this->wpdb->update(
                                $this->wpdb->posts,
                                [
                                    'post_date' => $post_data['post_date'],
                                    'post_date_gmt' => $post_data['post_date_gmt']
                                ],
                                ['ID' => $post_id],
                                ['%s', '%s'],
                                ['%d']
                            );

                            clean_post_cache($post_id);

                            // Verify again
                            $updated_post = get_post($post_id);
                            if ($updated_post) {
                            }
                        }
                    }
                } else {
                }
            }

            // Check for errors
            if (is_wp_error($post_id)) {
                return $post_id;
            }

            // Add post meta to link back to content plan item
            if (!empty($content_plan_item['id'])) {
                update_post_meta($post_id, '_content_plan_item_id', intval($content_plan_item['id']));
            }

            // Store keywords as post meta if available
            if (!empty($content_plan_item['keywords'])) {
                $keywords = $content_plan_item['keywords'];
                if (is_string($keywords)) {
                    $keywords = json_decode($keywords, true);
                }
                if (is_array($keywords)) {
                    update_post_meta($post_id, '_content_plan_keywords', $keywords);
                }
            }

            // Store generation metadata
            update_post_meta($post_id, '_content_plan_generated', true);
            update_post_meta($post_id, '_content_plan_generated_at', current_time('mysql'));
            update_post_meta($post_id, '_content_plan_generation_type', $type);

            // Set Elementor-specific meta if type is elementor
            if ($type === 'elementor') {
                // Store Elementor data structure
                if (!empty($elementor_data)) {
                    $elementor_json = wp_json_encode($elementor_data);
                    // Save Elementor data - use wp_slash to escape for database
                    update_post_meta($post_id, '_elementor_data', wp_slash($elementor_json));

                    // Set Elementor edit mode
                    update_post_meta($post_id, '_elementor_edit_mode', 'builder');

                    // Set Elementor version - get current Elementor version if available
                    $elementor_version = defined('ELEMENTOR_VERSION') ? constant('ELEMENTOR_VERSION') : '3.0.0';
                    update_post_meta($post_id, '_elementor_version', $elementor_version);

                    // Set template type
                    update_post_meta($post_id, '_elementor_template_type', 'wp-post');

                    // Set page template to Elementor Canvas if needed
                    update_post_meta($post_id, '_wp_page_template', 'default');

                    // Set CSS meta (empty initially, Elementor will regenerate)
                    update_post_meta($post_id, '_elementor_css', '');
                } else {
                }
            }

            // Get post URLs
            $post_url = get_permalink($post_id);
            $post_edit_url = get_edit_post_link($post_id, 'raw');

            return [
                'post_id' => $post_id,
                'post_url' => $post_url,
                'post_edit_url' => $post_edit_url
            ];
        } catch (Exception $e) {
            return new WP_Error('exception', 'Error saving post: ' . $e->getMessage());
        }
    }

    /**
     * Check if a section key corresponds to excerpt_tldr based on structure.
     * This checks if the structure contains excerpt_tldr (in any form) and if the section_key
     * matches the structure item that represents excerpt_tldr.
     *
     * @param string $section_key The section key to check.
     * @param array $structure The structure array.
     * @return bool True if the section is excerpt_tldr, false otherwise.
     */
    private function is_excerpt_tldr_section(string $section_key, array $structure): bool
    {
        $normalized_key = strtolower(trim($section_key));

        foreach ($structure as $structure_item) {
            $is_excerpt_in_structure = false;
            $structure_key = null;

            if (is_string($structure_item)) {
                // Check if structure_item is excerpt_tldr
                if ($structure_item === 'excerpt_tldr') {
                    $is_excerpt_in_structure = true;
                    $structure_key = 'excerpt_tldr';
                } else {
                    // Check if structure_item is a translated "Excerpt / TL;DR" variant
                    $normalized_structure = strtolower(trim($structure_item));

                    // Check for common TL;DR patterns
                    $tldr_patterns = [
                        'excerpt/tldr',
                        'excerpt/tl;dr',
                        'excerpt/tl/dr',
                        'excerpt tldr',
                        'excerpt tl;dr',
                        'excerpt tl/dr',
                        'excerpt/tl dr',
                        'excerpt tl dr',
                        'tldr',
                        'tl;dr',
                        'tl/dr',
                        'tl dr'
                    ];

                    foreach ($tldr_patterns as $pattern) {
                        if (strpos($normalized_structure, $pattern) !== false || $normalized_structure === $pattern) {
                            $is_excerpt_in_structure = true;
                            $structure_key = $structure_item; // Use original structure item as key
                            break;
                        }
                    }
                }
            } elseif (is_array($structure_item) && isset($structure_item['type'])) {
                // Check if structure item type is excerpt_tldr
                if ($structure_item['type'] === 'excerpt_tldr') {
                    $is_excerpt_in_structure = true;
                    // Use excerpt_tldr as key, or subtitle if available
                    $structure_key = isset($structure_item['subtitle']) ? $structure_item['subtitle'] : 'excerpt_tldr';
                }
            }

            // If this structure item represents excerpt_tldr, check if section_key matches
            if ($is_excerpt_in_structure && $structure_key !== null) {
                // Check exact match
                if ($section_key === $structure_key || $section_key === 'excerpt_tldr') {
                    return true;
                }
                // Check case-insensitive match
                if ($normalized_key === strtolower(trim($structure_key))) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Strip heading tags from HTML content.
     *
     * @param string $html The HTML content.
     * @return string The HTML content with heading tags removed.
     */
    private function strip_headings_from_html(string $html): string
    {
        // Remove h1, h2, h3, h4, h5, h6 tags and their content
        $html = preg_replace('/<h[1-6][^>]*>.*?<\/h[1-6]>/is', '', $html);
        // Also remove self-closing heading tags if any
        $html = preg_replace('/<h[1-6][^>]*\/>/is', '', $html);
        // Clean up extra whitespace
        $html = preg_replace('/\s+/', ' ', $html);
        $html = trim($html);
        return $html;
    }

    /**
     * Order AI response sections according to the post structure.
     *
     * @param array $ai_response The parsed AI response.
     * @param array $content_plan_item The content plan item with structure.
     * @return array The ordered AI response.
     */
    private function order_ai_response_by_structure(array $ai_response, array $content_plan_item): array
    {
        // Get structure from content plan item
        $structure = [];
        if (!empty($content_plan_item['structure'])) {
            $structure_raw = $content_plan_item['structure'];
            // Decode if it's a JSON string
            if (is_string($structure_raw)) {
                $structure = json_decode($structure_raw, true);
            } elseif (is_array($structure_raw)) {
                $structure = $structure_raw;
            }
        }

        // If no structure or invalid structure, return original order
        if (empty($structure) || !is_array($structure)) {
            return $ai_response;
        }

        // Build ordered response based on structure
        $ordered_response = [];
        $processed_keys = [];

        // First, add sections in the order defined by structure
        foreach ($structure as $idx => $structure_item) {
            $section_key = null;

            // Handle different structure item formats
            if (is_string($structure_item)) {
                // Direct string match (e.g., "excerpt_tldr", "conclusion_summary")
                $section_key = $structure_item;
            } elseif (is_array($structure_item) && isset($structure_item['type'])) {
                // Handle special types: excerpt_tldr, conclusion_summary, key_takeaways, and faq
                if (
                    $structure_item['type'] === 'excerpt_tldr' ||
                    $structure_item['type'] === 'conclusion_summary' ||
                    $structure_item['type'] === 'key_takeaways' ||
                    $structure_item['type'] === 'faq'
                ) {
                    // These types map directly to their keys in the AI response
                    $section_key = $structure_item['type'];
                } elseif ($structure_item['type'] === 'paragraph' && isset($structure_item['subtitle'])) {
                    // For paragraph types, we need to find matching key in ai_response
                    // The backend might use the subtitle as a key or a normalized version
                    $subtitle = $structure_item['subtitle'];
                    // Try exact match first
                    if (isset($ai_response[$subtitle])) {
                        $section_key = $subtitle;
                    } else {
                        // Try to find a matching key (case-insensitive, normalized)
                        foreach (array_keys($ai_response) as $key) {
                            if (strtolower(trim($key)) === strtolower(trim($subtitle))) {
                                $section_key = $key;
                                break;
                            }
                        }
                        if ($section_key === null) {
                        }
                    }
                }
            }

            // Add section if found in ai_response and not already processed
            if (
                $section_key !== null &&
                isset($ai_response[$section_key]) &&
                !in_array($section_key, $processed_keys)
            ) {
                $ordered_response[$section_key] = $ai_response[$section_key];
                $processed_keys[] = $section_key;
            } elseif ($section_key !== null && !isset($ai_response[$section_key])) {
            } elseif ($section_key !== null && in_array($section_key, $processed_keys)) {
            }
        }

        // Add any remaining sections from ai_response that weren't in structure
        // (in case backend returns extra sections)
        foreach ($ai_response as $key => $value) {
            if (!in_array($key, $processed_keys)) {
                $ordered_response[$key] = $value;
            }
        }
        // Strip headings from excerpt_tldr sections based on structure
        foreach ($ordered_response as $section_key => $section_content) {
            if ($this->is_excerpt_tldr_section($section_key, $structure)) {
                $original_content = $section_content;
                $stripped_content = $this->strip_headings_from_html($section_content);
                if ($original_content !== $stripped_content) {
                    $ordered_response[$section_key] = $stripped_content;
                }
            }
        }

        return $ordered_response;
    }

    /**
     * Convert AI response to Gutenberg block format.
     *
     * @param array $ai_response The parsed AI response (should be ordered by structure).
     * @return string The Gutenberg block content.
     */
    private function convert_ai_response_to_gutenberg(array $ai_response): string
    {
        $blocks = [];

        foreach ($ai_response as $section_key => $section_content) {
            // Special handling for excerpt_tldr - ensure it's included
            if ($section_key === 'excerpt_tldr') {
            }

            // Skip empty sections
            if (empty($section_content)) {
                continue;
            }

            // Log section being processed
            // Convert HTML content to proper Gutenberg blocks
            $converted_blocks = $this->convert_html_to_gutenberg_blocks($section_content);
            if (!empty($converted_blocks)) {
                $blocks = array_merge($blocks, $converted_blocks);
                if ($section_key === 'excerpt_tldr') {
                }
            } else {
            }
        }

        // Join all blocks with newlines
        return implode("\n\n", $blocks);
    }

    /**
     * Convert HTML content to Gutenberg blocks.
     *
     * @param string $html The HTML content to convert.
     * @return array Array of Gutenberg block strings.
     */
    private function convert_html_to_gutenberg_blocks(string $html): array
    {
        $blocks = [];

        // Strip whitespace and normalize
        $html = trim($html);
        if (empty($html)) {
            return $blocks;
        }

        // Use DOMDocument to parse HTML
        $dom = new DOMDocument('1.0', 'UTF-8');
        // Suppress warnings for malformed HTML
        libxml_use_internal_errors(true);

        // Wrap in a container div to handle multiple root elements
        // Use mb_convert_encoding to ensure UTF-8
        $html = mb_convert_encoding('<div>' . $html . '</div>', 'HTML-ENTITIES', 'UTF-8');
        $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        libxml_clear_errors();

        // Get the container div
        $container = $dom->getElementsByTagName('div')->item(0);
        if (!$container) {
            return $blocks;
        }

        // Process each child node
        foreach ($container->childNodes as $node) {
            $block = $this->convert_node_to_gutenberg_block($node);
            if (!empty($block)) {
                $blocks[] = $block;
            }
        }

        return $blocks;
    }

    /**
     * Convert a DOM node to a Gutenberg block.
     *
     * @param DOMNode $node The DOM node to convert.
     * @return string The Gutenberg block string, or empty string if node should be skipped.
     */
    private function convert_node_to_gutenberg_block(DOMNode $node): string
    {
        // Skip text nodes that are only whitespace
        if ($node->nodeType === XML_TEXT_NODE) {
            $text = trim($node->textContent);
            if (empty($text)) {
                return '';
            }
            // Convert standalone text to paragraph
            return $this->create_paragraph_block($text);
        }

        // Skip non-element nodes
        if ($node->nodeType !== XML_ELEMENT_NODE) {
            return '';
        }

        $tag_name = strtolower($node->nodeName);
        $inner_html = $this->get_inner_html($node);

        switch ($tag_name) {
            case 'h2':
                // If heading has schema attributes, it should be inside a schema container div
                // which will be handled by the div case above
                return $this->create_heading_block($inner_html, 2);

            case 'h3':
                // If heading has schema attributes, it should be inside a schema container div
                // which will be handled by the div case above
                return $this->create_heading_block($inner_html, 3);

            case 'p':
                // If paragraph has schema attributes, it should be inside a schema container div
                // which will be handled by the div case above
                return $this->create_paragraph_block($inner_html);

            case 'ul':
                return $this->create_list_block($node, false);

            case 'ol':
                return $this->create_list_block($node, true);

            case 'div':
                // Check if div has schema.org attributes - if so, preserve the entire div structure
                if ($this->has_schema_attributes($node)) {
                    // For schema markup, preserve the entire div with all its children as HTML
                    // This ensures nested schema attributes are preserved
                    $full_html = $this->get_full_html($node);
                    return $this->create_html_block($full_html);
                }
                // Process div children as separate blocks
                $div_blocks = [];
                foreach ($node->childNodes as $child) {
                    $block = $this->convert_node_to_gutenberg_block($child);
                    if (!empty($block)) {
                        $div_blocks[] = $block;
                    }
                }
                return implode("\n\n", $div_blocks);

            default:
                // For unknown tags, wrap in paragraph if it has content
                $text = trim($inner_html);
                if (!empty($text)) {
                    return $this->create_paragraph_block($inner_html);
                }
                return '';
        }
    }

    /**
     * Create a Gutenberg HTML block for custom HTML content.
     *
     * @param string $html The HTML content to preserve.
     * @return string The Gutenberg HTML block string.
     */
    private function create_html_block(string $html): string
    {
        $html = trim($html);
        if (empty($html)) {
            return '';
        }

        // Gutenberg HTML blocks should contain the raw HTML
        // Note: HTML blocks display as code in the editor but render properly on the frontend
        // The HTML block format is: <!-- wp:html -->\nHTML_CONTENT\n<!-- /wp:html -->
        // Ensure HTML is properly formatted (no extra whitespace)
        $html = preg_replace('/>\s+</', '><', $html); // Remove whitespace between tags
        $html = preg_replace('/\s+/', ' ', $html); // Normalize whitespace
        $html = trim($html);

        return '<!-- wp:html -->' . "\n" . $html . "\n" . '<!-- /wp:html -->';
    }

    /**
     * Create a Gutenberg heading block.
     *
     * @param string $content The heading content.
     * @param int $level The heading level (2 or 3).
     * @return string The Gutenberg block string.
     */
    private function create_heading_block(string $content, int $level): string
    {
        $content = trim($content);
        if (empty($content)) {
            return '';
        }

        // Preserve formatting tags like <strong> and <em> in headings
        $content = $this->preserve_formatting_tags($content);

        return sprintf(
            '<!-- wp:heading {"level":%d} -->' . "\n" . '<h%d>%s</h%d>' . "\n" . '<!-- /wp:heading -->',
            $level,
            $level,
            $content,
            $level
        );
    }

    /**
     * Create a Gutenberg paragraph block.
     *
     * @param string $content The paragraph content.
     * @return string The Gutenberg block string.
     */
    private function create_paragraph_block(string $content): string
    {
        $content = trim($content);
        if (empty($content)) {
            return '';
        }

        // Preserve HTML formatting tags like <strong> and <em>
        // But ensure proper encoding
        $content = $this->preserve_formatting_tags($content);

        return '<!-- wp:paragraph -->' . "\n" . '<p>' . $content . '</p>' . "\n" . '<!-- /wp:paragraph -->';
    }

    /**
     * Create a Gutenberg list block.
     *
     * @param DOMNode $list_node The list DOM node (ul or ol).
     * @param bool $ordered Whether the list is ordered.
     * @return string The Gutenberg block string.
     */
    private function create_list_block(DOMNode $list_node, bool $ordered): string
    {
        $items = [];

        foreach ($list_node->childNodes as $child) {
            if ($child->nodeName === 'li') {
                $item_content = $this->get_inner_html($child);
                $item_content = trim($item_content);
                if (!empty($item_content)) {
                    $item_content = $this->preserve_formatting_tags($item_content);
                    $items[] = '<li>' . $item_content . '</li>';
                }
            }
        }

        if (empty($items)) {
            return '';
        }

        $list_items = implode("\n", $items);
        $ordered_attr = $ordered ? 'true' : 'false';

        return sprintf(
            '<!-- wp:list {"ordered":%s} -->' .
                "\n" .
                '<%s class="wp-block-list">' .
                "\n" .
                '%s' .
                "\n" .
                '</%s>' .
                "\n" .
                '<!-- /wp:list -->',
            $ordered_attr,
            $ordered ? 'ol' : 'ul',
            $list_items,
            $ordered ? 'ol' : 'ul'
        );
    }

    /**
     * Preserve formatting tags like <strong> and <em> while sanitizing.
     *
     * @param string $content The content to process.
     * @return string The sanitized content with formatting preserved.
     */
    private function preserve_formatting_tags(string $content): string
    {
        // Allow only safe formatting tags
        $allowed_tags = '<strong><em><a><code>';
        return strip_tags($content, $allowed_tags);
    }

    /**
     * Get inner HTML of a DOM node.
     *
     * @param DOMNode $node The DOM node.
     * @return string The inner HTML content.
     */
    private function get_inner_html(DOMNode $node): string
    {
        $inner_html = '';
        foreach ($node->childNodes as $child) {
            $inner_html .= $node->ownerDocument->saveHTML($child);
        }
        return $inner_html;
    }

    /**
     * Check if a DOM node has schema.org microdata attributes.
     *
     * @param DOMNode $node The DOM node to check.
     * @return bool True if the node has schema.org attributes.
     */
    private function has_schema_attributes(DOMNode $node): bool
    {
        if ($node->nodeType !== XML_ELEMENT_NODE) {
            return false;
        }

        // Cast to DOMElement to access hasAttribute method
        if (!($node instanceof DOMElement)) {
            return false;
        }

        // Check for schema.org attributes
        if ($node->hasAttribute('itemscope') || $node->hasAttribute('itemtype') || $node->hasAttribute('itemprop')) {
            return true;
        }

        return false;
    }

    /**
     * Get the full HTML of a DOM node including its tag and attributes.
     *
     * @param DOMNode $node The DOM node.
     * @return string The full HTML including tag and attributes.
     */
    private function get_full_html(DOMNode $node): string
    {
        if ($node->nodeType !== XML_ELEMENT_NODE) {
            return '';
        }

        return $node->ownerDocument->saveHTML($node);
    }

    /**
     * Convert AI response to classic editor HTML format.
     *
     * @param array $ai_response The parsed AI response.
     * @return string The HTML content.
     */
    private function convert_ai_response_to_classic(array $ai_response): string
    {
        $html_parts = [];

        foreach ($ai_response as $section_key => $section_content) {
            // Special handling for excerpt_tldr - ensure it's included
            if ($section_key === 'excerpt_tldr') {
            }

            // Skip empty sections
            if (empty($section_content)) {
                continue;
            }

            // Log section being processed
            // Just append the HTML content
            $html_parts[] = $section_content;

            if ($section_key === 'excerpt_tldr') {
            }
        }

        return implode("\n\n", $html_parts);
    }

    /**
     * Convert AI response to Elementor JSON data structure.
     *
     * @param array $ai_response The parsed AI response.
     * @return array|null Elementor data structure or null on failure.
     */
    private function convert_ai_response_to_elementor(array $ai_response): ?array
    {
        $widgets = [];
        // Process each section separately (like Gutenberg conversion)
        foreach ($ai_response as $section_key => $section_content) {
            // Skip empty sections
            if (empty($section_content)) {
                continue;
            }
            // Convert HTML content to Elementor widgets
            $section_widgets = $this->convert_html_to_elementor_widgets($section_content);
            if (!empty($section_widgets)) {
                foreach ($section_widgets as $idx => $widget) {
                    $widget_type = $widget['widgetType'] ?? 'unknown';
                }
                $widgets = array_merge($widgets, $section_widgets);
            }
        }
        // If no widgets were created, return null
        if (empty($widgets)) {
            return null;
        }

        // Create Elementor structure: Section -> Column -> Widgets
        $column_id = $this->generate_elementor_id();
        $section_id = $this->generate_elementor_id();
        return [
            [
                'id' => $section_id,
                'elType' => 'section',
                'settings' => (object) [],
                'isInner' => false,
                'elements' => [
                    [
                        'id' => $column_id,
                        'elType' => 'column',
                        'settings' => (object) [
                            '_column_size' => 100,
                            '_inline_size' => null
                        ],
                        'isInner' => false,
                        'elements' => $widgets
                    ]
                ]
            ]
        ];
    }

    /**
     * Convert HTML content to Elementor widgets.
     *
     * @param string $html The HTML content to convert.
     * @return array Array of Elementor widget arrays.
     */
    private function convert_html_to_elementor_widgets(string $html): array
    {
        $widgets = [];

        // Strip whitespace and normalize
        $html = trim($html);
        if (empty($html)) {
            return $widgets;
        }
        // Use DOMDocument to parse HTML
        $dom = new DOMDocument('1.0', 'UTF-8');
        // Suppress warnings for malformed HTML
        libxml_use_internal_errors(true);

        // Wrap in a container div to handle multiple root elements
        // Use mb_convert_encoding to ensure UTF-8
        $wrapped_html = mb_convert_encoding('<div>' . $html . '</div>', 'HTML-ENTITIES', 'UTF-8');
        $load_result = $dom->loadHTML($wrapped_html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        $errors = libxml_get_errors();
        libxml_clear_errors();

        if (!$load_result) {
            return $widgets;
        }

        if (!empty($errors)) {
            foreach ($errors as $error) {
            }
        }

        // Get the container div - DOMDocument may wrap in html/body, so check for body first
        $container = null;
        $body = $dom->getElementsByTagName('body')->item(0);
        if ($body) {
            // If body exists, get the div inside it
            $container = $body->getElementsByTagName('div')->item(0);
            // If no div in body, use body itself
            if (!$container) {
                $container = $body;
            } else {
            }
        } else {
            // Otherwise, get the div directly
            $container = $dom->getElementsByTagName('div')->item(0);
            // If no div, try to get body or documentElement
            if (!$container) {
                $container = $dom->documentElement;
            }
        }

        if (!$container) {
            return $widgets;
        }
        // Log all child nodes
        $child_count = 0;
        foreach ($container->childNodes as $node) {
            $child_count++;
            $node_type = '';
            if ($node->nodeType === XML_ELEMENT_NODE) {
                $node_type = 'ELEMENT: ' . strtolower($node->nodeName);
            } elseif ($node->nodeType === XML_TEXT_NODE) {
                $text_preview = substr(trim($node->textContent), 0, 50);
                $node_type = 'TEXT: ' . ($text_preview ?: '(empty/whitespace)');
            } else {
                $node_type = 'OTHER: ' . $node->nodeType;
            }
        }

        // Process each child node
        $processed_count = 0;
        foreach ($container->childNodes as $node) {
            $processed_count++;
            $node_widgets = $this->convert_node_to_elementor_widgets($node);
            if (!empty($node_widgets)) {
                $widgets = array_merge($widgets, $node_widgets);
            } else {
            }
        }
        return $widgets;
    }

    /**
     * Convert a DOM node to Elementor widgets (can return multiple widgets for containers).
     *
     * @param DOMNode $node The DOM node to convert.
     * @return array Array of Elementor widget arrays (can be empty).
     */
    private function convert_node_to_elementor_widgets(DOMNode $node): array
    {
        $widgets = [];

        // Skip text nodes that are only whitespace
        if ($node->nodeType === XML_TEXT_NODE) {
            $text = trim($node->textContent);
            if (empty($text)) {
                return $widgets;
            }
            // Convert standalone text to text-editor widget
            $widgets[] = $this->create_elementor_text_widget($text);
            return $widgets;
        }

        // Skip non-element nodes
        if ($node->nodeType !== XML_ELEMENT_NODE) {
            return $widgets;
        }

        $tag_name = strtolower($node->nodeName);
        $inner_html = $this->get_inner_html($node);
        $text_content = trim($node->textContent);
        // Handle divs by processing their children
        if ($tag_name === 'div') {
            // Check if div has schema.org attributes - if so, preserve the entire div structure
            if ($this->has_schema_attributes($node)) {
                // Preserve schema markup by including the full div HTML in text widget
                $full_html = $this->get_full_html($node);
                $widgets[] = $this->create_elementor_text_widget($full_html);
                return $widgets;
            }
            foreach ($node->childNodes as $child) {
                $child_widgets = $this->convert_node_to_elementor_widgets($child);
                if (!empty($child_widgets)) {
                    $widgets = array_merge($widgets, $child_widgets);
                }
            }
            return $widgets;
        }

        // Handle headings first (before checking if text_content is empty)
        // Headings might have empty textContent if they contain only formatting tags
        if (in_array($tag_name, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])) {
            // Check if heading has schema attributes (like itemprop="name")
            if ($this->has_schema_attributes($node)) {
                // Preserve schema markup by including the full heading HTML in text widget
                $full_html = $this->get_full_html($node);
                $widgets[] = $this->create_elementor_text_widget($full_html);
                return $widgets;
            }
            $level = (int) substr($tag_name, 1);
            // Use textContent for headings (strip HTML formatting)
            $heading_text = trim($node->textContent);
            if (!empty($heading_text)) {
                $widgets[] = $this->create_elementor_heading_widget($heading_text, $level);
            } else {
            }
            return $widgets;
        }

        // Skip empty content for other elements
        if (empty($text_content)) {
            return $widgets;
        }

        switch ($tag_name) {
            case 'p':
                // Check if paragraph has schema attributes (like itemprop="text")
                if ($this->has_schema_attributes($node)) {
                    // Preserve schema markup by including the full paragraph HTML
                    $full_html = $this->get_full_html($node);
                    $widgets[] = $this->create_elementor_text_widget($full_html);
                } else {
                    // Preserve HTML formatting in paragraphs
                    $content = $this->preserve_formatting_tags($inner_html);
                    $widgets[] = $this->create_elementor_text_widget($content);
                }
                break;

            case 'ul':
            case 'ol':
                // Convert lists to text widget with HTML list tags
                $list_html = $this->get_inner_html($node);
                $list_html = $this->preserve_formatting_tags($list_html);
                $widgets[] = $this->create_elementor_text_widget(
                    '<' . $tag_name . '>' . $list_html . '</' . $tag_name . '>'
                );
                break;

            default:
                // For unknown tags, wrap in text widget if it has content
                $content = $this->preserve_formatting_tags($inner_html);
                $widgets[] = $this->create_elementor_text_widget($content);
                break;
        }
        return $widgets;
    }

    /**
     * Create an Elementor heading widget.
     *
     * @param string $title The heading text.
     * @param int $level The heading level (1-6).
     * @return array Elementor widget array.
     */
    private function create_elementor_heading_widget(string $title, int $level): array
    {
        // Ensure level is between 1 and 6
        $level = max(1, min(6, $level));
        $html_tag = 'h' . $level;

        return [
            'id' => $this->generate_elementor_id(),
            'elType' => 'widget',
            'settings' => (object) [
                'title' => $title,
                'header_size' => $html_tag
            ],
            'elements' => [],
            'widgetType' => 'heading'
        ];
    }

    /**
     * Create an Elementor text editor widget.
     *
     * @param string $content The text content (can include HTML).
     * @return array Elementor widget array.
     */
    private function create_elementor_text_widget(string $content): array
    {
        return [
            'id' => $this->generate_elementor_id(),
            'elType' => 'widget',
            'settings' => (object) [
                'editor' => $content
            ],
            'elements' => [],
            'widgetType' => 'text-editor'
        ];
    }

    /**
     * Counter for generating unique IDs
     */
    private static $elementor_id_counter = 0;

    /**
     * Generate a unique Elementor ID.
     *
     * @return string Unique ID.
     */
    private function generate_elementor_id(): string
    {
        // Elementor uses lowercase alphanumeric IDs, typically 7 characters
        // Use counter + random to ensure each ID is unique
        self::$elementor_id_counter++;

        // Combine counter with random hex characters
        $id = dechex(self::$elementor_id_counter) . bin2hex(random_bytes(3));

        // Take last 7 characters to ensure uniqueness (counter changes are at the end)
        return substr(strtolower($id), -7);
    }
}
