---
import "katex/dist/katex.css";

import { siteConfig, profileConfig, umamiConfig, musicPlayerConfig, pioConfig } from "@/config";
import type { Favicon } from "@/types/config";
import { defaultFavicons } from "@constants/icon";
import {
    BANNER_HEIGHT,
    BANNER_HEIGHT_EXTEND,
    BANNER_HEIGHT_HOME,
    LIGHT_MODE,
    DARK_MODE,
    SYSTEM_MODE,
    PAGE_WIDTH,
    NAVBAR_HEIGHT,
} from "@constants/constants";
import { pathsEqual, url } from "@utils/url";
import FontLoader from "@components/fontLoader.astro";
import LoadingOverlay from "@components/loadingOverlay.astro";
import ConfigCarrier from "@components/configCarrier.astro";
import MusicPlayer from "@components/musicPlayer.svelte";
import Pio from "@components/pio.svelte";
import "@styles/navbar.css";
import "@styles/banner.css";
import "@styles/fancybox.css";


interface Props {
    title?: string;
    banner?: string;
    description?: string;
    lang?: string;
    setOGTypeArticle?: boolean;
    postSlug?: string;
}

let { title, banner, description, lang, setOGTypeArticle, postSlug } = Astro.props;

// apply a class to the body element to decide the height of the banner, only used for initial page load
const isHomePage = pathsEqual(Astro.url.pathname, url("/"));

// defines global css variables
// why doing this in BaseLayout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
const configHue = siteConfig.themeColor.hue;

// 获取导航栏透明模式配置
const navbarTransparentMode =
    siteConfig.wallpaper.banner?.navbar?.transparentMode || "semi";
// 判断是否应该显示顶部高光效果（只在full和semifull模式下显示）
const shouldShowTopHighlight =
    navbarTransparentMode === "full" || navbarTransparentMode === "semifull";

// 获取默认banner图片的辅助函数
const getDefaultBanner = (): string => {
    const src = siteConfig.wallpaper.src;
    if (typeof src === "string") {
        return src;
    }
    if (Array.isArray(src)) {
        return src[0] || "";
    }
    if (src && typeof src === "object") {
        // 优先使用desktop，如果没有则使用mobile
        const desktopSrc = src.desktop;
        const mobileSrc = src.mobile;
        if (typeof desktopSrc === "string") {
            return desktopSrc;
        }
        if (Array.isArray(desktopSrc) && desktopSrc.length > 0) {
            return desktopSrc[0];
        }
        if (typeof mobileSrc === "string") {
            return mobileSrc;
        }
        if (Array.isArray(mobileSrc) && mobileSrc.length > 0) {
            return mobileSrc[0];
        }
    }
    return "";
};

// TODO don't use post cover as banner for now
banner = getDefaultBanner();

const enableBanner = siteConfig.wallpaper.mode === "banner";

let pageTitle: string;
if (title) {
    pageTitle = `${title} - ${siteConfig.title}`;
} else {
    pageTitle = siteConfig.subtitle
        ? `${siteConfig.title} - ${siteConfig.subtitle}`
        : siteConfig.title;
}

let ogImageUrl: string | undefined;
if (siteConfig.generateOgImages && postSlug) {
    ogImageUrl = new URL(`/og/${postSlug}.png`, Astro.site).toString();
}

const favicons: Favicon[] =
    siteConfig.favicon.length > 0 ? siteConfig.favicon : defaultFavicons;

// const siteLang = siteConfig.lang.replace('_', '-')
if (!lang) {
    lang = `${siteConfig.lang}`;
}
const siteLang = lang.replace("_", "-");

const bannerOffsetByPosition = {
    top: `${BANNER_HEIGHT_EXTEND}vh`,
    center: `${BANNER_HEIGHT_EXTEND / 2}vh`,
    bottom: "0",
};
const bannerOffset =
    bannerOffsetByPosition[siteConfig.wallpaper.position || "center"];

const umamiEnabled = umamiConfig.enabled || false;
const umamiScripts = umamiConfig.scripts || ""; // 获取Umami scripts配置
---

