import { createTimer } from "@solid-primitives/timer";
import { createMutation } from "@tanstack/solid-query";
import { getCurrentWindow } from "@tauri-apps/api/window";
import * as dialog from "@tauri-apps/plugin-dialog";
import { type as ostype } from "@tauri-apps/plugin-os";
import { cx } from "cva";
import {
	type ComponentProps,
	createEffect,
	createMemo,
	createSignal,
	onCleanup,
	Show,
} from "solid-js";
import { createStore, produce } from "solid-js/store";
import createPresence from "solid-presence";
import { createTauriEventListener } from "~/utils/createEventListener";
import {
	createCurrentRecordingQuery,
	createOptionsQuery,
} from "~/utils/queries";
import { commands, events } from "~/utils/tauri";

type State =
	| { variant: "countdown"; from: number; current: number }
	| { variant: "recording" }
	| { variant: "paused" }
	| { variant: "stopped" };

declare global {
	interface Window {
		COUNTDOWN: number;
	}
}

export default function () {
	const [state, setState] = createSignal<State>(
		window.COUNTDOWN === 0
			? { variant: "recording" }
			: {
					variant: "countdown",
					from: window.COUNTDOWN,
					current: window.COUNTDOWN,
				},
	);
	const [start, setStart] = createSignal(Date.now());
	const [time, setTime] = createSignal(Date.now());
	const currentRecording = createCurrentRecordingQuery();
	const optionsQuery = createOptionsQuery();

	const audioLevel = createAudioInputLevel();

	const [pauseResumes, setPauseResumes] = createStore<
		| []
		| [
				...Array<{ pause: number; resume?: number }>,
				{ pause: number; resume?: number },
		  ]
	>([]);

	createTauriEventListener(events.recordingEvent, (payload) => {
		if (payload.variant === "Countdown") {
			setState((s) => {
				if (s.variant === "countdown") return { ...s, current: payload.value };

				return s;
			});
		} else if (payload.variant === "Started") {
			setState({ variant: "recording" });
			setStart(Date.now());
		}
	});

	createTimer(
		() => {
			if (state().variant !== "recording") return;
			setTime(Date.now());
		},
		100,
		setInterval,
	);

	createEffect(() => {
		if (
			state().variant === "stopped" &&
			!currentRecording.isPending &&
			(currentRecording.data === undefined || currentRecording.data === null)
		)
			getCurrentWindow().close();
	});

	const stopRecording = createMutation(() => ({
		mutationFn: async () => {
			setState({ variant: "stopped" });
			await commands.stopRecording();
		},
	}));

	const togglePause = createMutation(() => ({
		mutationFn: async () => {
			if (state().variant === "paused") {
				await commands.resumeRecording();
				setPauseResumes(
					produce((a) => {
						if (a.length === 0) return a;
						a[a.length - 1].resume = Date.now();
					}),
				);
				setState({ variant: "recording" });
			} else {
				await commands.pauseRecording();
				setPauseResumes((a) => [...a, { pause: Date.now() }]);
				setState({ variant: "paused" });
			}
			setTime(Date.now());
		},
	}));

	const restartRecording = createMutation(() => ({
		mutationFn: async () => {
			const shouldRestart = await dialog.confirm(
				"Are you sure you want to restart the recording? The current recording will be discarded.",
				{ title: "Confirm Restart", okLabel: "Restart", cancelLabel: "Cancel" },
			);

			if (!shouldRestart) return;

			await commands.restartRecording();

			setState({ variant: "recording" });
			setTime(Date.now());
		},
	}));

	const deleteRecording = createMutation(() => ({
		mutationFn: async () => {
			const shouldDelete = await dialog.confirm(
				"Are you sure you want to delete the recording?",
				{ title: "Confirm Delete", okLabel: "Delete", cancelLabel: "Cancel" },
			);

			if (!shouldDelete) return;

			await commands.deleteRecording();

			setState({ variant: "stopped" });
		},
	}));

	const adjustedTime = () => {
		if (state().variant === "countdown") return 0;
		let t = time() - start();
		for (const { pause, resume } of pauseResumes) {
			if (pause && resume) t -= resume - pause;
		}
		return t;
	};

	const [countdownRef, setCountdownRef] = createSignal<HTMLDivElement | null>(
		null,
	);
	const showCountdown = () => state().variant === "countdown";
	const countdownPresence = createPresence({
		show: showCountdown,
		element: countdownRef,
	});
	const countdownState = createMemo<
		Extract<State, { variant: "countdown" }> | undefined
	>((prev) => {
		const s = state();
		if (s.variant === "countdown") return s;
		if (prev && countdownPresence.present()) return prev;
	});

	return (
		<div class="flex flex-row items-stretch w-full h-full bg-gray-1 animate-in fade-in">
			<Show when={countdownState()}>
				{(state) => (
					<div
						ref={setCountdownRef}
						class={cx(
							"transition-opacity",
							showCountdown() ? "opacity-100" : "opacity-0",
						)}
					>
						<Countdown from={state().from} current={state().current} />
					</div>
				)}
			</Show>
			<div class="flex flex-row justify-between p-[0.25rem] flex-1">
				<button
					disabled={stopRecording.isPending}
					class="py-[0.25rem] px-[0.5rem] text-red-300 gap-[0.25rem] flex flex-row items-center rounded-lg transition-opacity disabled:opacity-60"
					type="button"
					onClick={() => stopRecording.mutate()}
				>
					<IconCapStopCircle />
					<span class="font-[500] text-[0.875rem] tabular-nums">
						{formatTime(adjustedTime() / 1000)}
					</span>
				</button>

				<div class="flex gap-1 items-center">
					<div class="flex relative justify-center items-center w-8 h-8">
						{optionsQuery.rawOptions.micName != null ? (
							<>
								<IconCapMicrophone class="size-5 text-gray-12" />
								<div class="absolute bottom-1 left-1 right-1 h-0.5 bg-gray-10 overflow-hidden rounded-full">
									<div
										class="absolute inset-0 transition-transform duration-100 bg-blue-9"
										style={{
											transform: `translateX(-${(1 - audioLevel()) * 100}%)`,
										}}
									/>
								</div>
							</>
						) : (
							<IconLucideMicOff
								class="text-gray-7 size-5"
								data-tauri-drag-region
							/>
						)}
					</div>

					{(currentRecording.data?.mode === "studio" ||
						ostype() === "macos") && (
						<ActionButton
							disabled={togglePause.isPending}
							onClick={() => togglePause.mutate()}
						>
							{state().variant === "paused" ? (
								<IconCapPlayCircle />
							) : (
								<IconCapPauseCircle />
							)}
						</ActionButton>
					)}

					<ActionButton
						disabled={restartRecording.isPending}
						onClick={() => restartRecording.mutate()}
					>
						<IconCapRestart />
					</ActionButton>
					<ActionButton
						disabled={deleteRecording.isPending}
						onClick={() => deleteRecording.mutate()}
					>
						<IconCapTrash />
					</ActionButton>
				</div>
			</div>
			<div
				class="non-styled-move cursor-move flex items-center justify-center p-[0.25rem] border-l border-gray-5 hover:cursor-move"
				data-tauri-drag-region
			>
				<IconCapMoreVertical class="pointer-events-none text-gray-10" />
			</div>
		</div>
	);
}

