<?php
declare(strict_types=1);

namespace ROITheme\Public\TableOfContents\Infrastructure\Ui;

use ROITheme\Shared\Domain\Contracts\RendererInterface;
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
use ROITheme\Shared\Domain\Entities\Component;
use ROITheme\Shared\Infrastructure\Services\PageVisibilityHelper;
use DOMDocument;
use DOMXPath;

/**
 * TableOfContentsRenderer - Renderiza tabla de contenido con navegacion automatica
 *
 * RESPONSABILIDAD: Generar HTML y CSS de la tabla de contenido
 *
 * CARACTERISTICAS:
 * - Generacion automatica desde headings del contenido
 * - ScrollSpy para navegacion activa
 * - Sticky positioning configurable
 * - Smooth scroll
 * - Estilos 100% desde BD via CSSGenerator
 *
 * Cumple con:
 * - DIP: Recibe CSSGeneratorInterface por constructor
 * - SRP: Una responsabilidad (renderizar TOC)
 * - Clean Architecture: Infrastructure puede usar WordPress
 *
 * @package ROITheme\Public\TableOfContents\Infrastructure\Ui
 */
final class TableOfContentsRenderer implements RendererInterface
{
    private const COMPONENT_NAME = 'table-of-contents';

    private array $headingCounter = [];

    public function __construct(
        private CSSGeneratorInterface $cssGenerator
    ) {}

    public function render(Component $component): string
    {
        $data = $component->getData();

        if (!$this->isEnabled($data)) {
            return '';
        }

        if (!PageVisibilityHelper::shouldShow(self::COMPONENT_NAME)) {
            return '';
        }

        $tocItems = $this->generateTocItems($data);

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

        $css = $this->generateCSS($data);
        $html = $this->buildHTML($data, $tocItems);
        $script = $this->buildScript($data);

        return sprintf("<style>%s</style>\n%s\n%s", $css, $html, $script);
    }

    public function supports(string $componentType): bool
    {
        return $componentType === self::COMPONENT_NAME;
    }

    private function isEnabled(array $data): bool
    {
        return ($data['visibility']['is_enabled'] ?? false) === true;
    }

    private function getVisibilityClasses(bool $desktop, bool $mobile): ?string
    {
        if (!$desktop && !$mobile) {
            return null;
        }
        if (!$desktop && $mobile) {
            return 'd-lg-none';
        }
        if ($desktop && !$mobile) {
            return 'd-none d-lg-block';
        }
        return '';
    }

    private function generateTocItems(array $data): array
    {
        $content = $data['content'] ?? [];
        $autoGenerate = $content['auto_generate'] ?? true;

        if (!$autoGenerate) {
            return [];
        }

        $headingLevelsStr = $content['heading_levels'] ?? 'h2,h3';
        $headingLevels = array_map('trim', explode(',', $headingLevelsStr));

        return $this->generateTocFromContent($headingLevels);
    }

    private function generateTocFromContent(array $headingLevels): array
    {
        global $post;

        if (!$post || empty($post->post_content)) {
            return [];
        }

        $content = apply_filters('the_content', $post->post_content);

        $dom = new DOMDocument();
        libxml_use_internal_errors(true);
        $dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);
        libxml_clear_errors();

        $xpath = new DOMXPath($dom);
        $tocItems = [];

        $xpathQuery = implode(' | ', array_map(function($level) {
            return '//' . $level;
        }, $headingLevels));

        $headings = $xpath->query($xpathQuery);

        if ($headings->length === 0) {
            return [];
        }

        foreach ($headings as $heading) {
            $tagName = strtolower($heading->tagName);
            $level = intval(substr($tagName, 1));

            $text = trim($heading->textContent);

            if (empty($text)) {
                continue;
            }

            $existingId = $heading->getAttribute('id');

            // Generar anchor ID - la inyección de IDs se hace via JavaScript
            // para evitar problemas con orden de filtros WordPress y caché
            if (empty($existingId)) {
                $anchor = $this->generateAnchorId($text);
            } else {
                $anchor = $existingId;
            }

            $tocItems[] = [
                'text' => $text,
                'anchor' => $anchor,
                'level' => $level
            ];
        }

