---
interface Props {
	isZhTw?: boolean;
}
const { isZhTw = false } = Astro.props;
const placeholder = isZhTw ? "搜尋文件…" : "Search docs…";
const label = isZhTw ? "搜尋" : "Search";
---

<!-- ── Search trigger button ─────────────────────────────────────────────── -->
<button class="search-trigger" id="search-trigger" aria-label={label} aria-haspopup="dialog">
	<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
		<circle cx="11" cy="11" r="8"></circle>
		<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
	</svg>
	<span class="search-trigger-label">{label}</span>
	<span class="search-trigger-shortcut" aria-hidden="true">
		<kbd class="search-trigger-mod">⌘</kbd><kbd>K</kbd>
	</span>
</button>

<!-- ── Search overlay + dialog ───────────────────────────────────────────── -->
<div class="search-overlay" id="search-overlay" aria-hidden="true">
	<div class="search-dialog" id="search-dialog" role="dialog" aria-modal="true" aria-label={label}>
		<div class="search-header">
			<svg class="search-header-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
				<circle cx="11" cy="11" r="8"></circle>
				<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
			</svg>
			<input type="search" class="search-input" id="search-input" placeholder={placeholder} autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
			<button class="search-esc-btn" id="search-close-btn" aria-label="Close search">
				<kbd>ESC</kbd>
			</button>
		</div>
		<div class="search-results" id="search-results" role="listbox"></div>
		<div class="search-footer">
			<span class="search-footer-hint"><kbd>↑</kbd><kbd>↓</kbd> navigate</span>
			<span class="search-footer-hint"><kbd>↵</kbd> open</span>
			<span class="search-footer-hint"><kbd>Esc</kbd> close</span>
		</div>
	</div>
</div>

