<?php
/**
 * KB Content Parser - Parse WordPress posts into KB sections
 *
 * Detects Gutenberg blocks and Elementor widgets, extracts sections,
 * and prepares content for Knowledge Base ingestion.
 */

namespace SmartCloud\WPSuite\AiKit\KnowledgeBase;

use SmartCloud\WPSuite\AiKit\Logger;

if (!defined('ABSPATH')) {
    exit;
}

class Parser
{
    private Converter $converter;
    private ?\WP_Post $current_post = null;

    /**
     * Tracks post IDs referenced during parsing (for dependency tracking)
     * @var array<int>
     */
    private array $referenced_post_ids = [];

    public function __construct()
    {
        $this->converter = new Converter();
    }

    /**
     * Parse a WordPress post into KB sections grouped by doc_id
     *
     * @return array Sections grouped by doc_id: ['post-123/base' => [section1, section2...], ...]
     */
    public function parsePost(\WP_Post $post): array
    {
        $this->current_post = $post;
        $this->referenced_post_ids = []; // Reset for each parse

        Logger::info('Parsing post for KB', [
            'post_id' => $post->ID,
            'post_type' => $post->post_type,
            'post_title' => $post->post_title
        ]);

        // Detect content type
        if ($this->isElementorPost($post)) {
            Logger::debug('Detected Elementor post', ['post_id' => $post->ID]);
            $result = $this->parseElementorPost($post);
        } elseif ($this->isGutenbergPost($post)) {
            Logger::debug('Detected Gutenberg post', ['post_id' => $post->ID]);
            $result = $this->parseGutenbergPost($post);
        } else {
            Logger::debug('Detected Classic post', ['post_id' => $post->ID]);
            $result = $this->parseClassicPost($post);
        }

        $total_sections = array_sum(array_map('count', $result));
        Logger::info('Post parsing completed', [
            'post_id' => $post->ID,
            'doc_count' => count($result),
            'total_sections' => $total_sections,
            'referenced_posts' => count($this->referenced_post_ids)
        ]);

        return $result;
    }

    /**
     * Get all post IDs that were referenced during the last parse
     * 
     * @return array<int> Array of referenced post IDs
     */
    public function getReferencedPostIds(): array
    {
        return array_unique(array_filter($this->referenced_post_ids));
    }

    /**
     * Track a referenced post ID
     * 
     * @param int $post_id The referenced post ID
     */
    private function trackReferencedPost(int $post_id): void
    {
        if ($post_id > 0 && $post_id !== $this->current_post->ID) {
            $this->referenced_post_ids[] = $post_id;
        }
    }

    /**
     * Check if post uses Gutenberg blocks
     */
    private function isGutenbergPost(\WP_Post $post): bool
    {
        return has_blocks($post->post_content);
    }

    /**
     * Check if post uses Elementor
     */
    private function isElementorPost(\WP_Post $post): bool
    {
        // Elementor data can remain after a page is migrated back to Gutenberg.
        // Treat it as active Elementor content only when Elementor marks the post as builder-managed.
        $edit_mode = get_post_meta($post->ID, '_elementor_edit_mode', true);
        if ($edit_mode !== 'builder') {
            return false;
        }

        $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
        return !empty($elementor_data);
    }

    /**
     * Parse Gutenberg post with block detection
     */
    private function parseGutenbergPost(\WP_Post $post): array
    {
        $blocks = parse_blocks($post->post_content);

        Logger::debug('Parsing Gutenberg blocks', [
            'post_id' => $post->ID,
            'block_count' => count($blocks)
        ]);

        // Extract post references from blocks for dependency tracking
        $this->extractPostReferences($blocks);

        // Check if there are any smartcloud-ai-kit/kb-section blocks
        $has_kb_sections = $this->hasKbSectionBlocks($blocks);

        if ($has_kb_sections) {
            return $this->parseGutenbergWithSections($post, $blocks);
        } else {
            return $this->parseGutenbergSimple($post, $blocks);
        }
    }

