/* DS2 core (c) 2024-2026 Alexander McIlwraith
   Released under Creative Commons Attribution-ShareAlike 4.0 International
*/

// create a pure JS mouse click event
const click = new MouseEvent('click', {
	view: window,
	bubbles: false,
	cancelable: true
});

// wait for an element to appear in the DOM
const waitForElement = (selector) => {
	return new Promise(resolve => {
		if (document.querySelector(selector)) {
			return resolve(document.querySelector(selector));
		}
		const observer = new MutationObserver(() => {
			if (document.querySelector(selector)) {
				observer.disconnect();
				resolve(document.querySelector(selector));
			}
		});
		observer.observe(document.body, {
			childList: true,
			subtree: true
		});
	});
};

// Tab logic
const tabNamespace = "#tab:";

const chooseTab = (tab) => {
	const siblings = Array.from(
		tab.closest('[role="tablist"]').querySelectorAll('[role="tab"]')
	);
	siblings.forEach(sibling => sibling.classList.remove("selected"));
	tab.classList.add("selected");

	const tabGroup = tab.closest("tabset, .tab-group");
	const tabPanels = Array.from(
		tabGroup.querySelectorAll('[role="tabpanel"]')
	);
	
	tabPanels.forEach(panel => panel.classList.remove("open"));
	const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel");
	document.getElementById(tabPanelId).classList.add("open");
};

let pushStateCount = 0;
let tabsetCount = 0;

