<?php

declare(strict_types=1);

require_once dirname(__DIR__, 3) . '/system/license/CoreAttributionPolicy.php';

final class MyronPublishedPageHtmlAssembler
{
    /**
     * @param array<string, mixed> $page
     * @param array<string, mixed> $context
     */
    public function assemble(array $page, array $context = []): string
    {
        $basePath = (string) ($context['basePath'] ?? myronPublicBasePath());
        $requestUri = (string) ($context['requestUri'] ?? myronBuildPublicPageUrl((string) ($page['path'] ?? '/')));
        $resolvedPath = (string) ($context['resolvedPath'] ?? ($page['path'] ?? '/'));
        $footerNavigation = is_array($context['footerNavigation'] ?? null) ? (array) $context['footerNavigation'] : [];
        $navigation = is_array($context['navigation'] ?? null) ? (array) $context['navigation'] : [];
        $headerContext = is_array($context['headerContext'] ?? null)
            ? (array) $context['headerContext']
            : myronPublicHeaderContextResolver()->resolve($page, $resolvedPath, $navigation);
        $appearanceContext = is_array($context['appearanceContext'] ?? null)
            ? (array) $context['appearanceContext']
            : $this->resolveAppearanceContext(is_array($headerContext['settings'] ?? null) ? (array) $headerContext['settings'] : []);
        $headMetadata = is_array($context['headMetadata'] ?? null)
            ? (array) $context['headMetadata']
            : myronPublicHeadMetadataResolver()->resolvePublishedPage($page);
        $currentUser = is_array($context['currentUser'] ?? null) ? (array) $context['currentUser'] : null;
        $headerCreativeNeedsRuntime = (bool) ($context['headerCreativeNeedsRuntime'] ?? false);
        $buttonCreativeNeedsRuntime = (bool) ($context['buttonCreativeNeedsRuntime'] ?? false);

        myronPublicRenderProfiler()->mark('before_template_render');
        $pageHtml = myronDefinitionTemplateRenderer()->renderPublishedPage($page, $basePath, $footerNavigation);
        myronPublicRenderProfiler()->mark('after_template_render');
        $requiresQuizStyles = str_contains($pageHtml, 'myron-public-component--quiz')
            || str_contains($pageHtml, 'class="myron-quiz');

        $siteActionLabel = $currentUser !== null ? 'Manage Site' : 'Login to MyronCMS';
        $siteActionTarget = $currentUser !== null
            ? myronBuildAdminUrl(myronDefaultAuthorizedRoute($currentUser))
            : myronBuildAdminUrl('auth.login');
        $logoPreloadLinks = $this->logoPreloadLinks($headerContext, $appearanceContext, $basePath);
        $heroPreloadLink = $this->heroPreloadLink($page, $logoPreloadLinks);

        ob_start();
        ?>
<!DOCTYPE html>
<html lang="<?= $this->escape(myronSiteLanguage()) ?>" data-appearance-default="<?= $this->escape((string) ($appearanceContext['defaultMode'] ?? 'light')) ?>" data-appearance-policy="<?= $this->escape((string) ($appearanceContext['policy'] ?? 'auto')) ?>" data-appearance-system-follow="<?= !empty($appearanceContext['systemFollow']) ? 'true' : 'false' ?>"<?= (string) ($appearanceContext['initialMode'] ?? '') !== '' ? ' data-appearance="' . $this->escape((string) $appearanceContext['initialMode']) . '"' : '' ?>>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><?= $this->escape((string) ($headMetadata['documentTitle'] ?? '')) ?></title>
  <meta name="description" content="<?= $this->escape((string) ($headMetadata['metaDescription'] ?? '')) ?>">
  <?php if ((string) ($headMetadata['canonicalUrl'] ?? '') !== ''): ?>
  <link rel="canonical" href="<?= $this->escape((string) $headMetadata['canonicalUrl']) ?>">
  <?php endif; ?>
  <?php if ((string) ($headMetadata['robots'] ?? '') !== ''): ?>
  <meta name="robots" content="<?= $this->escape((string) $headMetadata['robots']) ?>">
  <?php endif; ?>
  <?php if (!empty($headMetadata['socialEnabled'])): ?>
  <?php $og = is_array($headMetadata['og'] ?? null) ? $headMetadata['og'] : []; ?>
  <?php $twitter = is_array($headMetadata['twitter'] ?? null) ? $headMetadata['twitter'] : []; ?>
  <meta property="og:type" content="<?= $this->escape((string) ($og['type'] ?? 'article')) ?>">
  <meta property="og:title" content="<?= $this->escape((string) ($og['title'] ?? '')) ?>">
  <meta property="og:description" content="<?= $this->escape((string) ($og['description'] ?? '')) ?>">
  <meta property="og:url" content="<?= $this->escape((string) ($og['url'] ?? '')) ?>">
  <?php if (trim((string) ($og['image'] ?? '')) !== ''): ?>
  <meta property="og:image" content="<?= $this->escape((string) $og['image']) ?>">
  <?php endif; ?>
  <meta name="twitter:card" content="<?= $this->escape((string) ($twitter['card'] ?? 'summary_large_image')) ?>">
  <meta name="twitter:title" content="<?= $this->escape((string) ($twitter['title'] ?? '')) ?>">
  <meta name="twitter:description" content="<?= $this->escape((string) ($twitter['description'] ?? '')) ?>">
  <?php if (trim((string) ($twitter['image'] ?? '')) !== ''): ?>
  <meta name="twitter:image" content="<?= $this->escape((string) $twitter['image']) ?>">
  <?php endif; ?>
  <?php endif; ?>
  <?= myronJsonLdService()->renderGlobal(myronSiteSettings()) . "\n" ?>
  <?= myronJsonLdService()->renderPage($page, myronSiteSettings()) . "\n" ?>
  <?= myronRenderSiteFaviconLink() . "\n" ?>
  <?= $this->renderAppearanceBootstrapScript() . "\n" ?>
  <?= $logoPreloadLinks . "\n" ?>
  <?= $heroPreloadLink . "\n" ?>
  <?= myronRenderDefinitionFontHeadTags() . "\n" ?>
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/tokens.css')) ?>">
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/runtime-fonts.css')) ?>">
  <?= myronCustomCssRepository()->renderCustomFontHeadTags() . "\n" ?>
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/runtime-token-overrides.css')) ?>">
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/base.css')) ?>">
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/public-critical.css')) ?>">
  <?php if ($currentUser === null): ?>
  <?= $this->asyncStylesheetLink(myronBuildAssetUrl('runtime/css/components.min.css')) . "\n" ?>
  <?= $this->asyncStylesheetLink(myronBuildAssetUrl('runtime/css/public-site.min.css')) . "\n" ?>
  <?php if ($requiresQuizStyles): ?>
  <?= $this->asyncStylesheetLink(myronBuildAssetUrl('runtime/css/quiz-public.min.css')) . "\n" ?>
  <?php endif; ?>
  <?php else: ?>
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/components.css')) ?>">
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/public-site.css')) ?>">
  <?php if ($requiresQuizStyles): ?>
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/quiz-public.css')) ?>">
  <?php endif; ?>
  <?php endif; ?>
  <?= myronCustomCssRepository()->renderSiteStyleTag(MyronCustomCssRepository::CONTEXT_PUBLIC) . "\n" ?>
  <?php if ($currentUser !== null): ?>
  <link rel="stylesheet" href="<?= $this->escape(myronBuildAssetUrl('runtime/css/style-guide.css')) ?>">
  <script src="<?= $this->escape(myronBuildAssetUrl('runtime/js/app-shell.js')) ?>" defer></script>
  <?php endif; ?>
  <script src="<?= $this->escape(myronBuildAssetUrl('runtime/js/interactive-atoms.min.js')) ?>" defer></script>
  <script src="<?= $this->escape(myronBuildAssetUrl('runtime/js/public-navigation.js')) ?>" defer></script>
  <?php if (myronAnalyticsHeadScript() !== ''): ?>
  <?= myronAnalyticsHeadScript() . "\n" ?>
  <?php endif; ?>
  <?php if (myronAnalyticsEnabled()): ?>
  <script src="<?= $this->escape(myronBuildAssetUrl('runtime/js/analytics-beacon.js')) ?>" defer></script>
  <?php endif; ?>
</head>
<body class="<?= $currentUser !== null ? 'myron-site-page myron-site-page--authenticated' : 'myron-site-page' ?>">
  <?php if ($currentUser !== null): ?>
  <?php myronRenderPublicShellHeader($currentUser, ['logoutReturnTo' => $requestUri]); ?>
  <?php endif; ?>
  <div class="myron-site">
    <?= myronPublicNavigationRenderer()->renderContext($headerContext, $basePath, $resolvedPath) . "\n" ?>

    <main class="myron-site__canvas">
      <?= $pageHtml . "\n" ?>
    </main>
  </div>
  <?= MyronCoreAttributionPolicy::fromRuntime()->renderBar() . "\n" ?>
  <?= myronCustomCssRepository()->renderCustomInteractionsScriptTags() . "\n" ?>
  <?php if (myronDefinitionTemplateRenderer()->hadCreativeSections() || $headerCreativeNeedsRuntime || $buttonCreativeNeedsRuntime): ?>
  <script src="<?= $this->escape(myronBuildAssetUrl('runtime/js/creative-runtime.min.js')) ?>" defer></script>
  <?php endif; ?>
</body>
</html>
        <?php
        return (string) ob_get_clean();
    }

