---
import ConfigCarrier from "@components/ConfigCarrier.astro";
import { profileConfig, siteConfig, umamiConfig } from "@/config";
import {
	AUTO_MODE,
	BANNER_HEIGHT,
	BANNER_HEIGHT_EXTEND,
	BANNER_HEIGHT_HOME,
	DARK_MODE,
	DEFAULT_THEME,
	LIGHT_MODE,
	PAGE_WIDTH,
} from "../constants/constants";
import { defaultFavicons } from "../constants/icon";
import type { Favicon } from "../types/config";
import { pathsEqual, url } from "../utils/url-utils";
import "katex/dist/katex.css";

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

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

// apply a class to the body element to decide the height of the banner, only used for initial page load
// 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
const isHomePage = pathsEqual(Astro.url.pathname, url("/"));

// defines global css variables
// why doing this in Layout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
const configHue = siteConfig.themeColor.hue;
if (!banner || typeof banner !== "string" || banner.trim() === "") {
	banner = siteConfig.banner.src;
}

// TODO don't use post cover as banner for now
banner = siteConfig.banner.src;

const enableBanner = siteConfig.banner.enable;

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

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.banner.position || "center"];
---

<!DOCTYPE html>
<html lang={siteLang} class="bg-[var(--page-bg)] transition text-[14px] md:text-[16px]"
	  data-overlayscrollbars-initialize
