(function () {
	"use strict";

	const apiUrl = window.SEARCH_API_URL;
	const elPattern = document.getElementById("search-patterns");
	const elPaths = document.getElementById("search-paths");
	const elGlobs = document.getElementById("search-globs");
	const elSort = document.getElementById("search-sort");
	const elMax = document.getElementById("search-max-count");
	const elHead = document.getElementById("search-heading");
	const elMulti = document.getElementById("search-multiline");
	const elStatus = document.getElementById("search-status");
	const elResults = document.getElementById("search-results");
	const elAdvanced = document.getElementById("search-advanced");

	function escapeHtml(s) {
		return String(s).replace(/[&<>"']/g, function (c) {
			return ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c];
		});
	}

	function readUrl() {
		const p = new URLSearchParams(window.location.search);
		if (p.has("patterns")) elPattern.value = p.get("patterns");
		if (p.has("paths")) elPaths.value = p.get("paths");
		if (p.has("globs")) elGlobs.value = p.get("globs");
		if (p.has("sort")) elSort.value = p.get("sort");
		elMax.value = p.has("max_count") ? p.get("max_count") : "25";
		if (p.has("heading")) elHead.checked = p.get("heading") === "true";
		if (p.has("multiline")) elMulti.checked = p.get("multiline") === "true";
		const anyAdvanced = ["paths", "globs", "sort", "multiline"].some((k) => p.has(k));
		if (anyAdvanced) elAdvanced.open = true;
	}

	function buildParams() {
		const qs = new URLSearchParams();
		qs.set("a", "search");
		const pat = elPattern.value.trim();
		if (pat) qs.set("patterns", pat);
		const paths = elPaths.value.trim();
		if (paths) qs.set("paths", paths);
		const globs = elGlobs.value.trim();
		if (globs) qs.set("globs", globs);
		if (elSort.value) qs.set("sort", elSort.value);
		const max = elMax.value.trim();
		if (max) qs.set("max_count", max);
		qs.set("heading", elHead.checked ? "true" : "false");
		qs.set("multiline", elMulti.checked ? "true" : "false");
		return qs;
	}

	function persistUrl(qs) {
		const url = new URL(window.location.href);
		const keep = new Set(Array.from(qs.keys()).filter((k) => k !== "a"));
		Array.from(url.searchParams.keys()).forEach((k) => {
			if (!keep.has(k)) url.searchParams.delete(k);
		});
		for (const [k, v] of qs.entries()) {
			if (k === "a") continue;
			url.searchParams.set(k, v);
		}
		window.history.replaceState(null, "", url);
	}

	function renderResults(data) {
		const lines = Array.isArray(data.results) ? data.results : [];
		if (lines.length === 0) {
			elResults.innerHTML = "";
			return;
		}
		const items = lines.map((line) => {
			return '<div class="search-result"><pre class="search-result-line">' + escapeHtml(line) + "</pre></div>";
		});
		elResults.innerHTML = items.join("");
	}

	let pendingCtrl = null;

	async function runSearch() {
		const pat = elPattern.value.trim();
		if (!pat) {
			elStatus.textContent = "";
			elResults.innerHTML = "";
			persistUrl(new URLSearchParams());
			return;
		}
		const qs = buildParams();
		persistUrl(qs);
		if (pendingCtrl) pendingCtrl.abort();
		pendingCtrl = new AbortController();
		elStatus.textContent = "Searching\u2026";
		elResults.classList.add("is-loading");
		try {
			const resp = await fetch(apiUrl + "?" + qs.toString(), {
				signal: pendingCtrl.signal,
				headers: { Accept: "application/json" },
			});
			if (!resp.ok) {
				let detail = resp.statusText;
				try {
					const j = await resp.json();
					if (j && j.detail) detail = j.detail;
				} catch (_) { /* ignore */ }
				elStatus.textContent = "Error " + resp.status + ": " + detail;
				elResults.innerHTML = "";
				return;
			}
			const data = await resp.json();
			const n = (data.results || []).length;
			elStatus.textContent = n + " match" + (n === 1 ? "" : "es");
			renderResults(data);
		} catch (err) {
			if (err && err.name === "AbortError") return;
			elStatus.textContent = "Error: " + (err && err.message ? err.message : String(err));
		} finally {
			elResults.classList.remove("is-loading");
		}
	}

	let debounceTimer = null;
	function debounced() {
		clearTimeout(debounceTimer);
		debounceTimer = setTimeout(runSearch, 500);
	}

	for (const el of [elPattern, elPaths, elGlobs, elMax]) el.addEventListener("input", debounced);
	for (const el of [elSort, elHead, elMulti]) el.addEventListener("change", debounced);

	elPattern.addEventListener("keydown", function (event) {
		if (event.key === "Enter") {
			event.preventDefault();
			clearTimeout(debounceTimer);
			runSearch();
		}
	});

	readUrl();
	runSearch();
})();