import type { blastinRoulette } from "web/alerts/types";

function easeOutQuad(t: number) {
	return t * (2 - t);
}

export default async function execute(alert: blastinRoulette) {
	const audio = new Audio("/alerts/assets/mariokartbox.ogg");
	audio.play();
	const div = document.createElement("div");
	div.classList.add("blastin-roulette");

	const words = alert.targets?.length ? alert.targets.slice() : ["..."];

	// how to center a div
	div.style.position = "fixed";
	div.style.top = "50%";
	div.style.left = "50%";
	div.style.transform = "translate(-50%, -50%)";
	div.style.display = "flex";
	div.style.alignItems = "center";
	div.style.justifyContent = "center";

	const text = document.createElement("span");
	text.style.fontFamily = '"Jersey 15"';
	text.style.color = "white";
	text.style.fontSize = "6rem";
	text.style.fontWeight = "700";
	text.style.lineHeight = "1";
	text.style.textShadow = "#000 0px 0px 5px";
	text.style.webkitTextStroke = "2px #000";
	text.style.textAlign = "center";
	div.appendChild(text);

	// animation parameters
	const totalDuration = 4200; // 4 sec
	const finalHold = 1800; // hold final word for 2 sec
	const steps = 60; // how many changes before settling

	let lastPick = "";
	let chosenFinal: string | undefined;
	for (let i = 0; i < steps; i++) {
		const t = i / (steps - 1);
		const when = Math.round(easeOutQuad(t) * totalDuration);
		setTimeout(() => {
			if (i === steps - 1) {
				// If a finaltarget was provided, use it as the final word
				let pick: string;
				if (alert.finaltarget) pick = alert.finaltarget;
				else {
					pick = words[Math.floor(Math.random() * words.length)];
					let attempts = 0;
					while (pick === lastPick && attempts < 10 && words.length > 1) {
						pick = words[Math.floor(Math.random() * words.length)];
						attempts++;
					}
				}
				text.textContent = pick;
				lastPick = pick;
				chosenFinal = pick;

				// brief flicker effect: toggle opacity a few times
				const flickerTimes = 8;
				const flickerInterval = 120; // ms
				// ensure opacity starts at 1
				text.style.opacity = "1";
				for (let k = 0; k < flickerTimes; k++) {
					setTimeout(() => {
						text.style.opacity = k % 2 === 0 ? "0" : "1";
					}, k * flickerInterval);
				}
				// ensure fully visible after flicker
				setTimeout(() => {
					text.style.opacity = "1";
				}, flickerTimes * flickerInterval);
			} else {
				// pick a random word different from the previous shown
				let pick = words[Math.floor(Math.random() * words.length)];
				let attempts = 0;
				// Avoid showing the same word as lastPick
				while (pick === lastPick && attempts < 10 && words.length > 1) {
					pick = words[Math.floor(Math.random() * words.length)];
					attempts++;
				}
				// If the next step is the final and a finaltarget is provided, ensure the penultimate isn't equal to it
				if (
					i === steps - 2 &&
					alert.finaltarget &&
					pick === alert.finaltarget &&
					words.length > 1
				) {
					attempts = 0;
					let fallback = words[Math.floor(Math.random() * words.length)];
					while (
						(fallback === pick || fallback === alert.finaltarget) &&
						attempts < 10 &&
						words.length > 1
					) {
						fallback = words[Math.floor(Math.random() * words.length)];
						attempts++;
					}
					pick = fallback;
				}

				text.textContent = pick;
				lastPick = pick;
			}
		}, when);
	}

	// resolve the completion promise after the final hold
	setTimeout(() => {
		// chosenFinal should be set by the final step; if not, pick one now
		if (!chosenFinal) {
			if (alert.finaltarget) chosenFinal = alert.finaltarget as string;
			else chosenFinal = words[Math.floor(Math.random() * words.length)];
		}
	}, totalDuration + finalHold);

	// total time manager should wait/remove
	const duration = totalDuration + finalHold;

	return { duration, alertDiv: div, blocking: true };
}