>
	<head>

		<title>{pageTitle}</title>

		<meta charset="UTF-8" />
		<meta name="description" content={description || pageTitle}>
		<meta name="author" content={profileConfig.name}>

		<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}>
		{setOGTypeArticle ? (
        <meta property="og:type" content="article" />
        ) : (
        <meta property="og:type" content="website" />
        )}

		<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}>

		<meta name="viewport" content="width=device-width" />
		<meta name="generator" content={Astro.generator} />
		<meta id="theme-color-meta" name="theme-color" content="#48823b" />

		{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={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, AUTO_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue}}>
			// Load the theme from local storage
			const theme = localStorage.getItem('theme') || DEFAULT_THEME;
			switch (theme) {
				case LIGHT_MODE:
					document.documentElement.classList.remove('dark');
					break
				case DARK_MODE:
					document.documentElement.classList.add('dark');
					break
				case AUTO_MODE:
					if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
						document.documentElement.classList.add('dark');
					} else {
						document.documentElement.classList.remove('dark');
					}
			}

			// Load the hue from local storage
			const hue = localStorage.getItem('hue') || configHue;
			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>
		<style define:vars={{
			configHue,
			'page-width': `${PAGE_WIDTH}rem`,
			/* https://github.com/afoim/fuwari/commit/aad0a7734be5f6e96a54a3aed51e49760ab3162a */
			'bg-url': siteConfig.background.src ? `url(${siteConfig.background.src})` : 'none',
			'bg-enable': siteConfig.background.enable ? '1' : '0',
			'bg-position': siteConfig.background.position || 'center',
			'bg-size': siteConfig.background.size || 'cover',
			'bg-repeat': siteConfig.background.repeat || 'no-repeat',
			'bg-attachment': siteConfig.background.attachment || 'fixed',
			'bg-opacity': (siteConfig.background.opacity || 0.3).toString()
		}}>
			:root {
				--bg-url: var(--bg-url);
				--bg-enable: var(--bg-enable);
				--bg-position: var(--bg-position);
				--bg-size: var(--bg-size);
				--bg-repeat: var(--bg-repeat);
				--bg-attachment: var(--bg-attachment);
				--bg-opacity: var(--bg-opacity);
			}
			
			/* Background image configuration */
			body {
				--bg-url: var(--bg-url);
				--bg-enable: var(--bg-enable);
				--bg-position: var(--bg-position);
				--bg-size: var(--bg-size);
				--bg-repeat: var(--bg-repeat);
				--bg-attachment: var(--bg-attachment);
				--bg-opacity: var(--bg-opacity);
			}
			
			body::before {
				content: '' !important;
				position: fixed !important;
				top: 0 !important;
				left: 0 !important;
				width: 100% !important;
				height: 100% !important;
				background-image: var(--bg-url) !important;
				background-position: var(--bg-position) !important;
				background-size: var(--bg-size) !important;
				background-repeat: var(--bg-repeat) !important;
				background-attachment: var(--bg-attachment) !important;
				opacity: 0 !important;
				pointer-events: none !important;
				z-index: -1 !important;
				display: block !important;
				transition: opacity 0.3s ease-in-out !important;
			}
			
			body.bg-loaded::before {
				opacity: calc(var(--bg-opacity) * var(--bg-enable)) !important;
			}
		</style>  <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->


		<slot name="head"></slot>

		<script is:inline define:vars={{ umamiConfig }} data-swup-ignore-script>
			window['__blogUmami'] = window['__blogUmami'] || {
				shareDataPromise: null,
				statsPromiseCache: new Map(),
				statsValueCache: new Map(),
				async getShareData() {
					if (!umamiConfig.enable) {
						return null;
					}
					if (this.shareDataPromise) {
						return this.shareDataPromise;
					}
					this.shareDataPromise = fetch(`${umamiConfig.baseUrl}/api/share/${umamiConfig.shareId}`)
						.then((response) => {
							if (!response.ok) {
								throw new Error('获取分享信息失败');
							}
							return response.json();
						})
						.catch((error) => {
							this.shareDataPromise = null;
							throw error;
						});
					return this.shareDataPromise;
				},
				async getStats(cacheKey, buildStatsUrl) {
					if (this.statsValueCache.has(cacheKey)) {
						return this.statsValueCache.get(cacheKey);
					}
					if (this.statsPromiseCache.has(cacheKey)) {
						return this.statsPromiseCache.get(cacheKey);
					}
					const requestPromise = this.getShareData()
						.then(async (shareData) => {
							if (!shareData) {
								return null;
							}
							const statsUrl = buildStatsUrl(shareData.websiteId);
							const statsResponse = await fetch(statsUrl, {
								headers: {
									'x-umami-share-token': shareData.token,
								},
							});
							if (!statsResponse.ok) {
								throw new Error('获取统计数据失败');
							}
							const statsData = await statsResponse.json();
							this.statsValueCache.set(cacheKey, statsData);
							this.statsPromiseCache.delete(cacheKey);
							return statsData;
						})
						.catch((error) => {
							this.statsPromiseCache.delete(cacheKey);
							throw error;
						});
					this.statsPromiseCache.set(cacheKey, requestPromise);
					return requestPromise;
				},
			};
		</script>

		<link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/>
		<!-- Umami分析（自建） -->
		<script defer src="https://umami.adclosenn.top/script.js" data-website-id="7610548d-677b-40cb-9ebd-31dc14e16be7" data-swup-ignore-script></script>
		<script async src="https://www.googletagmanager.com/gtag/js?id=G-YRCGFG45C1"></script>
		<script>
			window.dataLayer = window.dataLayer || [];
			function gtag(){dataLayer.push(arguments);}
			gtag('js', new Date());
			
			gtag('config', 'G-YRCGFG45C1');
		</script>

	</head>
	<body class=" min-h-screen transition " class:list={[{"lg:is-home": isHomePage, "enable-banner": enableBanner}]}
		  data-overlayscrollbars-initialize
	>
		<ConfigCarrier></ConfigCarrier>
		<slot />

		<!-- 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`,
}}>
@tailwind components;
@layer components {
	.enable-banner.is-home #banner-wrapper {
		@apply h-[var(--banner-height-home)] translate-y-[var(--banner-height-extend)]
	}
	.enable-banner #banner-wrapper {
		@apply h-[var(--banner-height-home)]
	}

	.enable-banner.is-home #banner {
		@apply h-[var(--banner-height-home)] translate-y-0
	}
	.enable-banner #banner {
		@apply h-[var(--banner-height-home)] translate-y-[var(--bannerOffset)]
	}
	.enable-banner.is-home #main-grid {
		@apply translate-y-[var(--banner-height-extend)];
	}
	.enable-banner #top-row {
		@apply h-[calc(var(--banner-height-home)_-_4.5rem)] transition-all duration-300
	}
	.enable-banner.is-home #sidebar-sticky {
		@apply top-[calc(1rem_-_var(--banner-height-extend))]
	}
	.navbar-hidden {
		@apply opacity-0 -translate-y-16
	}
}
</style>

<script>
import 'overlayscrollbars/overlayscrollbars.css';
import {
	OverlayScrollbars,
	// ScrollbarsHidingPlugin,
	// SizeObserverPlugin,
	// ClickScrollPlugin
} from 'overlayscrollbars';
import {getHue, getStoredTheme, setHue, setTheme} from "../utils/setting-utils";
import {pathsEqual, url} from "../utils/url-utils";
import {
	BANNER_HEIGHT,
	BANNER_HEIGHT_HOME,
	BANNER_HEIGHT_EXTEND,
	MAIN_PANEL_OVERLAPS_BANNER_HEIGHT
} from "../constants/constants";
import { siteConfig } from '../config';

/* Preload fonts */
// (async function() {
// 	try {
// 		await Promise.all([
// 			document.fonts.load("400 1em Roboto"),
// 			document.fonts.load("700 1em Roboto"),
// 		]);
// 		document.body.classList.remove("hidden");
// 	} catch (error) {
// 		console.log("Failed to load fonts:", error);
// 	}
// })();

/* TODO This is a temporary solution for style flicker issue when the transition is activated */
/* issue link: https://github.com/withastro/astro/issues/8711, the solution get from here too */
/* update: fixed in Astro 3.2.4 */
/*
function disableAnimation() {
	const css = document.createElement('style')
	css.appendChild(
		document.createTextNode(
			`*{
              -webkit-transition:none!important;
              -moz-transition:none!important;
              -o-transition:none!important;
              -ms-transition:none!important;
              transition:none!important
              }`
		)
	)
	document.head.appendChild(css)

	return () => {
		// Force restyle
		;(() => window.getComputedStyle(document.body))()

		// Wait for next tick before removing
		setTimeout(() => {
			document.head.removeChild(css)
		}, 1)
	}
}
*/

const bannerEnabled = !!document.getElementById('banner-wrapper')

function setClickOutsideToClose(panel: string, ignores: string[]) {
	document.addEventListener("click", event => {
		let panelDom = document.getElementById(panel);
		let tDom = event.target;
		if (!(tDom instanceof Node)) return;		// Ensure the event target is an HTML Node
		for (let ig of ignores) {
			let ie = document.getElementById(ig)
			if (ie == tDom || (ie?.contains(tDom))) {
				return;
			}
		}
		panelDom!.classList.add("float-panel-closed");
	});
}
setClickOutsideToClose("display-setting", ["display-setting", "display-settings-switch"])
setClickOutsideToClose("nav-menu-panel", ["nav-menu-panel", "nav-menu-switch"])
setClickOutsideToClose("search-panel", ["search-panel", "search-bar", "search-switch"])


function loadTheme() {
	const theme = getStoredTheme()
	setTheme(theme)
}

function loadHue() {
	setHue(getHue())
}

function initCustomScrollbar() {
	const bodyElement = document.querySelector('body');
	if (!bodyElement) return;
	OverlayScrollbars(
		// docs say that a initialization to the body element would affect native functionality like window.scrollTo
		// but just leave it here for now
		{
			target: bodyElement,
			cancel: {
				nativeScrollbarsOverlaid: true,    // don't initialize the overlay scrollbar if there is a native one
			}
		}, {
		scrollbars: {
			theme: 'scrollbar-base scrollbar-auto py-1',
			autoHide: 'move',
			autoHideDelay: 500,
			autoHideSuspend: false,
		},
	});

	const katexElements = document.querySelectorAll('.katex-display') as NodeListOf<HTMLElement>;

	const katexObserverOptions = {
		root: null,
		rootMargin: '100px',
		threshold: 0.1
	};

	const processKatexElement = (element: HTMLElement) => {
		if (!element.parentNode) return;
		if (element.hasAttribute('data-scrollbar-initialized')) return;

		const container = document.createElement('div');
		container.className = 'katex-display-container';
		container.setAttribute('aria-label', 'scrollable container for formulas');

		element.parentNode.insertBefore(container, element);
		container.appendChild(element);

		OverlayScrollbars(container, {
			scrollbars: {
				theme: 'scrollbar-base scrollbar-auto',
				autoHide: 'leave',
				autoHideDelay: 500,
				autoHideSuspend: false
			}
		});

		element.setAttribute('data-scrollbar-initialized', 'true');
	};

	const katexObserver = new IntersectionObserver((entries, observer) => {
		entries.forEach(entry => {
			if (entry.isIntersecting) {
			processKatexElement(entry.target as HTMLElement);
			observer.unobserve(entry.target);
			}
		});
	}, katexObserverOptions);

	katexElements.forEach(element => {
		katexObserver.observe(element);
	});
}

function showBanner() {
	if (!siteConfig.banner.enable) return;

	const banner = document.getElementById('banner');
	if (!banner) {
		console.error('Banner element not found');
		return;
	}

	// 在显示Banner前，先移除所有已加载的onload-animation类，防止动画重复触发
	const animatedElements = document.querySelectorAll<HTMLElement>('.onload-animation');
	animatedElements.forEach((el) => {
		el.style.animation = 'none';
		el.style.opacity = '1';
	});

	banner.classList.remove('opacity-0', 'scale-105');
}

function init() {
	// disableAnimation()()		// TODO
	loadTheme();
	loadHue();
	initCustomScrollbar();
	showBanner();
}

/* Load settings when entering the site */
init();

const setup = () => {
	// TODO: temp solution to change the height of the banner
/*
	window.swup.hooks.on('animation:out:start', () => {
		const path = window.location.pathname
		const body = document.querySelector('body')
		if (path[path.length - 1] === '/' && !body.classList.contains('is-home')) {
			body.classList.add('is-home')
		} else if (path[path.length - 1] !== '/' && body.classList.contains('is-home')) {
			body.classList.remove('is-home')
		}
	})
*/
	window.swup.hooks.on('link:click', () => {
		// Remove the delay for the first time page load
		document.documentElement.style.setProperty('--content-delay', '0ms')

		// prevent elements from overlapping the navbar
		if (!bannerEnabled) {
			return
		}
		let threshold = window.innerHeight * (BANNER_HEIGHT / 100) - 72 - 16
		let navbar = document.getElementById('navbar-wrapper')
		if (!navbar || !document.body.classList.contains('lg:is-home')) {
			return
		}
		if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) {
			navbar.classList.add('navbar-hidden')
		}
	})
	window.swup.hooks.on('content:replace', initCustomScrollbar)
	window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
		// change banner height immediately when a link is clicked
		const bodyElement = document.querySelector('body')
		if (pathsEqual(visit.to.url, url('/'))) {
			bodyElement!.classList.add('lg:is-home');
		} else {
			bodyElement!.classList.remove('lg:is-home');
		}

		// 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')
		}

		// Hide the TOC while scrolling back to top
		let toc = document.getElementById('toc-wrapper');
		if (toc) {
			toc.classList.add('toc-not-ready')
		}
	});
	window.swup.hooks.on('page:view', () => {
		// hide the temp high element when the transition is done
		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')
			}

            // Just make the transition looks better
            const toc = document.getElementById('toc-wrapper');
            if (toc) {
                toc.classList.remove('toc-not-ready')
            }
        }, 200)
	});
}
if (window?.swup?.hooks) {
	setup()
} else {
	document.addEventListener('swup:enable', setup)
}

let backToTopBtn = document.getElementById('back-to-top-btn');
let toc = document.getElementById('toc-wrapper');
let navbar = document.getElementById('navbar-wrapper')
function scrollFunction() {
	let bannerHeight = window.innerHeight * (BANNER_HEIGHT / 100)

	if (backToTopBtn) {
		if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
			backToTopBtn.classList.remove('hide')
		} else {
			backToTopBtn.classList.add('hide')
		}
	}

	if (bannerEnabled && toc) {
		if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
			toc.classList.remove('toc-hide')
		} else {
			toc.classList.add('toc-hide')
		}
	}

	if (!bannerEnabled) return
	if (navbar) {
		const NAVBAR_HEIGHT = 72
		const MAIN_PANEL_EXCESS_HEIGHT = MAIN_PANEL_OVERLAPS_BANNER_HEIGHT * 16			// The height the main panel overlaps the banner

		let bannerHeight = BANNER_HEIGHT
		if (document.body.classList.contains('lg:is-home') && window.innerWidth >= 1024) {
			bannerHeight = BANNER_HEIGHT_HOME
		}
		let threshold = window.innerHeight * (bannerHeight / 100) - NAVBAR_HEIGHT - MAIN_PANEL_EXCESS_HEIGHT - 16
		if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) {
			navbar.classList.add('navbar-hidden')
		} else {
			navbar.classList.remove('navbar-hidden')
		}
	}
}
window.onscroll = scrollFunction

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`);
}
</script>