function ActionButton(props: ComponentProps<"button">) {
	return (
		<button
			{...props}
			class={cx(
				"p-[0.25rem] rounded-lg transition-all",
				"text-gray-11",
				"h-8 w-8 flex items-center justify-center",
				"disabled:opacity-50 disabled:cursor-not-allowed",
				props.class,
			)}
			type="button"
		/>
	);
}

function formatTime(secs: number) {
	const minutes = Math.floor(secs / 60);
	const seconds = Math.floor(secs % 60);

	return `${minutes}:${seconds.toString().padStart(2, "0")}`;
}

function createAudioInputLevel() {
	const [level, setLevel] = createSignal(0);

	createTauriEventListener(events.audioInputLevelChange, (dbs) => {
		const DB_MIN = -60;
		const DB_MAX = 0;

		const dbValue = dbs ?? DB_MIN;
		const normalizedLevel = Math.max(
			0,
			Math.min(1, (dbValue - DB_MIN) / (DB_MAX - DB_MIN)),
		);
		setLevel(normalizedLevel);
	});

	return level;
}

function Countdown(props: { from: number; current: number }) {
	const [animation, setAnimation] = createSignal(1);
	setTimeout(() => setAnimation(0), 10);

	return (
		<div class="flex flex-row justify-between p-[0.25rem] flex-1 bg-gray-1 fixed inset-0 z-10">
			<div class="flex flex-1 gap-3 items-center px-3">
				<div class="flex-1 text-[13px] text-gray-11">Recording starting...</div>
				<div class="relative w-5 h-5 text-red-300">
					<svg class="absolute inset-0 w-5 h-5 -rotate-90" viewBox="0 0 20 20">
						<circle
							cx="10"
							cy="10"
							r="8"
							fill="none"
							stroke="currentColor"
							stroke-width="1.5"
							opacity="0.2"
						/>
						<circle
							cx="10"
							cy="10"
							r="8"
							fill="none"
							stroke="currentColor"
							stroke-width="1.5"
							stroke-dasharray={`${animation() * 50.265} 50.265`}
							stroke-linecap="round"
							class="transition-all duration-1000 ease-linear"
							style={{
								"transition-duration": `${props.from * 1000}ms`,
							}}
						/>
					</svg>
					<span class="flex absolute inset-0 justify-center items-center text-[11px]">
						{props.current}
					</span>
				</div>
			</div>
		</div>
	);
}