    /**
     * @return array{defaultMode:string,systemFollow:bool,policy:string,initialMode:string}
     */
    private function resolveAppearanceContext(array $headerSettings): array
    {
        $policy = trim((string) ($headerSettings['appearance_mode_policy'] ?? 'auto'));
        if (!in_array($policy, ['auto', 'light', 'dark'], true)) {
            $policy = 'auto';
        }

        $defaultMode = 'light';
        $systemFollow = false;
        try {
            $registry = new DesignSystemRegistry();
            $current = $registry->currentDefinition();
            $definition = StyleDefinitionRuntime::loadJson((string) ($current['path'] ?? ''));
            $defaultMode = StyleDefinitionRuntime::resolveDefaultMode($definition);
            $systemFollow = (bool) ($definition['settings']['systemFollow'] ?? false);
        } catch (Throwable) {
            $defaultMode = 'light';
            $systemFollow = false;
        }

        $initialMode = $policy === 'light' || $policy === 'dark'
            ? $policy
            : ($systemFollow ? '' : $defaultMode);

        return [
            'defaultMode' => $defaultMode,
            'systemFollow' => $systemFollow,
            'policy' => $policy,
            'initialMode' => $initialMode,
        ];
    }

    private function renderAppearanceBootstrapScript(): string
    {
        return <<<'HTML'
  <script>
(function () {
  "use strict";
  var html = document.documentElement;
  function isMode(value) {
    return value === "light" || value === "dark";
  }
  function readStoredMode() {
    try {
      var value = window.localStorage.getItem("myron-appearance") || "";
      return isMode(value) ? value : "";
    } catch (error) {
      return "";
    }
  }
  function systemPrefersDark() {
    return !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
  }
  function resolveMode(policy, defaultMode, systemFollow, storedMode) {
    if (policy === "light" || policy === "dark") {
      return policy;
    }
    if (isMode(storedMode)) {
      return storedMode;
    }
    if (systemFollow && systemPrefersDark()) {
      return "dark";
    }
    return isMode(defaultMode) ? defaultMode : "light";
  }
  var policy = html.getAttribute("data-appearance-policy") || "auto";
  var defaultMode = html.getAttribute("data-appearance-default") || "light";
  var systemFollow = html.getAttribute("data-appearance-system-follow") === "true";
  html.setAttribute("data-appearance", resolveMode(policy, defaultMode, systemFollow, readStoredMode()));
})();
  </script>
HTML;
    }