<script>
import PhotoSwipeLightbox from "photoswipe/lightbox"
import "photoswipe/style.css"
import "../styles/photoswipe.css"

const zoomTargetSelector = ".custom-md img, #post-cover img"
const pswpModule = import("photoswipe")

type PhotoSwipeState = {
	lightbox: PhotoSwipeLightbox | null
	hookRegistered: boolean
}

const photoSwipeState = (window.__blogPhotoSwipe ??= {
	lightbox: null,
	hookRegistered: false,
	pageLoadRegistered: false,
}) as PhotoSwipeState & { pageLoadRegistered: boolean }

function getZoomTargets() {
	return Array.from(document.querySelectorAll<HTMLImageElement>(zoomTargetSelector))
}

function destroyPhotoSwipe() {
	photoSwipeState.lightbox?.destroy()
	photoSwipeState.lightbox = null
}

function createPhotoSwipe() {
	const lightbox = new PhotoSwipeLightbox({
		gallery: "body",
		children: zoomTargetSelector,
		initialZoomLevel: "fit",
		pswpModule: () => pswpModule,
		closeSVG: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ffffff"><path d="M480-424 284-228q-11 11-28 11t-28-11q-11-11-11-28t11-28l196-196-196-196q-11-11-11-28t11-28q11-11 28-11t28 11l196 196 196-196q11-11 28-11t28 11q11 11 11 28t-11 28L536-480l196 196q11 11 11 28t-11 28q-11 11-28 11t-28-11L480-424Z"/></svg>',
		zoomSVG: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ffffff"><path d="M340-540h-40q-17 0-28.5-11.5T260-580q0-17 11.5-28.5T300-620h40v-40q0-17 11.5-28.5T380-700q17 0 28.5 11.5T420-660v40h40q17 0 28.5 11.5T500-580q0 17-11.5 28.5T460-540h-40v40q0 17-11.5 28.5T380-460q-17 0-28.5-11.5T340-500v-40Zm40 220q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l224 224q11 11 11 28t-11 28q-11 11-28 11t-28-11L532-372q-30 24-69 38t-83 14Zm0-80q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>',
		padding: { top: 20, bottom: 20, left: 20, right: 20 },
		showHideAnimationType: "fade",
		showAnimationDuration: 160,
		hideAnimationDuration: 140,
		secondaryZoomLevel: (zoomLevel) => Math.min(2.5, zoomLevel.max),
		wheelToZoom: true,
		arrowPrev: false,
		arrowNext: false,
		imageClickAction: "close",
		tapAction: "close",
		doubleTapAction: "zoom",
	})

	lightbox.addFilter("domItemData", (itemData, element) => {
		if (element instanceof HTMLImageElement) {
			const fullSrc = element.dataset.pswpSrc || element.currentSrc || element.src
			const width = Number(element.dataset.pswpWidth) || element.naturalWidth || element.width || window.innerWidth
			const height = Number(element.dataset.pswpHeight) || element.naturalHeight || element.height || window.innerHeight
			const thumbSrc = element.currentSrc || element.src
			const sourceElement = element.closest(zoomTargetSelector)
			const allImages = getZoomTargets()
			const index = sourceElement instanceof HTMLImageElement ? allImages.indexOf(sourceElement) : -1

			itemData.src = fullSrc
			itemData.w = Number(width)
			itemData.h = Number(height)
			itemData.msrc = thumbSrc
			if (index >= 0) {
				itemData.element = sourceElement
			}
		}

		return itemData
	})

	lightbox.addFilter("clickedIndex", (clickedIndex, event) => {
		const target = event.target instanceof Element ? event.target.closest(zoomTargetSelector) : null
		if (!(target instanceof HTMLImageElement)) {
			return clickedIndex
		}

		const allImages = getZoomTargets()
		const index = allImages.indexOf(target)
		return index >= 0 ? index : clickedIndex
	})

	lightbox.on("uiRegister", () => {
		lightbox.pswp?.ui?.registerElement({
			name: "open-link",
			order: 8,
			isButton: true,
			tagName: "a",
			className: "pswp__button pswp__button--open-link",
			title: "打开原图",
			html: '<svg class="pswp__icn" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42 9.3-9.29H14V3Zm5 16V11h2v10H3V3h10v2H5v14h14Z"/></svg>',
			onInit: (el, pswp) => {
				if (!(el instanceof HTMLAnchorElement)) {
					return
				}

				el.target = "_blank"
				el.rel = "noreferrer noopener"

				const syncHref = () => {
					const currSlide = pswp.currSlide
					const link = typeof currSlide?.data?.src === "string" ? currSlide.data.src : ""
					el.href = link
					el.classList.toggle("pswp__button--disabled", !link)
					el.setAttribute("aria-disabled", link ? "false" : "true")
					el.tabIndex = link ? 0 : -1
				}

				pswp.on("change", syncHref)
				syncHref()
			},
		})
	})

	lightbox.init()
	photoSwipeState.lightbox = lightbox
}

