<?php

declare(strict_types=1);

class ThemeRenderer
{
    private static array $cssFiles = [];
    private static array $jsFiles = [];
    private static string $bodyClass = '';
    private static string $currentTemplate = '';

    private static array $colorMap = [
        'color_primary'      => '--primary',
        'color_bg'           => '--bg',
        'color_bg_surface'   => '--bg-surface',
        'color_bg_card'      => '--bg-card',
        'color_bg_dark'      => '--bg-dark',
        'color_text'         => '--text',
        'color_text_muted'   => '--text-muted',
        'color_border'       => '--border',
    ];

    private static array $darkColorMap = [
        'dk_color_primary'    => '--primary',
        'dk_color_bg'         => '--bg',
        'dk_color_bg_surface' => '--bg-surface',
        'dk_color_bg_card'    => '--bg-card',
        'dk_color_bg_dark'    => '--bg-dark',
        'dk_color_text'       => '--text',
        'dk_color_text_muted' => '--text-muted',
        'dk_color_border'     => '--border',
    ];

    public static function render(string $template, array $data = []): void
    {
         
        header('X-Frame-Options: SAMEORIGIN');
        header('X-Content-Type-Options: nosniff');
        header('Referrer-Policy: strict-origin-when-cross-origin');
        header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
        header('Permissions-Policy: interest-cohort=()');
        header('X-XSS-Protection: 1; mode=block');

        $templatePath = ThemeManager::templatePath($template);
        if ($templatePath === '') {
            http_response_code(404);
            echo '<h1>Template not found: ' . htmlspecialchars($template) . '</h1>';
            return;
        }

        $data['_template'] = $template;
        $data['_siteName'] = Setting::get('site_name', 'TubePress');
        $data['_siteDescription'] = Setting::get('site_description', '');
        $data['_siteUrl'] = Setting::get('site_url', '');
        $data['_videosPerRow'] = (int) Setting::get('videos_per_row', '5');
        $data['_categoriesPerRow']  = (int) Setting::get('categories_per_row', '5');
        $data['_categoriesPerPage'] = (int) Setting::get('categories_per_page', '30');
        $data['_performersPerPage'] = (int) Setting::get('performers_per_page', '30');
        $data['_channelsPerPage']    = (int) Setting::get('channels_per_page', '30');
        $data['_user'] = Auth::user();
        $data['_footerPages'] = Page::footerPages();

         
        $data['_colorOverrides'] = self::buildColorOverrides();
        $data['_menuSearch'] = Setting::get('menu_search', '1') === '1';
        $data['_menuItems'] = MenuConfig::resolve(MenuConfig::get());
         
        if (Setting::get('albums_enabled', '1') !== '1') {
            $data['_menuItems'] = array_values(array_filter($data['_menuItems'], static function ($mi) {
                $id  = $mi['id'] ?? '';
                $url = rtrim((string) ($mi['url'] ?? ''), '/');
                return $id !== 'albums' && $url !== '/albums' && !str_starts_with($url, '/album-');
            }));
        }

         
        $data['_locale'] = Translator::locale();
        $data['_direction'] = Translator::direction();
        $data['_isRtl'] = Translator::isRtl();
        $data['_availableLangs'] = Translator::available();
        $data['_langPrefix'] = Router::langPrefix();
        $data['_typePrefix'] = Router::typePrefix();

         
        $data['_availableVideoTypes'] = Video::availableTypes();
        $data['_activeVideoType'] = Video::activeType();
        $data['_showVideoTypeSwitch'] = count($data['_availableVideoTypes']) >= 2;

         
         
        if ($data['_activeVideoType'] !== '') {
            $vt = $data['_activeVideoType'];
            $countRows = Database::fetchAll(
                "SELECT 'categories' as entity, COUNT(*) as cnt FROM categories WHERE video_type = ?
                 UNION ALL SELECT 'tags', COUNT(*) FROM tags WHERE video_type = ?
                 UNION ALL SELECT 'pornstars', COUNT(*) FROM performers WHERE video_type = ?
                 UNION ALL SELECT 'channels', COUNT(*) FROM channels WHERE video_type = ?",
                [$vt, $vt, $vt, $vt]
            );
            $entityCounts = [];
            foreach ($countRows as $row) {
                $entityCounts[$row['entity']] = (int) $row['cnt'];
            }
            $data['_menuItems'] = self::filterMenuByTypeCounts($data['_menuItems'], $entityCounts);
        }

         
        $megaMenuEnabled = true;
        foreach ($data['_menuItems'] as $_mi) {
            if (($_mi['id'] ?? '') === 'categories') {
                $megaMenuEnabled = $_mi['mega_menu'] ?? true;
                break;
            }
        }

         
        $data['_megaMenuCtrActive'] = PluginManager::isActive('ctr');
        $megaCols = (int) Setting::get('categories_per_row', '5');
        if (!$megaMenuEnabled) {
            $data['_megaMenuPopular'] = [];
            $data['_megaMenuTrending'] = [];
        } elseif ($data['_megaMenuCtrActive']) {
            $data['_megaMenuTrending'] = Category::all($megaCols, 0, 'trending', $data['_activeVideoType'], true);
            $trendingIds = array_column($data['_megaMenuTrending'], 'id');
             
            $where = '';
            $popParams = [];
            if ($data['_activeVideoType'] !== '') {
                $where .= ' WHERE c.video_type = ?';
                $popParams[] = $data['_activeVideoType'];
            }
            if (!empty($trendingIds)) {
                $ph = implode(',', array_fill(0, count($trendingIds), '?'));
                $where .= ($where ? ' AND' : ' WHERE') . " c.id NOT IN ({$ph})";
                $popParams = array_merge($popParams, $trendingIds);
            }
            $where .= ($where ? ' AND' : ' WHERE') . ' c.video_count > 0';
            $popParams[] = $megaCols;
            $popResults = Database::fetchAll(
                "SELECT c.* FROM categories c{$where} ORDER BY c.total_views DESC, c.name ASC LIMIT ?",
                $popParams
            );
             
            if (empty($popResults)) {
                $fbParams = [];
                $fbWhere = '';
                if ($data['_activeVideoType'] !== '') {
                    $fbWhere = ' WHERE c.video_type = ?';
                    $fbParams[] = $data['_activeVideoType'];
                }
                $fbWhere .= ($fbWhere ? ' AND' : ' WHERE') . ' c.video_count > 0';
                $fbParams[] = $megaCols;
                $popResults = Database::fetchAll(
                    "SELECT c.* FROM categories c{$fbWhere} ORDER BY c.total_views DESC, c.name ASC LIMIT ?",
                    $fbParams
                );
            }
            VideoAsset::assignUniqueThumbFolders($popResults, 'video_categories', 'category_id', 'thumbnail');
            $data['_megaMenuPopular'] = Category::localizeResults($popResults);
        } else {
            $data['_megaMenuPopular'] = Category::all($megaCols * 2, 0, 'popularity', $data['_activeVideoType'], true);
        }

         
        $data['_watchShowViews']     = Setting::get('watch_show_views', '1') === '1';
        $data['_watchShowDuration']  = Setting::get('watch_show_duration', '1') === '1';
        $data['_watchShowDate']      = Setting::get('watch_show_date', '1') === '1';
        $data['_watchShowLikes']     = Setting::get('watch_show_likes', '1') === '1';
        $data['_watchShowFavorites']  = Setting::get('watch_show_favorites', '1') === '1';
        $data['_watchShowPornstars'] = Setting::get('watch_show_pornstars', '1') === '1';
        $data['_watchShowChannels']   = Setting::get('watch_show_channels', '1') === '1';
        $data['_watchShowCategories']= Setting::get('watch_show_categories', '1') === '1';
        $data['_watchShowTags']      = Setting::get('watch_show_tags', '1') === '1';
        $data['_commentsEnabled']    = Setting::get('comments_enabled', '1') === '1';
        $data['_registrationEnabled'] = Setting::get('registration_enabled', '1') === '1';
        $data['_albumsEnabled']      = Setting::get('albums_enabled', '1') === '1';

         
        $data['_captchaProvider'] = Captcha::provider();
        $data['_captchaSiteKey'] = Captcha::siteKey();
        $data['_captchaOnRegister'] = Captcha::isEnabled('register');
        $data['_captchaOnComment'] = Captcha::isEnabled('comment');

         
        $data += [
            '_siteLogo' => '', '_siteFavicon' => '',
            '_cardShowDuration' => true, '_cardShowTitle' => true,
            '_cardMetaLeft' => 'views', '_cardMetaRight' => 'time',
            '_cardGridGap' => 4, '_cardBorderRadius' => 8,
            '_cardThumbnailHover' => true, '_cardTitleLines' => 1,
            '_footerColumns' => [], '_footerBottom' => [], '_footerSocial' => [],
            '_socialLinks' => [],
            '_navStyle' => 'default',
        ];

         
        if ($template === '404') {
            $data['trendingVideos'] = Video::trending(12);
            if (empty($data['trendingVideos'])) {
                $data['trendingVideos'] = Video::all(['sort' => 'popular'], 12);
            }
            $data['popularCategories'] = Category::all(12, 0, 'popularity', '', true);
        }

        self::$currentTemplate = $template;

         
        header('X-Frame-Options: SAMEORIGIN');
        header('X-Content-Type-Options: nosniff');
        header('Referrer-Policy: strict-origin-when-cross-origin');
        header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
        header('Permissions-Policy: interest-cohort=()');
        header("X-XSS-Protection: 1; mode=block");

        $data = HookSystem::applyFilter('theme.template_data', $data, $template);

         
        if (empty($data['canonicalUrl']) && $template !== '404') {
            $prefix = ltrim(Router::langPrefix() . Router::typePrefix(), '/');
            $rawPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
            $rawPath = rtrim($rawPath, '/') ?: '/';
             
            $cleanPath = $rawPath;
            if (Router::langPrefix() !== '' && str_starts_with($cleanPath, Router::langPrefix())) {
                $cleanPath = substr($cleanPath, strlen(Router::langPrefix())) ?: '/';
            }
            if (Router::typePrefix() !== '' && str_starts_with($cleanPath, Router::typePrefix())) {
                $cleanPath = substr($cleanPath, strlen(Router::typePrefix())) ?: '/';
            }
            $canonicalPath = $prefix !== '' ? $prefix . $cleanPath : ltrim($cleanPath, '/');
            if ($canonicalPath === '') $canonicalPath = '/';
            $data['canonicalUrl'] = SEO::canonical($canonicalPath);
        }

         
        $ogDefaults = [
            'og:title' => isset($data['pageTitle']) ? strip_tags($data['pageTitle']) : ($data['_siteName'] ?? ''),
            'og:description' => $data['metaDescription'] ?? ($data['_siteDescription'] ?? ''),
             
             
            'og:url' => isset($data['canonicalUrl']) ? html_entity_decode($data['canonicalUrl']) : '',
        ];
        $data['ogTags'] = array_merge($ogDefaults, $data['ogTags'] ?? []);

         
        $twDefaults = [
            'twitter:title' => isset($data['pageTitle']) ? strip_tags($data['pageTitle']) : ($data['_siteName'] ?? ''),
            'twitter:description' => $data['metaDescription'] ?? ($data['_siteDescription'] ?? ''),
        ];
        $data['twitterCard'] = array_merge($twDefaults, $data['twitterCard'] ?? []);

         
        if (empty($data['alternates']) && $template !== '404') {
            $siteUrl = SEO::siteUrl();
            $defaultLang = Translator::getDefaultCode();
            $rawPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
            $rawPath = rtrim($rawPath, '/') ?: '/';
            $cleanPath = $rawPath;
            if (Router::langPrefix() !== '' && str_starts_with($cleanPath, Router::langPrefix())) {
                $cleanPath = substr($cleanPath, strlen(Router::langPrefix())) ?: '/';
            }
             
            $data['alternates'] = [];
            $data['alternates'][$defaultLang] = $siteUrl . $cleanPath;
            foreach (Translator::available() as $lang) {
                if ($lang['code'] === $defaultLang) continue;
                $data['alternates'][$lang['code']] = $siteUrl . '/' . $lang['code'] . $cleanPath;
            }
            $data['alternates']['x-default'] = $data['alternates'][$defaultLang];
        }

         
        try {
            extract($data, EXTR_SKIP);
            ob_start();
            require $templatePath;
            $content = ob_get_clean();

             
            $layoutPath = ThemeManager::templatePath('layout');
            if ($layoutPath !== '' && $template !== 'layout') {
                extract($data, EXTR_SKIP);
                require $layoutPath;
            } else {
                echo $content;
            }
        } catch (\Throwable $e) {
             
            while (ob_get_level() > 0) {
                ob_end_clean();
            }
            error_log("ThemeRenderer: template '{$template}' error: " . $e->getMessage());
            if (http_response_code() === 200) {
                http_response_code(500);
            }
            echo '<!DOCTYPE html><html><head><title>Error</title></head><body><h1>Something went wrong</h1><p>Please try again later.</p></body></html>';
        }

         
         
         
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }

         
         
         
        $detached = false;
        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
            $detached = true;
        }
        if ($detached && http_response_code() < 400) {
            CronManager::run();
        }

         
        HookSystem::doAction('render.complete', $template);
    }

    public static function buildColorOverrides(): string
    {
        $overrides = [];
        foreach (self::$colorMap as $settingKey => $cssVar) {
            $val = Setting::get($settingKey, '');
            if ($val !== '' && preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $val)) {
                $op = (int) Setting::get($settingKey . '_opacity', '100');
                if ($op < 100) {
                    $h = ltrim($val, '#');
                    if (strlen($h) === 3) $h = $h[0].$h[0].$h[1].$h[1].$h[2].$h[2];
                    $r = hexdec(substr($h, 0, 2));
                    $g = hexdec(substr($h, 2, 2));
                    $b = hexdec(substr($h, 4, 2));
                    $overrides[] = $cssVar . ':rgba(' . $r . ',' . $g . ',' . $b . ',' . round($op / 100, 2) . ')';
                } else {
                    $overrides[] = $cssVar . ':' . $val;
                }
            }
        }
         
        $primaryHex = Setting::get('color_primary', '');
        if ($primaryHex !== '' && preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $primaryHex)) {
            $ph = ltrim($primaryHex, '#');
            if (strlen($ph) === 3) $ph = $ph[0].$ph[0].$ph[1].$ph[1].$ph[2].$ph[2];
            $r = hexdec(substr($ph, 0, 2));
            $g = hexdec(substr($ph, 2, 2));
            $b = hexdec(substr($ph, 4, 2));
            $overrides[] = '--primary-rgb:' . $r . ',' . $g . ',' . $b;
            $overrides[] = '--primary-dark:#' . str_pad(dechex((int)($r * 0.85)), 2, '0', STR_PAD_LEFT)
                . str_pad(dechex((int)($g * 0.85)), 2, '0', STR_PAD_LEFT)
                . str_pad(dechex((int)($b * 0.85)), 2, '0', STR_PAD_LEFT);
        }

        $maxWidth = (int) Setting::get('site_max_width', '1800');
        if ($maxWidth > 0 && $maxWidth !== 1800) {
            $overrides[] = "--max-width:{$maxWidth}px";
        }
        $padDesktop = (int) Setting::get('site_padding_desktop', '24');
        if ($padDesktop !== 24) {
            $overrides[] = "--container-padding:{$padDesktop}px";
        }
        $padMobile = (int) Setting::get('site_padding_mobile', '10');
        if ($padMobile !== 10) {
            $overrides[] = "--container-padding-mobile:{$padMobile}px";
        }
        $logoHDesktop = (int) Setting::get('site_logo_height_desktop', '36');
        if ($logoHDesktop !== 36) {
            $overrides[] = "--logo-h:{$logoHDesktop}px";
        }
        $logoHMobile = (int) Setting::get('site_logo_height_mobile', '28');
        if ($logoHMobile !== 28) {
            $overrides[] = "--logo-h-mobile:{$logoHMobile}px";
        }

         
        $darkOverrides = [];
        foreach (self::$darkColorMap as $settingKey => $cssVar) {
            $val = Setting::get($settingKey, '');
            if ($val !== '' && preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $val)) {
                $op = (int) Setting::get($settingKey . '_opacity', '100');
                if ($op < 100) {
                    $h = ltrim($val, '#');
                    if (strlen($h) === 3) $h = $h[0].$h[0].$h[1].$h[1].$h[2].$h[2];
                    $r = hexdec(substr($h, 0, 2));
                    $g = hexdec(substr($h, 2, 2));
                    $b = hexdec(substr($h, 4, 2));
                    $darkOverrides[] = $cssVar . ':rgba(' . $r . ',' . $g . ',' . $b . ',' . round($op / 100, 2) . ')';
                } else {
                    $darkOverrides[] = $cssVar . ':' . $val;
                }
            }
        }
         
        $dkPrimaryHex = Setting::get('dk_color_primary', '');
        if ($dkPrimaryHex !== '' && preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $dkPrimaryHex)) {
            $dph = ltrim($dkPrimaryHex, '#');
            if (strlen($dph) === 3) $dph = $dph[0].$dph[0].$dph[1].$dph[1].$dph[2].$dph[2];
            $dr = hexdec(substr($dph, 0, 2));
            $dg = hexdec(substr($dph, 2, 2));
            $db = hexdec(substr($dph, 4, 2));
            $darkOverrides[] = '--primary-rgb:' . $dr . ',' . $dg . ',' . $db;
            $darkOverrides[] = '--primary-dark:#' . str_pad(dechex((int)($dr * 0.85)), 2, '0', STR_PAD_LEFT)
                . str_pad(dechex((int)($dg * 0.85)), 2, '0', STR_PAD_LEFT)
                . str_pad(dechex((int)($db * 0.85)), 2, '0', STR_PAD_LEFT);
        }

        $css = '';
        if ($overrides) {
            $css .= '<style>:root{' . implode(';', $overrides) . '}';
            if ($darkOverrides) {
                $css .= '[data-theme="dark"]{' . implode(';', $darkOverrides) . '}';
            }
            $css .= '</style>';
        } elseif ($darkOverrides) {
            $css = '<style>[data-theme="dark"]{' . implode(';', $darkOverrides) . '}</style>';
        }

        return $css;
    }

    public static function enqueueCSS(string $url, int $priority = 10): void
    {
        self::$cssFiles[$priority][] = $url;
    }

    public static function enqueueJS(string $url, int $priority = 10): void
    {
        self::$jsFiles[$priority][] = $url;
    }

    public static function renderCSS(): string
    {
        ksort(self::$cssFiles);
        $html = '';
        foreach (self::$cssFiles as $files) {
            foreach ($files as $url) {
                $html .= '<link rel="stylesheet" href="' . htmlspecialchars($url) . '">' . "\n";
            }
        }
        return HookSystem::applyFilter('head.css', $html);
    }

    public static function renderJS(): string
    {
        ksort(self::$jsFiles);
        $html = '';
        foreach (self::$jsFiles as $files) {
            foreach ($files as $url) {
                $html .= '<script src="' . htmlspecialchars($url) . '"></script>' . "\n";
            }
        }
        return HookSystem::applyFilter('footer.scripts', $html);
    }

    public static function setBodyClass(string $class): void
    {
        self::$bodyClass = $class;
    }

    public static function bodyClass(): string
    {
        return self::$bodyClass;
    }

    public static function currentTemplate(): string
    {
        return self::$currentTemplate;
    }

    public static function partial(string $name, array $data = []): void
    {
        $path = ThemeManager::templatePath('partials/' . $name);
        if ($path !== '') {
            extract($data, EXTR_SKIP);
            require $path;
        }
    }

    private static function filterMenuByTypeCounts(array $menuItems, array $entityCounts): array
    {
        $filtered = [];
        foreach ($menuItems as $item) {
            $id = $item['id'] ?? '';
             
            if (isset($entityCounts[$id]) && $entityCounts[$id] === 0) {
                continue;
            }
             
            if (!empty($item['children'])) {
                $kids = [];
                foreach ($item['children'] as $child) {
                    $cid = $child['id'] ?? '';
                    if (isset($entityCounts[$cid]) && $entityCounts[$cid] === 0) {
                        continue;
                    }
                    $kids[] = $child;
                }
                $item['children'] = $kids;
            }
            $filtered[] = $item;
        }
        return $filtered;
    }

}