        return $tocItems;
    }

    private function generateAnchorId(string $text): string
    {
        $id = strtolower($text);
        $id = remove_accents($id);
        $id = preg_replace('/[^a-z0-9]+/', '-', $id);
        $id = trim($id, '-');

        $baseId = $id;
        $count = 1;

        while (isset($this->headingCounter[$id])) {
            $id = $baseId . '-' . $count;
            $count++;
        }

        $this->headingCounter[$id] = true;

        return $id;
    }

    private function addIdToHeading(string $headingText, string $anchorId): void
    {
        add_filter('the_content', function($content) use ($headingText, $anchorId) {
            $pattern = '/<(h[2-6])([^>]*)>(\s*)' . preg_quote($headingText, '/') . '(\s*)<\/\1>/i';
            $replacement = '<$1 id="' . esc_attr($anchorId) . '"$2>$3' . $headingText . '$4</$1>';
            return preg_replace($pattern, $replacement, $content, 1);
        }, 20);
    }

    public function generateCSS(array $data): string
    {
        $colors = $data['colors'] ?? [];
        $spacing = $data['spacing'] ?? [];
        $typography = $data['typography'] ?? [];
        $effects = $data['visual_effects'] ?? [];
        $behavior = $data['behavior'] ?? [];
        $visibility = $data['visibility'] ?? [];

        $cssRules = [];

        // Container styles - Flexbox layout for proper scrolling
        $cssRules[] = $this->cssGenerator->generate('.toc-container', [
            'background-color' => $colors['background_color'] ?? '#ffffff',
            'border' => ($effects['border_width'] ?? '1px') . ' solid ' . ($colors['border_color'] ?? '#E6E9ED'),
            'border-radius' => $effects['border_radius'] ?? '8px',
            'box-shadow' => $effects['box_shadow'] ?? '0 2px 8px rgba(0, 0, 0, 0.08)',
            'padding' => $spacing['container_padding'] ?? '12px 16px',
            'margin-bottom' => $spacing['margin_bottom'] ?? '13px',
            'max-height' => $behavior['max_height'] ?? 'calc(100vh - 71px - 10px - 250px - 15px - 15px)',
            'display' => 'flex',
            'flex-direction' => 'column',
            'overflow' => 'visible',
        ]);

        // Sticky behavior - aplica al wrapper .sidebar-sticky de single.php
        // NO al .toc-container individual (ver template líneas 817-835)
        if (($behavior['is_sticky'] ?? true)) {
            $cssRules[] = $this->cssGenerator->generate('.sidebar-sticky', [
                'position' => 'sticky',
                'top' => '85px',
                'display' => 'flex',
                'flex-direction' => 'column',
            ]);
        }

        // Custom scrollbar
        $cssRules[] = $this->cssGenerator->generate('.toc-container::-webkit-scrollbar', [
            'width' => $spacing['scrollbar_width'] ?? '6px',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container::-webkit-scrollbar-track', [
            'background' => $colors['scrollbar_track_color'] ?? '#F9FAFB',
            'border-radius' => $effects['scrollbar_border_radius'] ?? '3px',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container::-webkit-scrollbar-thumb', [
            'background' => $colors['scrollbar_thumb_color'] ?? '#6B7280',
            'border-radius' => $effects['scrollbar_border_radius'] ?? '3px',
        ]);

        // Title styles - Color #1e3a5f = navy-primary del Design System
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-title', [
            'font-size' => $typography['title_font_size'] ?? '1rem',
            'font-weight' => $typography['title_font_weight'] ?? '600',
            'color' => $colors['title_color'] ?? '#1e3a5f',
            'padding-bottom' => $spacing['title_padding_bottom'] ?? '8px',
            'margin-bottom' => $spacing['title_margin_bottom'] ?? '0.75rem',
            'border-bottom' => '2px solid ' . ($colors['title_border_color'] ?? '#E6E9ED'),
            'margin-top' => '0',
        ]);

        // List styles - Scrollable area with flex
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list', [
            'margin' => '0',
            'padding' => '0',
            'padding-right' => '0.5rem',
            'list-style' => 'none',
            'overflow-y' => 'auto',
            'flex' => '1',
            'min-height' => '0',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list li', [
            'margin-bottom' => $spacing['item_margin_bottom'] ?? '0.15rem',
        ]);

        // Link styles - Color #495057 = neutral-600 del template
        $transitionDuration = $effects['transition_duration'] ?? '0.3s';
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-link', [
            'display' => 'block',
            'font-size' => $typography['link_font_size'] ?? '0.9rem',
            'line-height' => $typography['link_line_height'] ?? '1.3',
            'color' => $colors['link_color'] ?? '#495057',
            'text-decoration' => 'none',
            'padding' => $spacing['link_padding'] ?? '0.3rem 0.85rem',
            'border-radius' => $effects['link_border_radius'] ?? '4px',
            'border-left' => ($effects['active_border_left_width'] ?? '3px') . ' solid transparent',
            'transition' => "all {$transitionDuration} ease",
        ]);

        // Link hover - Color #1e3a5f = navy-primary del Design System
        // Template: background, border-left-color, color
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-link:hover', [
            'color' => $colors['link_hover_color'] ?? '#1e3a5f',
            'background-color' => $colors['link_hover_background'] ?? '#F9FAFB',
            'border-left-color' => $colors['active_border_color'] ?? '#1e3a5f',
        ]);

        // Active link - Color #1e3a5f = navy-primary del Design System
        // Template: font-weight: 600
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-link.active', [
            'color' => $colors['active_text_color'] ?? '#1e3a5f',
            'background-color' => $colors['active_background_color'] ?? '#F9FAFB',
            'border-left-color' => $colors['active_border_color'] ?? '#1e3a5f',
            'font-weight' => '600',
        ]);

        // Focus - eliminar outline azul del browser en click
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-link:focus', [
            'outline' => 'none',
        ]);

        // Level indentation
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-level-3 .toc-link', [
            'padding-left' => $spacing['level_three_padding_left'] ?? '1.5rem',
            'font-size' => $typography['level_three_font_size'] ?? '0.85rem',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-level-4 .toc-link', [
            'padding-left' => $spacing['level_four_padding_left'] ?? '2rem',
            'font-size' => $typography['level_four_font_size'] ?? '0.8rem',
        ]);

        // Scrollbar for toc-list
        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list::-webkit-scrollbar', [
            'width' => $spacing['scrollbar_width'] ?? '6px',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list::-webkit-scrollbar-track', [
            'background' => $colors['scrollbar_track_color'] ?? '#F9FAFB',
            'border-radius' => $effects['scrollbar_border_radius'] ?? '3px',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list::-webkit-scrollbar-thumb', [
            'background' => $colors['scrollbar_thumb_color'] ?? '#6B7280',
            'border-radius' => $effects['scrollbar_border_radius'] ?? '3px',
        ]);

        $cssRules[] = $this->cssGenerator->generate('.toc-container .toc-list::-webkit-scrollbar-thumb:hover', [
            'background' => $colors['active_border_color'] ?? '#1e3a5f',
        ]);

        // Responsive visibility
        $showOnDesktop = $visibility['show_on_desktop'] ?? true;
        $showOnMobile = $visibility['show_on_mobile'] ?? false;

        if (!$showOnMobile) {
            $cssRules[] = "@media (max-width: 991.98px) {
                .toc-container { display: none !important; }
            }";
        }

        if (!$showOnDesktop) {
            $cssRules[] = "@media (min-width: 992px) {
                .toc-container { display: none !important; }
            }";
        }

        // Responsive layout adjustments
        $cssRules[] = "@media (max-width: 991px) {
            .sidebar-sticky {
                position: relative !important;
                top: 0 !important;
            }
            .toc-container {
                margin-bottom: 2rem;
            }
            .toc-container .toc-list {
                max-height: 300px;
            }
        }";

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

    private function buildHTML(array $data, array $tocItems): string
    {
        $content = $data['content'] ?? [];

        $title = $content['title'] ?? 'Tabla de Contenido';

        // NOTA: El sticky behavior se maneja en el wrapper .sidebar-sticky de single.php
        // El TOC no debe tener la clase sidebar-sticky - está dentro del wrapper
        $html = '<div class="toc-container">';

        $html .= sprintf(
            '<span class="toc-title d-block h4">%s</span>',
            esc_html($title)
        );

        $html .= '<ol class="list-unstyled toc-list">';

        foreach ($tocItems as $item) {
            $text = $item['text'] ?? '';
            $anchor = $item['anchor'] ?? '';
            $level = $item['level'] ?? 2;

            if (empty($text) || empty($anchor)) {
                continue;
            }

            $indentClass = $level > 2 ? 'toc-level-' . $level : '';

            $html .= sprintf(
                '<li class="%s"><a href="#%s" class="toc-link" data-level="%d">%s</a></li>',
                esc_attr($indentClass),
                esc_attr($anchor),
                intval($level),
                esc_html($text)
            );
        }

        $html .= '</ol>';
        $html .= '</div>';

        return $html;
    }

    private function buildScript(array $data): string
    {
        $content = $data['content'] ?? [];
        $behavior = $data['behavior'] ?? [];

        $smoothScroll = $content['smooth_scroll'] ?? true;
        $scrollOffset = intval($behavior['scroll_offset'] ?? 100);

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

        // Script con inyección de IDs client-side para mayor fiabilidad
        // Resuelve problemas de orden de filtros WordPress y caché
        $script = <<<JS
<script>
document.addEventListener('DOMContentLoaded', function() {
    var tocLinks = document.querySelectorAll('.toc-link');
    var offsetTop = {$scrollOffset};

    // ===========================================
    // PASO 1: Generar IDs para headings sin ID
    // ===========================================
    function generateSlug(text) {
        return text
            .toLowerCase()
            .normalize('NFD')
            .replace(/[\u0300-\u036f]/g, '') // Remover acentos
            .replace(/[^a-z0-9]+/g, '-')     // Reemplazar caracteres especiales
            .replace(/^-+|-+$/g, '');        // Trim guiones
    }

    // Mapeo de IDs existentes para evitar duplicados
    var existingIds = {};
    document.querySelectorAll('[id]').forEach(function(el) {
        existingIds[el.id] = true;
    });

    // Para cada link del TOC, buscar el heading correspondiente y asignarle ID
    tocLinks.forEach(function(link) {
        var targetId = link.getAttribute('href').substring(1); // Remover #
        var existingElement = document.getElementById(targetId);

        // Si ya existe el elemento con ese ID, no hacer nada
        if (existingElement) return;

        // Buscar el heading por su texto
        var linkText = link.textContent.trim();
        var headings = document.querySelectorAll('h2, h3, h4, h5, h6');

        headings.forEach(function(heading) {
            // Comparar texto normalizado
            var headingText = heading.textContent.trim();
            if (headingText === linkText && !heading.id) {
                // Asignar el ID que espera el TOC
                heading.id = targetId;
                existingIds[targetId] = true;
            }
        });
    });

    // ===========================================
    // PASO 2: Smooth scroll on click
    // ===========================================
    tocLinks.forEach(function(link) {
        link.addEventListener('click', function(e) {
            e.preventDefault();
            var targetId = this.getAttribute('href');
            var targetElement = document.querySelector(targetId);

            if (targetElement) {
                var elementPosition = targetElement.getBoundingClientRect().top;
                var offsetPosition = elementPosition + window.pageYOffset - offsetTop;

                window.scrollTo({
                    top: offsetPosition,
                    behavior: 'smooth'
                });
            }
        });
    });

    // ===========================================
    // PASO 3: ScrollSpy con Intersection Observer
    // ===========================================
    var sections = [];
    var sectionMap = {};

    tocLinks.forEach(function(link) {
        var id = link.getAttribute('href').substring(1);
        var section = document.getElementById(id);
        if (section) {
            sections.push(section);
            sectionMap[id] = link;
        }
    });

    if (sections.length === 0) return;

    var visibleSections = new Set();
    var currentActive = null;

    function updateActiveFromVisible() {
        if (visibleSections.size === 0) return;

        var topMostSection = null;
        var topMostPosition = Infinity;

        visibleSections.forEach(function(id) {
            var section = document.getElementById(id);
            if (section) {
                var rect = section.getBoundingClientRect();
                if (rect.top < topMostPosition) {
                    topMostPosition = rect.top;
                    topMostSection = id;
                }
            }
        });

        if (topMostSection && topMostSection !== currentActive) {
            tocLinks.forEach(function(link) {
                link.classList.remove('active');
            });

            if (sectionMap[topMostSection]) {
                sectionMap[topMostSection].classList.add('active');
                currentActive = topMostSection;

                // Auto-scroll del TOC para mostrar el elemento activo
                sectionMap[topMostSection].scrollIntoView({
                    behavior: 'smooth',
                    block: 'nearest'
                });
            }
        }
    }

    var observerOptions = {
        root: null,
        rootMargin: '-' + offsetTop + 'px 0px -50% 0px',
        threshold: 0
    };

    var observer = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
            var id = entry.target.id;
            if (entry.isIntersecting) {
                visibleSections.add(id);
            } else {
                visibleSections.delete(id);
            }
        });

        requestAnimationFrame(updateActiveFromVisible);
    }, observerOptions);

    sections.forEach(function(section) {
        observer.observe(section);
    });

    // Activar primera sección al cargar
    requestAnimationFrame(function() {
        if (visibleSections.size === 0 && sections.length > 0) {
            var firstId = sections[0].id;
            if (sectionMap[firstId]) {
                sectionMap[firstId].classList.add('active');
                currentActive = firstId;
            }
        }
    });
});
</script>
JS;

        return $script;
    }
}