function initPhotoSwipe() {
	if (photoSwipeState.lightbox || getZoomTargets().length === 0) {
		return
	}
	createPhotoSwipe()
}

function reinitPhotoSwipe() {
	destroyPhotoSwipe()
	window.requestAnimationFrame(() => {
		initPhotoSwipe()
	})
}

function registerPhotoSwipeHook() {
	if (photoSwipeState.hookRegistered || !window.swup?.hooks) {
		return
	}
	window.swup.hooks.on("page:view", reinitPhotoSwipe)
	photoSwipeState.hookRegistered = true
}

function scheduleInitialPhotoSwipe() {
	window.requestAnimationFrame(() => {
		initPhotoSwipe()
	})
}

if (document.readyState === "loading") {
	document.addEventListener("DOMContentLoaded", scheduleInitialPhotoSwipe, { once: true })
} else {
	scheduleInitialPhotoSwipe()
}

if (!photoSwipeState.pageLoadRegistered) {
	document.addEventListener("astro:page-load", scheduleInitialPhotoSwipe)
	photoSwipeState.pageLoadRegistered = true
}

if (window.swup?.hooks) {
	registerPhotoSwipeHook()
} else {
	document.addEventListener("swup:enable", registerPhotoSwipeHook, { once: true })
}
</script>