    /**
     * Check if blocks contain smartcloud-ai-kit/kb-section blocks (recursively)
     */
    private function hasKbSectionBlocks(array $blocks): bool
    {
        foreach ($blocks as $block) {
            if (($block['blockName'] ?? '') === 'smartcloud-ai-kit/kb-section') {
                return true;
            }

            if (!empty($block['innerBlocks'])) {
                if ($this->hasKbSectionBlocks($block['innerBlocks'])) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Extract post references from blocks (recursively) for dependency tracking
     * Detects common Gutenberg blocks that reference other posts
     * 
     * @param array $blocks Array of Gutenberg blocks
     */
    private function extractPostReferences(array $blocks): void
    {
        foreach ($blocks as $block) {
            $block_name = $block['blockName'] ?? '';
            $attrs = $block['attrs'] ?? [];

            // Detect post references based on block type
            switch ($block_name) {
                case 'core/post-content':
                case 'core/post-featured-image':
                case 'core/post-title':
                    // These blocks reference a specific post via postId attribute
                    if (!empty($attrs['postId'])) {
                        $this->trackReferencedPost((int) $attrs['postId']);
                    }
                    break;

                case 'core/latest-posts':
                    // Latest posts block - track specific posts if available
                    if (!empty($attrs['selectedPosts'])) {
                        foreach ($attrs['selectedPosts'] as $post_id) {
                            $this->trackReferencedPost((int) $post_id);
                        }
                    }
                    break;

                case 'core/query':
                    // Query loop - track specific posts if available
                    if (!empty($attrs['query']['include'])) {
                        $include = is_string($attrs['query']['include'])
                            ? explode(',', $attrs['query']['include'])
                            : $attrs['query']['include'];
                        foreach ($include as $post_id) {
                            $this->trackReferencedPost((int) $post_id);
                        }
                    }
                    break;

                case 'core/embed':
                    // Embedded WordPress posts - extract post ID from URL
                    $url = $attrs['url'] ?? '';
                    if (!empty($url) && preg_match('/[?&]p=(\d+)/', $url, $matches)) {
                        $this->trackReferencedPost((int) $matches[1]);
                    }
                    break;
            }

            // Also check innerHTML for shortcodes that reference posts
            $inner_html = $block['innerHTML'] ?? '';
            if (!empty($inner_html)) {
                // Detect common shortcodes with post ID parameters
                // Examples: [post id="123"], [display-posts id="123"], etc.
                if (preg_match_all('/\[\w+[^\]]*\bid\s*=\s*["\']?(\d+)["\']?[^\]]*\]/', $inner_html, $matches)) {
                    foreach ($matches[1] as $post_id) {
                        $this->trackReferencedPost((int) $post_id);
                    }
                }
            }

            // Recursively process inner blocks
            if (!empty($block['innerBlocks'])) {
                $this->extractPostReferences($block['innerBlocks']);
            }
        }
    }

    /**
     * Extract post references from shortcodes in content (for Classic Editor)
     * 
     * @param string $content The post content to scan
     */
    private function extractShortcodeReferences(string $content): void
    {
        if (empty($content)) {
            return;
        }

        // Detect common shortcodes with post ID parameters
        // Examples: [post id="123"], [display-posts id="123"], [embed post="456"], etc.

        // Pattern 1: id="123" or id='123' or id=123
        if (preg_match_all('/\[\w+[^\]]*\bid\s*=\s*["\']?(\d+)["\']?[^\]]*\]/', $content, $matches)) {
            foreach ($matches[1] as $post_id) {
                $this->trackReferencedPost((int) $post_id);
            }
        }

        // Pattern 2: post="123" or posts="123,456"
        if (preg_match_all('/\[\w+[^\]]*\bposts?\s*=\s*["\']?([0-9,\s]+)["\']?[^\]]*\]/', $content, $matches)) {
            foreach ($matches[1] as $id_list) {
                $ids = preg_split('/[,\s]+/', trim($id_list), -1, PREG_SPLIT_NO_EMPTY);
                foreach ($ids as $post_id) {
                    $this->trackReferencedPost((int) $post_id);
                }
            }
        }

        // Pattern 3: href or src containing ?p=123 or &p=123 (embedded post links)
        if (preg_match_all('/(?:href|src)\s*=\s*["\'][^"\']*[?&]p=(\d+)[^"\']*["\']/', $content, $matches)) {
            foreach ($matches[1] as $post_id) {
                $this->trackReferencedPost((int) $post_id);
            }
        }
    }

    /**
     * Parse Gutenberg post with smartcloud-ai-kit/kb-section blocks
     * Sections are extracted and grouped by doc_id
     */
    private function parseGutenbergWithSections(\WP_Post $post, array $blocks): array
    {
        $sections_by_doc = [];
        $base_content_blocks = [];
        $sort_order = 0;

        foreach ($blocks as $block) {
            if (($block['blockName'] ?? '') === 'smartcloud-ai-kit/kb-section') {
                // This is a KB section block
                $section = $this->parseKbSectionBlock($post, $block, $sort_order);

                if ($section['mode'] !== 'exclude') {
                    $doc_id = $section['doc_id'];

                    if (!isset($sections_by_doc[$doc_id])) {
                        $sections_by_doc[$doc_id] = [];
                    }

                    $sections_by_doc[$doc_id][] = $section;
                    $sort_order++;
                }
            } else {
                // Regular content - goes to base doc
                $base_content_blocks[] = $block;
            }
        }

        // Process base content if any
        if (!empty($base_content_blocks)) {
            $base_doc_id = "post-{$post->ID}/base";

            $category_hierarchy = $this->getPostCategoryHierarchy($post);

            $base_section = [
                'post_id' => $post->ID,
                'doc_id' => $base_doc_id,
                'section_id' => 'main',
                'mode' => 'inherit',
                'sort_order' => 0,
                'title' => $post->post_title,
                'category' => $category_hierarchy['category'],
                'subcategory' => $category_hierarchy['subcategory'],
                'tags' => $this->getPostTags($post),
                'md' => $this->converter->blocksToMarkdown($base_content_blocks),
                'generated_at' => current_time('mysql'),
                'source_updated_at' => $post->post_modified,
                'extra_meta' => [
                    'description' => $this->getPostDescription($post),
                    'postUrl' => $this->getPostUrl($post),
                ],
            ];

            $base_section['origin_hash'] = $this->calculateOriginHash($base_section);

            if (!isset($sections_by_doc[$base_doc_id])) {
                $sections_by_doc[$base_doc_id] = [];
            }

            array_unshift($sections_by_doc[$base_doc_id], $base_section);
        }

        return $sections_by_doc;
    }

    /**
     * Parse a single smartcloud-ai-kit/kb-section block
     */
    private function parseKbSectionBlock(\WP_Post $post, array $block, int $sort_order): array
    {
        $attrs = $block['attrs'] ?? [];
        $inner_blocks = $block['innerBlocks'] ?? [];

        // Extract attributes
        $mode = $attrs['mode'] ?? 'inherit';
        $section_key = $attrs['sectionKey'] ?? null;
        $doc_key = $attrs['docKey'] ?? null;
        $title = $attrs['title'] ?? null;
        $description = $attrs['description'] ?? null;
        $post_url = $attrs['postUrl'] ?? null;
        $tags = $attrs['tags'] ?? null;
        $category = $attrs['category'] ?? null;
        $subcategory = $attrs['subcategory'] ?? null;
        $priority = $attrs['priority'] ?? null;

        // Generate stable IDs
        $section_id = $section_key ?? $this->generateSectionId($block);

        // Determine doc_id
        if ($mode === 'separate_doc' && !empty($doc_key)) {
            $doc_id = "post-{$post->ID}/{$doc_key}";
        } else {
            $doc_id = "post-{$post->ID}/base";
        }

        // Convert inner blocks to markdown
        $md = $this->converter->blocksToMarkdown($inner_blocks);

        // Get category hierarchy if not overridden
        $category_hierarchy = $this->getPostCategoryHierarchy($post);

        // Build section data
        $section = [
            'post_id' => $post->ID,
            'doc_id' => $doc_id,
            'section_id' => $section_id,
            'mode' => $mode,
            'sort_order' => $sort_order,
            'title' => $title ?? $post->post_title,
            'category' => $category ?? $category_hierarchy['category'],
            'subcategory' => $subcategory ?? $category_hierarchy['subcategory'],
            'tags' => $tags ?? $this->getPostTags($post),
            'md' => $md,
            'generated_at' => current_time('mysql'),
            'source_updated_at' => $post->post_modified,
            'extra_meta' => [
                'block_type' => 'gutenberg',
                'description' => $description,
                'postUrl' => $post_url,
                'priority' => $priority,
            ],
        ];

        $section['origin_hash'] = $this->calculateOriginHash($section);

        return $section;
    }

    /**
     * Parse Gutenberg post without KB sections (simple mode)
     */
    private function parseGutenbergSimple(\WP_Post $post, array $blocks): array
    {
        $doc_id = "post-{$post->ID}/base";

        $markdown = $this->converter->blocksToMarkdown($blocks);

        $category_hierarchy = $this->getPostCategoryHierarchy($post);

        $section = [
            'post_id' => $post->ID,
            'doc_id' => $doc_id,
            'section_id' => 'main',
            'mode' => 'inherit',
            'sort_order' => 0,
            'title' => $post->post_title,
            'category' => $category_hierarchy['category'],
            'subcategory' => $category_hierarchy['subcategory'],
            'tags' => $this->getPostTags($post),
            'md' => $markdown,
            'generated_at' => current_time('mysql'),
            'source_updated_at' => $post->post_modified,
            'extra_meta' => [
                'description' => $this->getPostDescription($post),
                'postUrl' => $this->getPostUrl($post),
            ],
        ];

        $section['origin_hash'] = $this->calculateOriginHash($section);

        return [$doc_id => [$section]];
    }

    /**
     * Parse Elementor post
     */
    private function parseElementorPost(\WP_Post $post): array
    {
        $elementor_data = get_post_meta($post->ID, '_elementor_data', true);

        if (empty($elementor_data)) {
            Logger::warning('Elementor data empty, falling back to classic parser', [
                'post_id' => $post->ID
            ]);
            return $this->parseClassicPost($post);
        }

        $elements = json_decode($elementor_data, true);

        if (!is_array($elements)) {
            Logger::warning('Elementor data invalid JSON, falling back to classic parser', [
                'post_id' => $post->ID
            ]);
            return $this->parseClassicPost($post);
        }

        Logger::debug('Parsing Elementor elements', [
            'post_id' => $post->ID,
            'element_count' => count($elements)
        ]);

        // Extract post references from Elementor widgets for dependency tracking
        $this->extractElementorReferences($elements);

        // Check for KB section widgets
        $has_kb_widgets = $this->hasKbSectionWidgets($elements);

        if ($has_kb_widgets) {
            return $this->parseElementorWithSections($post, $elements);
        } else {
            return $this->parseElementorSimple($post, $elements);
        }
    }

    /**     * Extract post references from Elementor widgets (recursively)
     * 
     * @param array $elements Array of Elementor elements/widgets
     */
    private function extractElementorReferences(array $elements): void
    {
        foreach ($elements as $element) {
            $widget_type = $element['widgetType'] ?? $element['elType'] ?? '';
            $settings = $element['settings'] ?? [];

            // Detect post references based on Elementor widget type
            switch ($widget_type) {
                case 'posts':
                case 'archive-posts':
                case 'loop-grid':
                    // Posts widget - check for specific posts
                    if (!empty($settings['posts_post_id'])) {
                        $post_ids = is_string($settings['posts_post_id'])
                            ? explode(',', $settings['posts_post_id'])
                            : (array) $settings['posts_post_id'];
                        foreach ($post_ids as $post_id) {
                            $this->trackReferencedPost((int) $post_id);
                        }
                    }
                    break;

                case 'single-post':
                    // Single post widget
                    if (!empty($settings['post_id'])) {
                        $this->trackReferencedPost((int) $settings['post_id']);
                    }
                    break;

                case 'wordpress-widget-recent-posts':
                    // Recent posts widget - no specific IDs, skip
                    break;
            }

            // Also check for shortcodes in text/HTML widgets
            if (in_array($widget_type, ['text-editor', 'html', 'shortcode'], true)) {
                $content = $settings['editor'] ?? $settings['html'] ?? $settings['shortcode'] ?? '';
                if (!empty($content)) {
                    $this->extractShortcodeReferences($content);
                }
            }

            // Recursively process child elements
            if (!empty($element['elements'])) {
                $this->extractElementorReferences($element['elements']);
            }
        }
    }

    /**     * Check if Elementor data contains KB section widgets or KB-enabled containers
     */
    private function hasKbSectionWidgets(array $elements): bool
    {
        foreach ($elements as $element) {
            $widget_type = $element['widgetType'] ?? $element['elType'] ?? '';

            // Check for standalone KB section widget (legacy)
            if ($widget_type === 'smartcloud_ai_kit_kb_section') {
                return true;
            }

            // Check for KB-enabled containers/sections
            $settings = $element['settings'] ?? [];
            if (!empty($settings['kb_enabled']) && $settings['kb_enabled'] === 'yes') {
                return true;
            }

            // Recursively check child elements
            if (!empty($element['elements'])) {
                if ($this->hasKbSectionWidgets($element['elements'])) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Parse Elementor post with KB section widgets
     */
    private function parseElementorWithSections(\WP_Post $post, array $elements): array
    {
        $sections_by_doc = [];
        $sort_order_counter = ['value' => 0];

        // Process all elements recursively
        $base_elements = $this->extractElementorKbSections($post, $elements, $sections_by_doc, $sort_order_counter);

        // Always create base doc if we have base elements OR if sections_by_doc is empty
        // (empty means we had KB sections but they were all excluded or separate_doc)
        if (!empty($base_elements) || empty($sections_by_doc)) {
            $base_doc_id = "post-{$post->ID}/base";

            $category_hierarchy = $this->getPostCategoryHierarchy($post);

            // If no base elements, still create empty base doc
            $markdown = '';
            if (!empty($base_elements)) {
                // Render base elements to HTML then convert to markdown
                $html = $this->renderElementorElements($post->ID, $base_elements);
                $markdown = $this->converter->htmlToMarkdown($html);
            }

            $base_section = [
                'post_id' => $post->ID,
                'doc_id' => $base_doc_id,
                'section_id' => 'main',
                'mode' => 'inherit',
                'sort_order' => 0,
                'title' => $post->post_title,
                'category' => $category_hierarchy['category'],
                'subcategory' => $category_hierarchy['subcategory'],
                'tags' => $this->getPostTags($post),
                'md' => $markdown,
                'generated_at' => current_time('mysql'),
                'source_updated_at' => $post->post_modified,
                'extra_meta' => [
                    'description' => $this->getPostDescription($post),
                    'postUrl' => $this->getPostUrl($post),
                    'source_type' => 'elementor',
                ],
            ];

            $base_section['origin_hash'] = $this->calculateOriginHash($base_section);

            if (!isset($sections_by_doc[$base_doc_id])) {
                $sections_by_doc[$base_doc_id] = [];
            }

            // Base section should be first
            array_unshift($sections_by_doc[$base_doc_id], $base_section);
        }

        return $sections_by_doc;
    }

    /**
     * Recursively extract KB sections and return remaining base elements
     * 
     * @param \WP_Post $post The post being parsed
     * @param array $elements Array of Elementor elements
     * @param array &$sections_by_doc Reference to sections_by_doc array to populate
     * @param array &$sort_order_counter Reference to counter array ['value' => int]
     * @return array Elements that should go to base doc
     */
    private function extractElementorKbSections(\WP_Post $post, array $elements, array &$sections_by_doc, array &$sort_order_counter): array
    {
        $base_elements = [];

        foreach ($elements as $element) {
            $settings = $element['settings'] ?? [];
            $kb_enabled = $settings['kb_enabled'] ?? '';

            // Check if this element is KB-enabled
            if ($kb_enabled === 'yes') {
                $kb_mode = $settings['kb_mode'] ?? 'inherit';

                // Skip excluded sections
                if ($kb_mode === 'exclude') {
                    continue;
                }

                // Parse this KB section (including all its children)
                $section = $this->parseElementorKbSection($post, $element, $sort_order_counter['value']);

                if ($section) {
                    $doc_id = $section['doc_id'];

                    if (!isset($sections_by_doc[$doc_id])) {
                        $sections_by_doc[$doc_id] = [];
                    }

                    $sections_by_doc[$doc_id][] = $section;
                    $sort_order_counter['value']++;
                }
            } else {
                // This element goes to base, but check its children recursively
                if (!empty($element['elements'])) {
                    // Process child elements
                    $processed_children = $this->extractElementorKbSections(
                        $post,
                        $element['elements'],
                        $sections_by_doc,
                        $sort_order_counter
                    );

                    // Replace children with processed children
                    $element['elements'] = $processed_children;
                }

                $base_elements[] = $element;
            }
        }

        return $base_elements;
    }

    /**
     * Parse a single Elementor KB section element
     */
    private function parseElementorKbSection(\WP_Post $post, array $element, int $sort_order): ?array
    {
        $settings = $element['settings'] ?? [];

        // Extract KB settings
        $mode = $settings['kb_mode'] ?? 'inherit';
        $section_key = $settings['kb_section_key'] ?? null;
        $doc_key = $settings['kb_doc_key'] ?? null;
        $doc_title = $settings['kb_doc_title'] ?? null;
        $doc_description = $settings['kb_doc_description'] ?? null;
        $doc_post_url = $settings['kb_post_url']['url'] ?? null;
        $title = $settings['kb_title'] ?? null;
        $category = $settings['kb_category'] ?? null;
        $subcategory = $settings['kb_subcategory'] ?? null;
        $tags_string = $settings['kb_tags'] ?? null;
        $priority = $settings['kb_priority'] ?? null;

        // Parse tags if provided (comma-separated string)
        $tags = null;
        if (!empty($tags_string)) {
            $tags = array_map('trim', explode(',', $tags_string));
        }

        // Generate stable section ID
        $section_id = $section_key ?? $element['id'] ?? ('section-' . uniqid());

        // Determine doc_id
        if ($mode === 'separate_doc') {
            if (empty($doc_key)) {
                // Use section_id as doc_key if not provided
                $doc_key = $section_id;
            }
            $doc_id = "post-{$post->ID}/{$doc_key}";

            // For separate docs, doc_title is required, fallback to post title
            if (empty($doc_title)) {
                $doc_title = $post->post_title;
            }
        } else {
            $doc_id = "post-{$post->ID}/base";
        }

        // Render this element to HTML then convert to markdown
        $html = $this->renderElementorElements($post->ID, [$element]);
        $markdown = $this->converter->htmlToMarkdown($html);

        // Get category hierarchy if not overridden
        $category_hierarchy = $this->getPostCategoryHierarchy($post);

        // Build section data
        $section = [
            'post_id' => $post->ID,
            'doc_id' => $doc_id,
            'section_id' => $section_id,
            'mode' => $mode,
            'sort_order' => $sort_order,
            'title' => ($mode === 'separate_doc' ? $doc_title : ($title ?? $post->post_title)),
            'category' => $category ?? $category_hierarchy['category'],
            'subcategory' => $subcategory ?? $category_hierarchy['subcategory'],
            'tags' => $tags ?? $this->getPostTags($post),
            'md' => $markdown,
            'generated_at' => current_time('mysql'),
            'source_updated_at' => $post->post_modified,
            'extra_meta' => [
                'description' => $doc_description,
                'postUrl' => $doc_post_url,
                'source_type' => 'elementor',
                'priority' => $priority,
            ],
        ];

        $section['origin_hash'] = $this->calculateOriginHash($section);

        return $section;
    }

    /**
     * Render Elementor elements to HTML without modifying the post
     */
    private function renderElementorElements(int $post_id, array $elements): string
    {
        // If Elementor is not available, return empty
        if (!class_exists('\Elementor\Plugin')) {
            return '';
        }

        // If no elements, return empty
        if (empty($elements)) {
            return '';
        }

        try {
            // Use Elementor's rendering directly without modifying post meta
            $html = '';

            foreach ($elements as $element) {
                $html .= $this->renderElementorElement($element);
            }

            return $html;
        } catch (\Exception $e) {
            Logger::warning('Failed to render Elementor elements: ' . $e->getMessage(), [
                'exception' => get_class($e)
            ]);
            return '';
        }
    }

    /**
     * Render a single Elementor element recursively
     */
    private function renderElementorElement(array $element_data): string
    {
        if (!class_exists('\Elementor\Plugin')) {
            return '';
        }

        try {
            $element_type = $element_data['elType'] ?? '';

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

            // Get the element instance
            $element = \Elementor\Plugin::instance()->elements_manager->create_element_instance($element_data);

            if (!$element) {
                return '';
            }

            // Render the element
            ob_start();
            $element->print_element();
            $html = ob_get_clean();

            return $html;
        } catch (\Exception $e) {
            Logger::warning('Failed to render single Elementor element: ' . $e->getMessage(), [
                'exception' => get_class($e)
            ]);
            return '';
        }
    }

    /**
     * Parse Elementor post without KB sections (render to HTML)
     */
    private function parseElementorSimple(\WP_Post $post, array $elements): array
    {
        $doc_id = "post-{$post->ID}/base";

        // Try to get rendered HTML from Elementor
        $html = '';

        if (class_exists('\Elementor\Plugin')) {
            try {
                $html = \Elementor\Plugin::instance()->frontend->get_builder_content($post->ID, true);
            } catch (\Exception $e) {
                // Fallback to post content
                $html = $post->post_content;
            }
        } else {
            // Elementor not active, use post content
            $html = $post->post_content;
        }

        $markdown = $this->converter->htmlToMarkdown($html);

        $category_hierarchy = $this->getPostCategoryHierarchy($post);

        $section = [
            'post_id' => $post->ID,
            'doc_id' => $doc_id,
            'section_id' => 'main',
            'mode' => 'inherit',
            'sort_order' => 0,
            'title' => $post->post_title,
            'category' => $category_hierarchy['category'],
            'subcategory' => $category_hierarchy['subcategory'],
            'tags' => $this->getPostTags($post),
            'md' => $markdown,
            'generated_at' => current_time('mysql'),
            'source_updated_at' => $post->post_modified,
            'extra_meta' => [
                'description' => $this->getPostDescription($post),
                'postUrl' => $this->getPostUrl($post),
                'source_type' => 'elementor',
            ],
        ];

        $section['origin_hash'] = $this->calculateOriginHash($section);

        return [$doc_id => [$section]];
    }

    /**
     * Parse classic post (no blocks, no Elementor)
     */
    private function parseClassicPost(\WP_Post $post): array
    {
        $doc_id = "post-{$post->ID}/base";

        Logger::debug('Parsing classic post content', [
            'post_id' => $post->ID,
            'content_length' => strlen($post->post_content)
        ]);

        // Extract post references from shortcodes
        $this->extractShortcodeReferences($post->post_content);

        // Apply WordPress content filters (using core WP filter)
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
        $content = apply_filters('the_content', $post->post_content);

        $markdown = $this->converter->htmlToMarkdown($content);

        $category_hierarchy = $this->getPostCategoryHierarchy($post);

        $section = [
            'post_id' => $post->ID,
            'doc_id' => $doc_id,
            'section_id' => 'main',
            'mode' => 'inherit',
            'sort_order' => 0,
            'title' => $post->post_title,
            'category' => $category_hierarchy['category'],
            'subcategory' => $category_hierarchy['subcategory'],
            'tags' => $this->getPostTags($post),
            'md' => $markdown,
            'generated_at' => current_time('mysql'),
            'source_updated_at' => $post->post_modified,
            'extra_meta' => [
                'description' => $this->getPostDescription($post),
                'postUrl' => $this->getPostUrl($post),
            ],
        ];

        $section['origin_hash'] = $this->calculateOriginHash($section);

        return [$doc_id => [$section]];
    }

    /**
     * Get post category and subcategory from WordPress taxonomy
     * Returns array with 'category' and 'subcategory' (null if flat)
     */
    private function getPostCategoryHierarchy(\WP_Post $post): array
    {
        $categories = get_the_category($post->ID);

        if (empty($categories)) {
            return ['category' => null, 'subcategory' => null];
        }

        // Find the first category that has a parent
        foreach ($categories as $cat) {
            if ($cat->parent != 0) {
                $parent = get_category($cat->parent);
                if ($parent) {
                    return [
                        'category' => $parent->name,
                        'subcategory' => $cat->name
                    ];
                }
            }
        }

        // No hierarchical categories found, use first category
        return [
            'category' => $categories[0]->name,
            'subcategory' => null
        ];
    }

    /**
     * Get post tags as array
     */
    private function getPostTags(\WP_Post $post): ?array
    {
        $tags = get_the_tags($post->ID);

        if (empty($tags) || is_wp_error($tags)) {
            return null;
        }

        return array_map(fn($tag) => $tag->name, $tags);
    }

    /**
     * Get the best available post description/excerpt for metadata.
     */
    private function getPostDescription(\WP_Post $post): ?string
    {
        $description = has_excerpt($post->ID)
            ? get_the_excerpt($post->ID)
            : wp_trim_words(wp_strip_all_tags($post->post_content), 30, '...');

        $description = trim((string) $description);

        return $description !== '' ? $description : null;
    }

    /**
     * Get the canonical post URL when available.
     */
    private function getPostUrl(\WP_Post $post): ?string
    {
        $url = get_permalink($post);

        return is_string($url) && $url !== '' ? $url : null;
    }

    /**
     * Generate a stable section ID from block data
     */
    private function generateSectionId(array $block): string
    {
        // Try to use clientId if available
        if (!empty($block['attrs']['clientId'])) {
            return $block['attrs']['clientId'];
        }

        // Generate from content hash
        $content = $block['innerHTML'] ?? '';
        $inner_blocks = $block['innerBlocks'] ?? [];

        if (!empty($inner_blocks)) {
            $content .= serialize($inner_blocks);
        }

        return 'section-' . substr(md5($content), 0, 12);
    }

    /**
     * Calculate origin hash for a section
     */
    private function calculateOriginHash(array $section): string
    {
        $data = $section['md'] .
            ($section['title'] ?? '') .
            ($section['category'] ?? '') .
            json_encode($section['tags'] ?? []);

        return hash('sha256', $data);
    }
}