<!DOCTYPE html>
<html lang={siteLang} class="bg-(--page-bg) text-[14px] md:text-[16px] is-loading"
>
    <head>
        <meta charset="UTF-8" />
        <!-- Resource Preconnect -->
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link rel="preconnect" href="https://code.iconify.design">
        <!-- Font Loader -->
        <FontLoader />
        <!-- title -->
        <title>{pageTitle}</title>
        <!-- description -->
        <meta name="description" content={description || pageTitle}>
        <!-- keywords -->
        {siteConfig.keywords && siteConfig.keywords.length > 0 && (
            <meta name="keywords" content={siteConfig.keywords.join(', ')} />
        )}
        <!-- author -->
        <meta name="author" content={profileConfig.name}>
        <!-- Open Graph -->
        <meta property="og:site_name" content={siteConfig.title}>
        <meta property="og:url" content={Astro.url}>
        <meta property="og:title" content={pageTitle}>
        <meta property="og:description" content={description || pageTitle}>
        {ogImageUrl && <meta property="og:image" content={ogImageUrl} />}
        {
            setOGTypeArticle ? (
                <meta property="og:type" content="article" />
            ) : (
                <meta property="og:type" content="website" />
            )
        }
        <!-- Twitter -->
        <meta name="twitter:card" content="summary_large_image">
        <meta property="twitter:url" content={Astro.url}>
        <meta name="twitter:title" content={pageTitle}>
        <meta name="twitter:description" content={description || pageTitle}>
        <!-- viewport -->
        <meta name="viewport" content="width=device-width" />
        <!-- generator -->
        <meta name="generator" content={Astro.generator} />
        <!-- favicons -->
        {favicons.map(favicon => (
            <link rel="icon"
                  href={favicon.src.startsWith('/') ? url(favicon.src) : favicon.src}
                  sizes={favicon.sizes}
                  media={favicon.theme && `(prefers-color-scheme: ${favicon.theme})`}
            />
        ))}
        <!-- Set the theme before the page is rendered to avoid a flash -->
        <script is:inline define:vars={{LIGHT_MODE, DARK_MODE, SYSTEM_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, NAVBAR_HEIGHT, configHue, defaultTheme: siteConfig.defaultTheme}}>
            // Load the theme from local storage
            const theme = localStorage.getItem('theme') || defaultTheme;
            // Apply the theme to the document
            let isDark = false;
            switch (theme) {
                case LIGHT_MODE:
                    document.documentElement.classList.remove('dark');
                    isDark = false;
                    break
                case DARK_MODE:
                    document.documentElement.classList.add('dark');
                    isDark = true;
                    break
                case SYSTEM_MODE:
                    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
                        document.documentElement.classList.add('dark');
                        isDark = true;
                    } else {
                        document.documentElement.classList.remove('dark');
                        isDark = false;
                    }
            }
            const expressiveTheme = isDark ? "github-dark" : "github-light";
            document.documentElement.setAttribute("data-theme", expressiveTheme);
            // Load the hue from local storage
            const hue = localStorage.getItem('hue') || configHue;
            // Apply the hue to the document
            document.documentElement.style.setProperty('--hue', hue);
            // calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
            let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
            offset = offset - offset % 4;
            document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
        </script>
        <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
        <style define:vars={{
            configHue,
            'page-width': `${PAGE_WIDTH}rem`,
        }}></style>
        <!-- custom head -->
        <slot name="head"></slot>
        <!-- Pio 看板娘样式 - 仅在启用时加载 -->
        {pioConfig.enable && <link rel="stylesheet" href="/pio/static/pio.css" />}
        <!-- rss feed -->
        <link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/>
        <!-- Umami Analytics -->
        {umamiEnabled && umamiScripts && <Fragment set:html={umamiScripts} />}
    </head>

    <body class=" min-h-screen " class:list={[{"is-home": isHomePage, "enable-banner": enableBanner}]}
    >
        <!-- 页面加载动画 -->
        <LoadingOverlay />
        <!-- 全局配置载体 -->
        <ConfigCarrier />
        <!-- 页面顶部渐变高光效果 - 只在full和semifull模式下显示 -->
        {shouldShowTopHighlight && <div class="top-gradient-highlight"></div>}
        <!-- 页面内容 -->
        <slot />
        <!-- Music Player - 仅在启用时加载 -->
        {musicPlayerConfig.enable && <MusicPlayer client:idle />}
        <!-- Pio 看板娘组件 - 如果启用了侧边栏，可能需要调整 z-index -->
        {pioConfig.enable && <Pio client:idle />}
        <!-- increase the page height during page transition to prevent the scrolling animation from jumping -->
        <div id="page-height-extend" class="hidden h-[300vh]"></div>
    </body>
</html>


<style is:global define:vars={{
    bannerOffset,
    'banner-height-home': `${BANNER_HEIGHT_HOME}vh`,
    'banner-height': `${BANNER_HEIGHT}vh`,
}}>
@reference "../styles/main.css";
@layer components {
    .enable-banner.is-home #banner-wrapper {
        @apply h-(--banner-height-home) translate-y-(--banner-height-extend)
    }
    .enable-banner #banner-wrapper {
        @apply h-(--banner-height-home)
    }
    .enable-banner.is-home #banner {
        @apply h-(--banner-height-home) translate-y-0
    }
    .enable-banner #banner {
        @apply h-(--banner-height-home) translate-y-(--bannerOffset)
    }
    .enable-banner.is-home #main-grid {
        translate: 0 var(--banner-height-extend);
    }
    .enable-banner #top-row {
        height: calc(var(--banner-height-home) - 5.5rem);
    }
    .enable-banner.is-home #sidebar-sticky {
        top: calc(1rem - var(--banner-height-extend)) !important;
    }
}
</style>