    /**
     * @param array<string, mixed> $headerContext
     * @param array{defaultMode:string,systemFollow:bool,policy:string,initialMode:string} $appearanceContext
     */
    private function logoPreloadLinks(array $headerContext, array $appearanceContext, string $basePath): string
    {
        $brand = is_array($headerContext['brand'] ?? null) ? $headerContext['brand'] : [];
        $display = trim((string) ($brand['display'] ?? 'title')) ?: 'title';
        if ($display !== 'logo' && $display !== 'lockup') {
            return '';
        }

        $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;
        }
        if ($lightLogoUrl === '') {
            return '';
        }

        $links = [];
        $append = function (string $url, string $media = '') use (&$links, $basePath): void {
            $href = $this->headerAssetHref($basePath, $url);
            if ($href === '' || !str_starts_with($href, '/') || str_starts_with($href, '//') || isset($links[$href . '|' . $media])) {
                return;
            }
            $mediaAttribute = $media === '' ? '' : ' media="' . $this->escape($media) . '"';
            $links[$href . '|' . $media] = '  <link rel="preload" as="image" href="' . $this->escape($href) . '"' . $mediaAttribute . '>';
        };

        if ($lightLogoUrl === $darkLogoUrl) {
            $append($lightLogoUrl);
        } elseif ($appearanceContext['policy'] === 'light' || $appearanceContext['initialMode'] === 'light') {
            $append($lightLogoUrl);
        } elseif ($appearanceContext['policy'] === 'dark' || $appearanceContext['initialMode'] === 'dark') {
            $append($darkLogoUrl);
        } elseif ($appearanceContext['systemFollow']) {
            $append($lightLogoUrl, '(prefers-color-scheme: light)');
            $append($darkLogoUrl, '(prefers-color-scheme: dark)');
        }

