<?php

declare(strict_types=1);

final class MyronPublicNavigationRenderer
{
    /**
     * @param list<array<string, mixed>> $navigation
     * @param array<string, string> $settings
     */
    public function renderHeader(
        string $siteTitle,
        string $basePath,
        array $navigation,
        string $currentPath = '/',
        array $settings = [],
    ): string {
        return $this->renderContext([
            'variant' => 'full',
            'settings' => $settings,
            'brand' => [
                'title' => $siteTitle,
                'subtitle' => $settings['brand_subtitle'] ?? '',
                'display' => $settings['brand_display'] ?? 'title',
                'logo_url' => $settings['logo_url'] ?? '',
                'logo_light_url' => $settings['logo_light_url'] ?? '',
                'logo_dark_url' => $settings['logo_dark_url'] ?? '',
                'logo_alt' => $settings['logo_alt'] ?? '',
            ],
            'primaryNavigation' => $navigation,
            'utilityActions' => [],
            'features' => [
                'brand' => true,
                'primaryMenu' => true,
                'utilityActions' => false,
            ],
        ], $basePath, $currentPath);
    }

    /**
     * @param array<string, mixed> $context
     */
    public function renderContext(array $context, string $basePath, string $currentPath = '/'): string
    {
        if ((string) ($context['variant'] ?? 'full') === 'hidden') {
            return '';
        }

        $settings = $this->normalizeSettings(is_array($context['settings'] ?? null) ? $context['settings'] : []);
        $brand = is_array($context['brand'] ?? null) ? $context['brand'] : [];
        $features = is_array($context['features'] ?? null) ? $context['features'] : [];
        $creative = is_array($context['creative'] ?? null) ? $context['creative'] : [];
        $navigation = is_array($context['primaryNavigation'] ?? null) ? $context['primaryNavigation'] : [];
        $utilityActions = is_array($context['utilityActions'] ?? null) ? $context['utilityActions'] : [];
        $siteTitle = trim((string) ($brand['title'] ?? '')) ?: 'MyronCMS';
        $showBrand = (bool) ($features['brand'] ?? true);
        $showPrimary = (bool) ($features['primaryMenu'] ?? true);
        $showUtility = (bool) ($features['utilityActions'] ?? false) && $utilityActions !== [];
        $showAppearanceToggle = $settings['appearance_mode_policy'] === 'auto';
        $hasPanel = $showPrimary || $showUtility;
        $panelId = 'myron-site-primary-navigation';
        $creativeNavStyle = myronNormalizeSlug((string) ($creative['navStyle'] ?? ''));
        $creativeNavParams = $this->normalizeNavParams($creativeNavStyle, is_array($creative['navParams'] ?? null) ? $creative['navParams'] : []);
        $classes = [
            'myron-site__masthead',
            'myron-site__masthead--align-' . $settings['desktop_alignment'],
            'myron-site__masthead--menu-' . $settings['menu_alignment'],
            'myron-site__masthead--density-' . $settings['density'],
            'myron-site__masthead--surface-' . $settings['surface_treatment'],
            'myron-site__masthead--position-' . $settings['position_behavior'],
            'myron-site__masthead--mobile-' . $settings['mobile_mode'],
            'myron-site__masthead--variant-' . ((string) ($context['variant'] ?? 'full')),
        ];
        if ($creativeNavStyle !== '') {
            $classes[] = 'mc-nav--' . $creativeNavStyle;
            if ($creativeNavStyle === 'floating-nav') {
                $classes[] = 'mc-nav--radius-' . $creativeNavParams['navRadius'];
            }
        }

        $html = [];
        $navParamsJson = $creativeNavParams !== [] ? json_encode($creativeNavParams, JSON_UNESCAPED_SLASHES) : '';
        $html[] = '<header class="' . $this->escape(implode(' ', $classes)) . '" data-myron-public-navigation data-mobile-mode="' . $this->escape($settings['mobile_mode']) . '" data-appearance-mode-policy="' . $this->escape($settings['appearance_mode_policy']) . '"' . ($creativeNavStyle !== '' ? ' data-mc-nav-style="' . $this->escape($creativeNavStyle) . '"' : '') . (is_string($navParamsJson) && $navParamsJson !== '' ? ' data-mc-nav-params="' . $this->escape($navParamsJson) . '"' : '') . '>';
        if ($showBrand) {
            $html[] = $this->renderBrand($brand, $basePath, $siteTitle);
        }
        if ($showAppearanceToggle && !$hasPanel) {
            $html[] = $this->renderAppearanceToggle();
        }
        if ($hasPanel) {
            $html[] = '  <button class="myron-site__nav-toggle" type="button" aria-controls="' . $this->escape($panelId) . '" aria-expanded="false" data-myron-public-nav-toggle>';
            $html[] = '    <span class="myron-site__nav-toggle-bars" aria-hidden="true"></span>';
            $html[] = '    <span class="myron-site__nav-toggle-label">Menu</span>';
            $html[] = '  </button>';
            $html[] = '  <div class="myron-site__nav-panel" id="' . $this->escape($panelId) . '" data-myron-public-nav-panel data-state="open">';
            $html[] = '    <div class="myron-site__nav-panel-header">';
            $html[] = '      <span class="myron-site__nav-panel-title">' . $this->escape($siteTitle) . '</span>';
            $html[] = '      <button class="myron-site__nav-close" type="button" data-myron-public-nav-close aria-label="Close menu">Close</button>';
            $html[] = '    </div>';
        }

        if ($showPrimary) {
            $html[] = '    <nav class="myron-site__nav" aria-label="Primary navigation">';

            if ($navigation === []) {
                $html[] = '      <span class="myron-site__nav-empty">No primary navigation items are published.</span>';
            } else {
                $html[] = $this->renderNavigationList($navigation, $basePath, $currentPath, 0, '      ');
            }

            $html[] = '    </nav>';
        }

        if ($showUtility) {
            $html[] = '    <nav class="myron-site__utility" aria-label="Header actions">';
            foreach ($utilityActions as $action) {
                if (!is_array($action)) {
                    continue;
                }
                $label = trim((string) ($action['label'] ?? ''));
                $href = trim((string) ($action['href'] ?? ''));
                if ($label === '' || $href === '') {
                    continue;
                }
                $html[] = '      <a href="' . $this->escape($this->linkHref($basePath, $href)) . '">' . $this->escape($label) . '</a>';
            }
            $html[] = '    </nav>';
        }
        if ($showAppearanceToggle && $hasPanel) {
            $html[] = $this->renderAppearanceToggle('    ');
        }

        if ($hasPanel) {
            $html[] = '  </div>';
        }
        $html[] = '</header>';
        if ($creativeNavStyle === 'mobile-bottom-nav' && $showPrimary && $navigation !== []) {
            $html[] = $this->renderBottomBar($navigation, $basePath, $currentPath);
        }

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

    /**
     * @param list<array<string, mixed>> $items
     */
    private function renderNavigationList(array $items, string $basePath, string $currentPath, int $level, string $indent): string
    {
        $classes = [
            'myron-site__nav-list',
            'myron-site__nav-list--level-' . (string) min(max(0, $level), 6),
        ];
        $attributes = ' class="' . $this->escape(implode(' ', $classes)) . '"';
        if ($level > 0) {
            $attributes .= ' data-myron-public-submenu data-state="closed"';
        }

        $html = [$indent . '<ul' . $attributes . '>'];
        foreach (array_values($items) as $index => $item) {
            if (!is_array($item)) {
                continue;
            }

            $html[] = $this->renderNavigationItem($item, $basePath, $currentPath, $level, $index, $indent . '  ');
        }
        $html[] = $indent . '</ul>';

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

    /**
     * @param array<string, mixed> $item
     */
    private function renderNavigationItem(array $item, string $basePath, string $currentPath, int $level, int $index, string $indent): string
    {
        $children = $this->navigationChildren($item);
        $path = (string) ($item['path'] ?? $item['page_path'] ?? '/');
        $label = $this->navigationLabel($item);
        $isCurrent = myronNormalizePath($path) === myronNormalizePath($currentPath);
        $hasCurrentDescendant = $children !== [] && $this->navigationTreeContainsCurrent($children, $currentPath);
        $itemClasses = [
            'myron-site__nav-item',
            'myron-site__nav-item--level-' . (string) min(max(0, $level), 6),
        ];
        if ($children !== []) {
            $itemClasses[] = 'myron-site__nav-item--has-children';
        }
        if ($isCurrent || $hasCurrentDescendant) {
            $itemClasses[] = 'myron-site__nav-item--active';
        }

        $html = [$indent . '<li class="' . $this->escape(implode(' ', $itemClasses)) . '">'];
        $html[] = $indent . '  <div class="myron-site__nav-item-row">';
        $html[] = $indent . '    <a class="myron-site__nav-link" href="' . $this->escape($this->linkHref($basePath, $path)) . '"' . ($isCurrent ? ' aria-current="page"' : '') . '>' . $this->escape($label) . '</a>';

        if ($children !== []) {
            $submenuId = $this->submenuId($item, $level, $index);
            $html[] = $indent . '    <button class="myron-site__submenu-toggle" type="button" aria-label="Toggle ' . $this->escape($label) . ' submenu" aria-controls="' . $this->escape($submenuId) . '" aria-expanded="false" data-myron-public-submenu-toggle>';
            $html[] = $indent . '      <span class="myron-site__submenu-toggle-icon" aria-hidden="true">+</span>';
            $html[] = $indent . '    </button>';
        }

        $html[] = $indent . '  </div>';

        if ($children !== []) {
            $childMarkup = $this->renderNavigationList($children, $basePath, $currentPath, $level + 1, $indent . '  ');
            $childMarkup = preg_replace('/<ul class="/', '<ul id="' . $this->escape($submenuId) . '" class="', $childMarkup, 1);
            $html[] = is_string($childMarkup) ? $childMarkup : $this->renderNavigationList($children, $basePath, $currentPath, $level + 1, $indent . '  ');
        }

        $html[] = $indent . '</li>';

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

    /**
     * @param array<string, mixed> $item
     * @return list<array<string, mixed>>
     */
    private function navigationChildren(array $item): array
    {
        $children = $item['children'] ?? [];
        if (!is_array($children)) {
            return [];
        }

        return array_values(array_filter($children, static fn (mixed $child): bool => is_array($child)));
    }

    /**
     * @param array<string, mixed> $item
     */
    private function navigationLabel(array $item): string
    {
        $label = trim((string) ($item['label'] ?? ''));
        if ($label !== '') {
            return $label;
        }

        return trim((string) ($item['page_title'] ?? 'Untitled page')) ?: 'Untitled page';
    }

    /**
     * @param list<array<string, mixed>> $items
     */
    private function navigationTreeContainsCurrent(array $items, string $currentPath): bool
    {
        foreach ($items as $item) {
            $path = (string) ($item['path'] ?? $item['page_path'] ?? '/');
            if (myronNormalizePath($path) === myronNormalizePath($currentPath)) {
                return true;
            }

            if ($this->navigationTreeContainsCurrent($this->navigationChildren($item), $currentPath)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array<string, mixed> $item
     */
    private function submenuId(array $item, int $level, int $index): string
    {
        $seed = trim((string) ($item['id'] ?? ''));
        if ($seed === '') {
            $seed = (string) ($item['path'] ?? $item['page_path'] ?? '') . '-' . $level . '-' . $index;
        }

        $normalized = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $seed);
        $normalized = is_string($normalized) ? trim($normalized, '-') : '';
        if ($normalized === '') {
            $normalized = substr(sha1($seed), 0, 10);
        }

        return 'myron-site-submenu-' . $normalized;
    }

    /**
     * @param array<string, string> $settings
     * @return array<string, string>
     */
    private function normalizeSettings(array $settings): array
    {
        $defaults = [
            'desktop_alignment' => 'split',
            'menu_alignment' => 'right',
            'brand_display' => 'title',
            'brand_subtitle' => '',
            'logo_url' => '',
            'logo_light_url' => '',
            'logo_dark_url' => '',
            'logo_alt' => '',
            'utility_actions_display' => 'auto',
            'appearance_mode_policy' => 'auto',
            'density' => 'comfortable',
            'mobile_mode' => 'takeover',
            'surface_treatment' => 'bordered',
            'position_behavior' => 'static',
        ];

        $allowed = [
            'desktop_alignment' => ['split', 'centered', 'end'],
            'menu_alignment' => ['left', 'center', 'right'],
            'brand_display' => ['title', 'title-subtitle', 'logo', 'lockup'],
            'utility_actions_display' => ['auto', 'show', 'hide'],
            'appearance_mode_policy' => ['auto', 'light', 'dark'],
            'density' => ['compact', 'comfortable', 'spacious'],
            'mobile_mode' => ['takeover'],
            'surface_treatment' => ['plain', 'bordered', 'elevated'],
            'position_behavior' => ['static', 'sticky'],
        ];

        foreach ($defaults as $key => $default) {
            $value = trim((string) ($settings[$key] ?? $default));
            $defaults[$key] = !isset($allowed[$key]) || in_array($value, $allowed[$key], true) ? $value : $default;
        }

        return $defaults;
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, string>
     */
    private function normalizeNavParams(string $navStyle, array $params): array
    {
        if ($navStyle !== 'floating-nav') {
            return [];
        }

        $radius = myronNormalizeSlug((string) ($params['navRadius'] ?? 'pill'));
        if (!in_array($radius, ['pill', 'lg', 'md', 'sm', 'none'], true)) {
            $radius = 'pill';
        }

        return ['navRadius' => $radius];
    }

    /**
     * @param array<string, mixed> $brand
     */
    private function renderBrand(array $brand, string $basePath, string $siteTitle): string
    {
        $display = trim((string) ($brand['display'] ?? 'title')) ?: 'title';
        $logoUrl = trim((string) ($brand['logo_url'] ?? ''));
        $lightLogoUrl = trim((string) ($brand['logo_light_url'] ?? '')) ?: $logoUrl;
        $darkLogoUrl = trim((string) ($brand['logo_dark_url'] ?? '')) ?: $logoUrl;
        if ($lightLogoUrl === '' && $darkLogoUrl !== '') {
            $lightLogoUrl = $darkLogoUrl;
        }
        if ($darkLogoUrl === '' && $lightLogoUrl !== '') {
            $darkLogoUrl = $lightLogoUrl;
        }
        $logoAlt = trim((string) ($brand['logo_alt'] ?? '')) ?: $siteTitle;
        $lightLogoAlt = trim((string) ($brand['logo_light_alt'] ?? '')) ?: $logoAlt;
        $darkLogoAlt = trim((string) ($brand['logo_dark_alt'] ?? '')) ?: $logoAlt;
        $subtitle = trim((string) ($brand['subtitle'] ?? ''));
        $parts = [];

        if (($display === 'logo' || $display === 'lockup') && $lightLogoUrl !== '') {
            $logoParts = [];
            $logoFrameAttributes = $this->logoFrameAttributes($brand);
            $lightDimAttrs = $this->logoDimensionAttributes($brand, 'light');
            $darkDimAttrs = $this->logoDimensionAttributes($brand, 'dark');
            if ($lightLogoUrl === $darkLogoUrl) {
                $logoParts[] = '<img class="myron-site__brand-logo" src="' . $this->escape($this->assetHref($basePath, $lightLogoUrl)) . '" alt="' . $this->escape($lightLogoAlt) . '"' . $lightDimAttrs . ' loading="eager" decoding="async" fetchpriority="high" data-myron-public-logo>';
            } else {
                $logoParts[] = '<img class="myron-site__brand-logo myron-site__brand-logo--mode-light" src="' . $this->escape($this->assetHref($basePath, $lightLogoUrl)) . '" alt="' . $this->escape($lightLogoAlt) . '"' . $lightDimAttrs . ' loading="eager" decoding="async" data-myron-public-logo>';
                $logoParts[] = '<img class="myron-site__brand-logo myron-site__brand-logo--mode-dark" src="' . $this->escape($this->assetHref($basePath, $darkLogoUrl)) . '" alt="' . $this->escape($darkLogoAlt) . '"' . $darkDimAttrs . ' loading="eager" decoding="async" data-myron-public-logo>';
            }
            $parts[] = '<span class="myron-site__brand-logo-frame"' . $logoFrameAttributes . '>' . implode('', $logoParts) . '</span>';
        }

        if ($display !== 'logo' || $lightLogoUrl === '') {
            $parts[] = '<span class="myron-site__brand-title">' . $this->escape($siteTitle) . '</span>';
        } elseif ($display === 'lockup') {
            $parts[] = '<span class="myron-site__brand-title">' . $this->escape($siteTitle) . '</span>';
        }

        if (($display === 'title-subtitle' || $display === 'lockup') && $subtitle !== '') {
            $parts[] = '<span class="myron-site__brand-subtitle">' . $this->escape($subtitle) . '</span>';
        }

        return '  <a class="myron-site__brand myron-site__brand--display-' . $this->escape($display) . '" href="' . $this->escape($this->routeHref($basePath, '/')) . '">' . implode('', $parts) . '</a>';
    }

    /**
     * @param array<string, mixed> $brand
     */
    private function logoFrameAttributes(array $brand): string
    {
        $candidates = [];
        foreach ([
            ['width' => $brand['logo_light_width'] ?? 0, 'height' => $brand['logo_light_height'] ?? 0],
            ['width' => $brand['logo_dark_width'] ?? 0, 'height' => $brand['logo_dark_height'] ?? 0],
        ] as $candidate) {
            $width = max(0, (int) ($candidate['width'] ?? 0));
            $height = max(0, (int) ($candidate['height'] ?? 0));
            if ($width <= 0 || $height <= 0) {
                continue;
            }
            $candidates[] = [
                'width' => $width,
                'height' => $height,
                'ratio' => $width / $height,
            ];
        }

        if ($candidates === []) {
            return '';
        }

        usort($candidates, static fn (array $left, array $right): int => $right['ratio'] <=> $left['ratio']);
        $selected = $candidates[0];

        return ' style="--myron-site-logo-aspect-ratio: ' . $this->escape((string) $selected['width']) . ' / ' . $this->escape((string) $selected['height']) . ';"';
    }

    /**
     * @param array<string, mixed> $brand
     */
    private function logoDimensionAttributes(array $brand, string $variant): string
    {
        $widthKey = $variant === 'dark' ? 'logo_dark_width' : 'logo_light_width';
        $heightKey = $variant === 'dark' ? 'logo_dark_height' : 'logo_light_height';
        $width = max(0, (int) ($brand[$widthKey] ?? 0));
        $height = max(0, (int) ($brand[$heightKey] ?? 0));
        if ($width <= 0 || $height <= 0) {
            return '';
        }

        return ' width="' . $this->escape((string) $width) . '" height="' . $this->escape((string) $height) . '"';
    }

    private function renderAppearanceToggle(string $indent = '  '): string
    {
        return $indent . '<button class="myron-site__appearance-toggle" type="button" aria-label="Toggle appearance mode" title="Toggle appearance mode" data-myron-appearance-toggle data-appearance-state="">' .
            '<span class="myron-site__appearance-toggle-icon myron-site__appearance-toggle-icon--light" aria-hidden="true"><img class="myron-site__appearance-toggle-image" src="' . $this->escape(myronBuildAssetUrl('icons/bootstrap/sun.svg')) . '" alt=""></span>' .
            '<span class="myron-site__appearance-toggle-icon myron-site__appearance-toggle-icon--dark" aria-hidden="true"><img class="myron-site__appearance-toggle-image" src="' . $this->escape(myronBuildAssetUrl('icons/bootstrap/moon-fill.svg')) . '" alt=""></span>' .
            '</button>';
    }

    /**
     * @param list<array<string, mixed>> $items
     */
    private function renderBottomBar(array $items, string $basePath, string $currentPath): string
    {
        $html = ['<nav class="mc-nav-bottom-bar" aria-label="Primary mobile navigation">'];
        foreach (array_slice(array_values($items), 0, 5) as $item) {
            if (!is_array($item)) {
                continue;
            }
            $path = (string) ($item['path'] ?? $item['page_path'] ?? '/');
            $label = $this->navigationLabel($item);
            $isCurrent = myronNormalizePath($path) === myronNormalizePath($currentPath);
            $html[] = '  <a class="mc-nav-bottom-bar__item' . ($isCurrent ? ' mc-nav-bottom-bar__item--active' : '') . '" href="' . $this->escape($this->linkHref($basePath, $path)) . '"' . ($isCurrent ? ' aria-current="page"' : '') . '>';
            $html[] = '    <span class="mc-nav-bottom-bar__icon" aria-hidden="true">' . $this->bottomBarIcon($label) . '</span>';
            $html[] = '    <span class="mc-nav-bottom-bar__label">' . $this->escape($label) . '</span>';
            $html[] = '  </a>';
        }
        $html[] = '</nav>';

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

    private function bottomBarIcon(string $label): string
    {
        $normalized = myronNormalizeSlug($label);
        if (str_contains($normalized, 'home')) {
            return '&#8962;';
        }
        if (str_contains($normalized, 'contact') || str_contains($normalized, 'email')) {
            return '&#9993;';
        }
        if (str_contains($normalized, 'search')) {
            return '&#128269;';
        }
        if (str_contains($normalized, 'blog') || str_contains($normalized, 'news')) {
            return '&#128196;';
        }
        if (str_contains($normalized, 'shop') || str_contains($normalized, 'cart')) {
            return '&#9673;';
        }

        return '&#9679;';
    }

    private function linkHref(string $basePath, string $href): string
    {
        $trimmed = trim($href);
        if ($trimmed === '') {
            return $this->routeHref($basePath, '/');
        }
        if (str_starts_with($trimmed, '#') || preg_match('/^https?:\/\//i', $trimmed) === 1) {
            return $trimmed;
        }
        if (str_starts_with($trimmed, '/') && !str_starts_with($trimmed, '//')) {
            return ($basePath === '' ? '' : $basePath) . $trimmed;
        }

        return $this->routeHref($basePath, $trimmed);
    }

    private function assetHref(string $basePath, string $href): string
    {
        $trimmed = trim($href);
        if ($trimmed === '') {
            return '';
        }
        if (str_starts_with($trimmed, '#') || preg_match('/^https?:\/\//i', $trimmed) === 1) {
            return $trimmed;
        }
        if (str_starts_with($trimmed, '/') && !str_starts_with($trimmed, '//')) {
            return $trimmed;
        }

        return $this->routeHref($basePath, $trimmed);
    }

    private function routeHref(string $basePath, string $path): string
    {
        $normalizedPath = myronNormalizePath($path);
        $siteRoot = $basePath === '' ? '' : $basePath;

        if ($normalizedPath === '/') {
            return ($siteRoot === '' ? '' : $siteRoot) . '/';
        }

        return ($siteRoot === '' ? '' : $siteRoot) . $normalizedPath;
    }

    private function escape(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    }
}