export function init(container = document, spacer = true) {
	container.querySelectorAll(".tab-group, tabset").forEach(tabGroup => {

		if (tabGroup.querySelector('[role="tablist"]') === null) {

			if (tabGroup.getAttribute("id") == null) {
				tabGroup.setAttribute("id", `tab-group-${tabsetCount++}`);
			}

			const tabgroup = tabGroup.getAttribute("id");
			let tablist = "";

			Array.from(tabGroup.children).forEach(child => {

				const isDetails = child.nodeName === "DETAILS";
				const tabLabel = isDetails ? child.querySelector("summary")?.innerHTML : child.getAttribute("tab") || child.getAttribute("data-tab");

				if (tabLabel !== null) {

					const tabID = tabLabel.replace(/\W+/g, "-").toLowerCase();
					let tabPanel;

					if (isDetails) {
						tabPanel = child;
						tabPanel.setAttribute("open", "");
					} else {
						tabPanel = document.createElement("div");
						tabPanel.appendChild(child.cloneNode(true));
					}

					tabPanel.id = `tab-panel-${tabgroup}-${tabID}`;
					tabPanel.className = tablist === "" ? "open" : "";
					tabPanel.setAttribute("role", "tabpanel");
					tabPanel.setAttribute("tabindex", "0");
					tabPanel.setAttribute(
						"aria-labelledby",
						`tab-${tabgroup}-${tabID}`
					);

					child.parentNode.replaceChild(tabPanel, child);

					tablist += `<li tabindex="0" role="tab" id="tab-${tabgroup}-${tabID}">
							<span>${tabLabel}</span>
						</li>`;
				} else {
					child.classList.add("tab-hidden");
				}
			});

			const ul = document.createElement("ul");
			ul.setAttribute("role", "tablist");
			tabGroup.insertBefore(ul, tabGroup.firstChild);


			const wrapper = document.createElement("div");
			wrapper.className = "tab-scroll-wrapper";

			const scroller = document.createElement("div");
			scroller.className = "tab-scroll";

			const leftArrow = document.createElement("div");
			leftArrow.className = "tab-scroll-arrow left";

			leftArrow.setAttribute("aria-hidden", "true");

			leftArrow.innerHTML = `
				<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" width="24" height="24">
					<path d="M400-80 0-480l400-400 71 71-329 329 329 329-71 71Z"/>
				</svg>
			`;

			const rightArrow = document.createElement("div");
			rightArrow.className = "tab-scroll-arrow right";

			rightArrow.setAttribute("aria-hidden", "true");

			rightArrow.innerHTML = `
				<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" width="24" height="24">
					<path d="m321-80-71-71 329-329-329-329 71-71 400 400L321-80Z"/>
				</svg>
			`;

			ul.parentNode.insertBefore(wrapper, ul);
			wrapper.appendChild(scroller);
			wrapper.appendChild(leftArrow);
			wrapper.appendChild(rightArrow);
			scroller.appendChild(ul);

			const updateScrollState = () => {
				const isOverflowing = scroller.scrollWidth > scroller.clientWidth;

				wrapper.classList.toggle("is-overflowing", isOverflowing);

				const atStart = scroller.scrollLeft <= 1;
				const atEnd = scroller.scrollLeft + scroller.clientWidth >= scroller.scrollWidth - 2;

				leftArrow.classList.toggle("hidden", !isOverflowing || atStart);
				rightArrow.classList.toggle("hidden", !isOverflowing || atEnd);
				
				
    wrapper.classList.toggle("show-left", isOverflowing && !atStart);
    wrapper.classList.toggle("show-right", isOverflowing && !atEnd);

			};

			const scrollAmount = 15;   // smaller = smoother per frame
			const scrollInterval = 16; // ~60fps

			let scrollTimer = null;

			const startScrolling = (direction) => {
				if (scrollTimer) return;

				let speed = 0.1;

				scrollTimer = setInterval(() => {
					speed = Math.min(speed + 0.01, 5); // ramp up
					scroller.scrollBy({
						left: direction * scrollAmount * speed,
						behavior: "auto"
					});
				}, scrollInterval);
			};


			const stopScrolling = () => {
				clearInterval(scrollTimer);
				scrollTimer = null;
			};
			leftArrow.addEventListener("mousedown", () => startScrolling(-1));
			leftArrow.addEventListener("mouseup", stopScrolling);
			leftArrow.addEventListener("mouseleave", stopScrolling);

			leftArrow.addEventListener("touchstart", () => startScrolling(-1));
			leftArrow.addEventListener("touchend", stopScrolling);

			rightArrow.addEventListener("mousedown", () => startScrolling(1));
			rightArrow.addEventListener("mouseup", stopScrolling);
			rightArrow.addEventListener("mouseleave", stopScrolling);

			rightArrow.addEventListener("touchstart", () => startScrolling(1));
			rightArrow.addEventListener("touchend", stopScrolling);

			const ARROW_ZONE = 40;

			wrapper.addEventListener("contextmenu", (e) => {
				const isOverflowing = wrapper.classList.contains("is-overflowing");
				if (!isOverflowing) return;

				const rect = wrapper.getBoundingClientRect();

				const isLeftZone = e.clientX < rect.left + ARROW_ZONE;
				const isRightZone = e.clientX > rect.right - ARROW_ZONE;

				if (isLeftZone || isRightZone) {
					e.preventDefault();
				}
			});


			const scrollClickAmount = 150;

			leftArrow.addEventListener("click", () => {
					scroller.scrollBy({ left: -scrollClickAmount, behavior: "smooth" });
			});

			rightArrow.addEventListener("click", () => {
					scroller.scrollBy({ left: scrollClickAmount, behavior: "smooth" });
			});

			scroller.addEventListener("scroll", updateScrollState);
			window.addEventListener("resize", updateScrollState);

			updateScrollState();
			ul.innerHTML = spacer !== true ? tablist : `${tablist}<li aria-hidden="true" class="separator"></li>`;

			requestAnimationFrame(updateScrollState);
			setTimeout(updateScrollState, 50);

			ul.innerHTML = spacer !== true ? tablist : `${tablist}<li aria-hidden="true" class="separator"></li>`;

			// Tab ordering
			if (tabGroup.hasAttribute("order") || tabGroup.hasAttribute("data-order")) {

				const order = (
					tabGroup.getAttribute("order") ||
					tabGroup.getAttribute("data-order")
				).split(",");

				const items = Array.from(ul.getElementsByTagName("li"));

				items.sort((a, b) => {
					const aa = order.indexOf(a.textContent.trim());
					const bb = order.indexOf(b.textContent.trim());
					if (aa === -1) return 1;
					if (bb === -1) return -1;
					return aa - bb;
				});

				ul.innerHTML = "";
				items.forEach(item => ul.appendChild(item));
				chooseTab(items[0]);
			}

			// Tab event handlers
			tabGroup.querySelectorAll('[role="tab"]').forEach(tab => {

				tab.addEventListener("click", (evt) => {

					const tabId = tab.getAttribute("id");
					const hash = `${tabNamespace}${tabId}`;

					chooseTab(evt.currentTarget);

					window.history.pushState(
						{ tab: tabId },
						"",
						`${location.pathname}${location.search}${hash}`
					);

					pushStateCount++;
				});

				tab.addEventListener("keypress", (e) => {
					if (e.which === 32 || e.which === 13) {
						e.preventDefault();
						e.currentTarget.dispatchEvent(click);
					}
				});
			});

			ul.querySelector("li")?.classList.add("selected");
		}
	});

	// Initial hash handling (tabs only)
	if (location.hash.startsWith(tabNamespace)) {
		const tabId = location.hash.replace(tabNamespace, "");
		waitForElement(`#${tabId}`).then(el => {
			el.focus();
			el.dispatchEvent(click);
		});
	}

	// History navigation (tabs only)
	window.addEventListener("popstate", (e) => {
		if (!location.hash.startsWith(tabNamespace)) return;
		if (e.state?.tab) {
			const tab = document.querySelector(`#${e.state.tab}`);
			if (tab) chooseTab(tab);
		}
	});
}