<script>
import {
    NAVBAR_HEIGHT,
    BANNER_HEIGHT,
    BANNER_HEIGHT_HOME,
    BANNER_HEIGHT_EXTEND,
} from "@/constants/constants";
import { pathsEqual, url } from "@utils/url";
import { initWallpaperMode } from "@utils/wallpaper";
import { initTheme } from "@utils/theme";
import { initHue } from "@utils/hue";
import { initMarkdownActions } from "@/utils/markdown";
import { initFancybox, cleanupFancybox } from "@utils/fancybox";
import { loadAndInitTranslate } from "@utils/language";
import { setupParticleEffects } from "@/utils/particle";


// 初始化Swup
const setup = () => {
    // Swup can update the body for each page visit, but it's after the page transition, causing a delay for banner height change, so use Swup hooks instead to change the height immediately when a link is clicked
    window.swup.hooks.on('link:click', () => {
        // 简化 navbar 处理逻辑
        const bannerEnabled = !!document.getElementById('banner-wrapper')
        if (bannerEnabled) {
            const navbar = document.getElementById('navbar-wrapper')
            if (navbar && document.body.classList.contains('is-home')) {
                const threshold = window.innerHeight * (BANNER_HEIGHT / 100) - NAVBAR_HEIGHT
                if (document.documentElement.scrollTop >= threshold) {
                    navbar.classList.add('navbar-hidden')
                }
            }
        }
    })
    window.swup.hooks.on('content:replace', () => {
        // Update main grid layout class name after page transition
        const gridClassCarrier = document.getElementById("grid-class-carrier");
        if (gridClassCarrier) {
            const gridClass = gridClassCarrier.getAttribute("data-grid-class");
            const mainGrid = document.getElementById("main-grid");
            if (mainGrid && gridClass) {
                const currentClasses = mainGrid.className.split(" ");
                const newClasses = currentClasses.filter(c => !c.startsWith("lg:grid-cols-") && !c.startsWith("md:grid-cols-") && !c.startsWith("grid-cols-"));
                const gridClasses = gridClass.split(" ");
                mainGrid.className = [...newClasses, ...gridClasses].join(" ");
            }
        }
        // Reinitialize semifull mode scroll detection after page transition
        const navbar = document.getElementById('navbar')
        if (navbar) {
            const transparentMode = navbar.getAttribute('data-transparent-mode')
            if (transparentMode === 'semifull') {
                // Re-call the initialization function to rebind scroll events
                if (typeof (window as any).initSemifullScrollDetection === 'function') {
                    (window as any).initSemifullScrollDetection()
                }
            }
        }
    })
    window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
        // Disable scroll protection when a link is clicked to prevent interference with page jump scrolling to top
        if ((window as any).scrollProtectionManager && typeof (window as any).scrollProtectionManager.disable === 'function') {
            (window as any).scrollProtectionManager.disable();
        }
        // Change banner height immediately when a link is clicked
        const bodyElement = document.querySelector('body')
        const isHomePage = pathsEqual(visit.to.url, url('/'))
        if (isHomePage) {
            bodyElement!.classList.add('is-home');
        } else {
            bodyElement!.classList.remove('is-home');
        }
        // Control banner text visibility based on page
        const bannerTextOverlay = document.querySelector('.banner-text-overlay')
        if (bannerTextOverlay) {
            if (isHomePage) {
                bannerTextOverlay.classList.remove('hidden')
            } else {
                bannerTextOverlay.classList.add('hidden')
            }
        }
        // Control navbar transparency based on page
        const navbar = document.getElementById('navbar')
        if (navbar) {
            navbar.setAttribute('data-is-home', isHomePage.toString())
            const transparentMode = navbar.getAttribute('data-transparent-mode')
            if (transparentMode === 'semifull') {
                // Re-call the initialization function to rebind scroll events
                if (typeof window.initSemifullScrollDetection === 'function') {
                    window.initSemifullScrollDetection()
                }
            }
        }
        // increase the page height during page transition to prevent the scrolling animation from jumping
        const heightExtend = document.getElementById('page-height-extend')
        if (heightExtend) {
            heightExtend.classList.remove('hidden')
        }
    });
    window.swup.hooks.on('visit:end', (_visit: {to: {url: string}}) => {
        setTimeout(() => {
            const heightExtend = document.getElementById('page-height-extend')
            if (heightExtend) {
                heightExtend.classList.add('hidden')
            }
        }, 300)
    });
    window.swup.hooks.on('page:view', () => {
        // Scroll to top of the page after page transition
        window.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
        // Check if the current page is a post page, if so, trigger a custom event to initialize the comment system
        setTimeout(() => {
            if (document.getElementById('tcomment')) {
                const pageLoadedEvent = new CustomEvent('twilight:page:loaded', {
                    detail: {
                        path: window.location.pathname,
                        timestamp: Date.now()
                    }
                });
                document.dispatchEvent(pageLoadedEvent);
            }
        }, 300);
        // Initialize Fancybox
        initFancybox();
    });
    initFancybox();
    window.swup.hooks.on("content:replace", () => {
            cleanupFancybox();
        },
        { before: true },
    )
}
if (window?.swup?.hooks) {
    setup()
} else {
    document.addEventListener('swup:enable', setup)
}