<style is:global>
	/* ── Trigger button ─────────────────────────────────────────────────────── */
	.search-trigger {
		display: flex;
		align-items: center;
		gap: 0.375rem;
		padding: 0.25rem 0.625rem;
		font-size: 0.78rem;
		color: var(--text-3);
		background: none;
		border: 1px solid var(--border-1);
		border-radius: 0.3125rem;
		cursor: pointer;
		white-space: nowrap;
		transition:
			color 0.15s,
			border-color 0.15s,
			background 0.15s;
	}
	.search-trigger:hover {
		color: var(--text-1);
		border-color: var(--border-2);
		background: rgba(205, 214, 244, 0.05);
	}
	.search-trigger-shortcut {
		display: flex;
		align-items: center;
	}
	.search-trigger-shortcut kbd {
		font-family: var(--font-mono);
		font-size: 0.6875rem;
		color: var(--text-4);
		background: rgba(205, 214, 244, 0.06);
		border: 1px solid rgba(205, 214, 244, 0.1);
		border-radius: 0.25rem;
		padding: 0.0625rem 0.25rem;
		line-height: 1.4;
	}
	.search-trigger-shortcut .search-trigger-mod {
		border-right: none;
		border-radius: 0.25rem 0 0 0.25rem;
		padding-right: 0.125rem;
	}
	.search-trigger-shortcut kbd:last-child:not(.search-trigger-mod) {
		border-radius: 0 0.25rem 0.25rem 0;
		padding-left: 0.125rem;
	}

	/* ── Overlay ────────────────────────────────────────────────────────────── */
	.search-overlay {
		position: fixed;
		inset: 0;
		z-index: 500;
		display: flex;
		align-items: flex-start;
		justify-content: center;
		padding-top: clamp(3rem, 8vh, 6rem);
		background: rgba(17, 17, 27, 0.72);
		backdrop-filter: blur(4px);
		-webkit-backdrop-filter: blur(4px);
		opacity: 0;
		pointer-events: none;
		transition: opacity 0.15s;
	}
	.search-overlay[aria-hidden="false"] {
		opacity: 1;
		pointer-events: auto;
	}

	/* ── Dialog ─────────────────────────────────────────────────────────────── */
	.search-dialog {
		width: min(42rem, calc(100vw - 2rem));
		max-height: min(30rem, calc(100vh - 8rem));
		background: var(--bg-1);
		border: 1px solid var(--border-2);
		border-radius: 0.625rem;
		box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
		display: flex;
		flex-direction: column;
		overflow: hidden;
		transform: translateY(-0.375rem);
		transition: transform 0.15s;
	}
	.search-overlay[aria-hidden="false"] .search-dialog {
		transform: translateY(0);
	}

	/* ── Header / input row ─────────────────────────────────────────────────── */
	.search-header {
		display: flex;
		align-items: center;
		gap: 0.625rem;
		padding: 0.75rem 1rem;
		border-bottom: 1px solid var(--border-1);
		flex-shrink: 0;
	}
	.search-header-icon {
		color: var(--text-3);
		flex-shrink: 0;
	}
	.search-input {
		flex: 1;
		min-width: 0;
		background: none;
		border: none;
		outline: none;
		font-family: var(--font-body);
		font-size: 0.9375rem;
		color: var(--text);
		caret-color: var(--green);
	}
	.search-input::placeholder {
		color: var(--text-4);
	}
	.search-input::-webkit-search-cancel-button {
		display: none;
	}
	.search-esc-btn {
		background: none;
		border: none;
		cursor: pointer;
		padding: 0;
		flex-shrink: 0;
	}
	.search-esc-btn kbd {
		font-family: var(--font-mono);
		font-size: 0.6875rem;
		color: var(--text-4);
		background: rgba(205, 214, 244, 0.06);
		border: 1px solid var(--border-1);
		border-radius: 0.25rem;
		padding: 0.125rem 0.375rem;
		transition:
			color 0.1s,
			border-color 0.1s;
	}
	.search-esc-btn:hover kbd {
		color: var(--text-2);
		border-color: var(--border-2);
	}

	/* ── Results ────────────────────────────────────────────────────────────── */
	.search-results {
		overflow-y: auto;
		flex: 1;
		padding: 0.375rem;
		min-height: 0;
	}
	.search-result {
		border-radius: 0.375rem;
	}
	.search-result-link {
		display: flex;
		flex-direction: column;
		gap: 0.1875rem;
		padding: 0.625rem 0.75rem;
		text-decoration: none;
		border-radius: 0.375rem;
		transition: background 0.1s;
	}
	.search-result:hover .search-result-link,
	.search-result--selected .search-result-link {
		background: rgba(205, 214, 244, 0.07);
	}
	.search-result-title {
		font-size: 0.875rem;
		font-weight: 500;
		color: var(--text);
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	.search-result-excerpt {
		font-size: 0.8125rem;
		color: var(--text-3);
		line-height: 1.5;
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}
	.search-result-excerpt mark {
		background: transparent;
		color: var(--green);
		font-weight: 500;
	}
	.search-no-results {
		padding: 2rem 1rem;
		text-align: center;
		color: var(--text-3);
		font-size: 0.875rem;
	}

	/* ── Footer ─────────────────────────────────────────────────────────────── */
	.search-footer {
		display: flex;
		gap: 1rem;
		align-items: center;
		padding: 0.5rem 1rem;
		border-top: 1px solid var(--border);
		flex-shrink: 0;
	}
	.search-footer-hint {
		display: flex;
		align-items: center;
		gap: 0.25rem;
		font-size: 0.75rem;
		color: var(--text-4);
	}
	.search-footer-hint kbd {
		font-family: var(--font-mono);
		font-size: 0.625rem;
		color: var(--text-3);
		background: rgba(205, 214, 244, 0.06);
		border: 1px solid var(--border-1);
		border-radius: 0.25rem;
		padding: 0.0625rem 0.25rem;
	}

	/* ── Responsive ─────────────────────────────────────────────────────────── */
	@media (max-width: 53.75rem) {
		.search-trigger-label {
			display: none;
		}
		.search-trigger-shortcut {
			display: none;
		}
	}
	@media (max-width: 30rem) {
		.search-footer {
			display: none;
		}
	}
</style>

<script is:inline>
	// Module-level state persists across SPA navigations (attached to window)
	window.__pagefind = window.__pagefind ?? null;

	async function loadPagefind() {
		if (window.__pagefind) return window.__pagefind;
		try {
			const pf = await import("/pagefind/pagefind.js");
			await pf.init();
			window.__pagefind = pf;
		} catch {
			window.__pagefind = null;
		}
		return window.__pagefind;
	}

	function escapeHtml(str) {
		return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
	}

	function initSearch() {
		const trigger = document.getElementById("search-trigger");
		const overlay = document.getElementById("search-overlay");
		const input = document.getElementById("search-input");
		const resultsEl = document.getElementById("search-results");
		const closeBtn = document.getElementById("search-close-btn");

		if (!trigger || !overlay || !input || !resultsEl) return;

		// Teleport overlay to <body> so it escapes SiteNav's backdrop-filter
		// stacking context (which is z-index: 100). Without this, the overlay's
		// own z-index: 500 is scoped inside SiteNav and images outside it paint
		// on top of the backdrop.
		if (overlay.parentElement !== document.body) {
			document.body.appendChild(overlay);
		}

		let selectedIndex = -1;
		let searchTimeout = null;

		function open() {
			overlay.setAttribute("aria-hidden", "false");
			document.body.style.overflow = "hidden";
			requestAnimationFrame(() => input.focus());
			loadPagefind(); // preload index eagerly
		}

		function close() {
			overlay.setAttribute("aria-hidden", "true");
			document.body.style.overflow = "";
			input.value = "";
			resultsEl.innerHTML = "";
			selectedIndex = -1;
		}

		trigger.addEventListener("click", open);
		closeBtn?.addEventListener("click", close);

		overlay.addEventListener("click", e => {
			if (e.target === overlay) close();
		});

		function handleKey(e) {
			if ((e.metaKey || e.ctrlKey) && e.key === "k") {
				e.preventDefault();
				overlay.getAttribute("aria-hidden") === "false" ? close() : open();
				return;
			}
			if (overlay.getAttribute("aria-hidden") !== "false") return;
			if (e.key === "Escape") {
				close();
				return;
			}
			const items = resultsEl.querySelectorAll(".search-result");
			if (!items.length) return;
			if (e.key === "ArrowDown") {
				e.preventDefault();
				selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
				updateSelection(items);
			} else if (e.key === "ArrowUp") {
				e.preventDefault();
				selectedIndex = Math.max(selectedIndex - 1, 0);
				updateSelection(items);
			} else if (e.key === "Enter" && selectedIndex >= 0) {
				const link = items[selectedIndex]?.querySelector("a");
				if (link) {
					close();
					link.click();
				}
			}
		}

		document.addEventListener("keydown", handleKey);
		document.addEventListener("astro:before-swap", () => document.removeEventListener("keydown", handleKey), { once: true });

		function updateSelection(items) {
			items.forEach((item, i) => {
				item.classList.toggle("search-result--selected", i === selectedIndex);
				item.setAttribute("aria-selected", i === selectedIndex ? "true" : "false");
				if (i === selectedIndex) item.scrollIntoView({ block: "nearest" });
			});
		}

		input.addEventListener("input", () => {
			selectedIndex = -1;
			const query = input.value.trim();
			if (searchTimeout) clearTimeout(searchTimeout);
			if (!query) {
				resultsEl.innerHTML = "";
				return;
			}
			searchTimeout = setTimeout(() => doSearch(query), 150);
		});

		async function doSearch(query) {
			const pf = await loadPagefind();
			if (!pf) {
				resultsEl.innerHTML = `<div class="search-no-results">Search index not available — run a build first.</div>`;
				return;
			}
			const search = await pf.search(query);
			if (!search.results.length) {
				resultsEl.innerHTML = `<div class="search-no-results">No results found.</div>`;
				return;
			}
			const data = await Promise.all(search.results.slice(0, 8).map(r => r.data()));
			resultsEl.innerHTML = data
				.map(
					r => `
				<div class="search-result" role="option" aria-selected="false">
					<a href="${r.url}" class="search-result-link">
						<span class="search-result-title">${escapeHtml(r.meta?.title ?? "")}</span>
						<span class="search-result-excerpt">${r.excerpt}</span>
					</a>
				</div>`
				)
				.join("");
			resultsEl.querySelectorAll(".search-result-link").forEach(link => {
				link.addEventListener("click", close);
			});
		}
	}

	document.addEventListener("astro:page-load", initSearch);
</script>