        return implode("\n", array_values($links));
    }

    /**
     * @param array<string, mixed> $page
     */
    private function heroPreloadLink(array $page, string $existingPreloadLinks = ''): string
    {
        try {
            $candidate = myronPublicCriticalResourceResolver()->primaryHeroImage($page);
        } catch (Throwable) {
            return '';
        }
        if (!is_array($candidate)) {
            return '';
        }

        $href = trim((string) ($candidate['href'] ?? ''));
        if ($href === '' || !MyronPublicCriticalResourceResolver::isLocalPreloadHref($href)) {
            return '';
        }
        if ($existingPreloadLinks !== '' && str_contains($existingPreloadLinks, 'href="' . $this->escape($href) . '"')) {
            return '';
        }

        $attributes = [
            'rel="preload"',
            'as="image"',
            'href="' . $this->escape($href) . '"',
            'fetchpriority="high"',
        ];
        $srcset = trim((string) ($candidate['imagesrcset'] ?? ''));
        if ($srcset !== '') {
            $attributes[] = 'imagesrcset="' . $this->escape($srcset) . '"';
        }
        $sizes = trim((string) ($candidate['imagesizes'] ?? ''));
        if ($sizes !== '') {
            $attributes[] = 'imagesizes="' . $this->escape($sizes) . '"';
        }

        return '  <link ' . implode(' ', $attributes) . '>';
    }

    private function asyncStylesheetLink(string $href): string
    {
        $escapedHref = $this->escape($href);

        return '<link rel="preload" as="style" href="' . $escapedHref . '" onload="this.onload=null;this.rel=\'stylesheet\'">'
            . '<noscript><link rel="stylesheet" href="' . $escapedHref . '"></noscript>';
    }

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

        $normalizedPath = myronNormalizePath($trimmed);
        return ($basePath === '' ? '' : $basePath) . $normalizedPath;
    }

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