// 初始化 navbar 元素，用于控制 banner 隐藏
let navbar = document.getElementById('navbar-wrapper')
// 初始化返回顶部按钮元素，用于控制返回顶部按钮的显示与隐藏
let backToTopBtn = document.getElementById('back-to-top-btn');
// 节流函数
function throttle(func: (...args: any[]) => void, limit: number) {
    let inThrottle: boolean;
    return function(this: any, ...args: any[]) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    }
}
// 滚动事件处理函数，用于控制返回顶部按钮和banner隐藏
function scrollFunction() {
    const scrollTop = document.documentElement.scrollTop;
    const bannerHeight = window.innerHeight * (BANNER_HEIGHT / 100);
    const bannerEnabled = !!document.getElementById('banner-wrapper')
    // 批量处理DOM操作
    requestAnimationFrame(() => {
        if (backToTopBtn) {
            if (scrollTop > bannerHeight) {
                backToTopBtn.classList.remove('hide')
            } else {
                backToTopBtn.classList.add('hide')
            }
        }
        if (bannerEnabled && navbar) {
            const isHome = document.body.classList.contains('is-home');
            const currentBannerHeight = isHome ? BANNER_HEIGHT_HOME : BANNER_HEIGHT;
            const threshold = window.innerHeight * (currentBannerHeight / 100) - NAVBAR_HEIGHT;
            if (scrollTop >= threshold) {
                navbar.classList.add('navbar-hidden')
            } else {
                navbar.classList.remove('navbar-hidden')
            }
        }
    });
}
// 使用节流优化滚动性能
window.onscroll = throttle(scrollFunction, 16); // 约60fps
// 监听窗口 resize 事件，用于更新 banner 高度相关的 CSS 变量
window.onresize = () => {
    // calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
    let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
    offset = offset - offset % 4;
    document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
}

// Initialize theme
initTheme();

// Initialize hue
initHue();

// Initialize wallpaper mode
initWallpaperMode();

// 初始化 Markdown 相关操作以防止因 Swup 无刷新跳转的机制限制而导致操作失效
initMarkdownActions();

// Initialize translate.js
loadAndInitTranslate();

// Initialize particle effects
setupParticleEffects();

// Remove is-loading class and trigger initial-animation after page load
if (typeof window !== 'undefined') {
    window.addEventListener('load', () => {
        const overlay = document.getElementById('loading-overlay');
        const enableBanner = document.body.classList.contains('enable-banner');
        // 触发 LoadingOverlay 淡出
        if (overlay) {
            overlay.classList.add('fade-out');
        }
        // 等待 LoadingOverlay 淡出（600ms）
        setTimeout(() => {
            if (overlay) {
                overlay.style.display = 'none';
            }
            if (enableBanner) {
                // banner 开始恢复过渡：添加 banner-restoring 类（解除 CSS 中的 height: 0）
                document.documentElement.classList.add('banner-restoring');
                // 等待 banner 恢复过渡完成（600ms）
                setTimeout(() => {
                    // 触发各容器淡入动画
                    document.documentElement.classList.remove('is-loading');
                    document.documentElement.classList.add('show-initial-animation');
                    setTimeout(() => {
                        document.documentElement.classList.remove('show-initial-animation');
                        document.documentElement.classList.remove('banner-restoring');
                    }, 1200);
                }, 600);
            } else {
                // 非 banner 模式，直接触发淡入
                document.documentElement.classList.remove('is-loading');
                document.documentElement.classList.add('show-initial-animation');
                setTimeout(() => {
                    document.documentElement.classList.remove('show-initial-animation');
                }, 1200);
            }
        }, 600);
    });
}
</script>