import { Minimize2, RefreshCw } from "lucide-react";
import type React from "react";
import { useSyncExternalStore } from "react";
import { Button } from "@/components/ui/button";
import {
	Dialog,
	DialogContent,
	DialogFooter,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Progress } from "@/components/ui/progress";
import type { TauriCreateBackupProgress } from "@/lib/bindings";
import { commands } from "@/lib/bindings";
import { callAsyncCommand } from "@/lib/call-async-command";
import { tc } from "@/lib/i18n";
import {
	getActiveProjectProgressTask,
	registerProjectProgressTask,
	restoreProjectProgressTask,
} from "@/lib/project-progress-task";
import { toastNormal, toastSuccess } from "@/lib/toast";

type BackupProgressState = {
	projectPath: string;
	header?: React.ReactNode;
	progress: TauriCreateBackupProgress;
	minimized: boolean;
	cancelRequested: boolean;
	cancel: () => void;
	promise: Promise<null | "cancelled">;
};

const initialBackupProgress: TauriCreateBackupProgress = {
	proceed: 0,
	total: 1,
	last_proceed: "",
};

let backupProgressState: BackupProgressState | null = null;
const backupProgressListeners = new Set<() => void>();

function emitBackupProgress() {
	for (const listener of backupProgressListeners) listener();
}

function setBackupProgressState(state: BackupProgressState | null) {
	backupProgressState = state;
	emitBackupProgress();
}

function subscribeBackupProgress(listener: () => void) {
	backupProgressListeners.add(listener);
	return () => backupProgressListeners.delete(listener);
}

function getBackupProgressSnapshot() {
	return backupProgressState;
}

function updateBackupProgress(progress: TauriCreateBackupProgress) {
	if (backupProgressState == null) return;
	if (backupProgressState.progress.proceed > progress.proceed) return;
	setBackupProgressState({
		...backupProgressState,
		progress,
	});
}

function minimizeBackupProgress() {
	if (backupProgressState == null) return;
	setBackupProgressState({
		...backupProgressState,
		minimized: true,
	});
}

function restoreBackupProgress() {
	if (backupProgressState == null) return;
	setBackupProgressState({
		...backupProgressState,
		minimized: false,
	});
}

registerProjectProgressTask("backup", {
	isActive: () => backupProgressState != null,
	restore: restoreBackupProgress,
});

function cancelBackupProgress() {
	if (backupProgressState == null) return;
	setBackupProgressState({
		...backupProgressState,
		cancelRequested: true,
		minimized: false,
	});
	backupProgressState.cancel();
}

export function startProjectBackup({
	projectPath,
	header,
}: {
	projectPath: string;
	header?: React.ReactNode;
}) {
	const activeTask = getActiveProjectProgressTask();
	if (activeTask != null) {
		restoreProjectProgressTask(activeTask);
		toastNormal(
			tc(
				activeTask === "backup"
					? "projects:toast:backup already running"
					: "projects:toast:copy already running",
			),
		);
		return Promise.resolve("cancelled" as const);
	}

	const [cancel, commandPromise] = callAsyncCommand(
		commands.projectCreateBackup,
		[projectPath],
		updateBackupProgress,
	);

	const promise = commandPromise
		.then((result) => {
			if (!header) {
				if (result === "cancelled") {
					toastNormal(tc("projects:toast:backup canceled"));
				} else {
					toastSuccess(tc("projects:toast:backup succeeded"));
				}
			}
			setBackupProgressState(null);
			return result;
		})
		.catch((error) => {
			setBackupProgressState(null);
			throw error;
		});

	setBackupProgressState({
		projectPath,
		header,
		progress: initialBackupProgress,
		minimized: false,
		cancelRequested: false,
		cancel,
		promise,
	});

	return promise;
}

export function BackupProjectProgressHost() {
	const state = useSyncExternalStore(
		subscribeBackupProgress,
		getBackupProgressSnapshot,
	);

	if (state == null) return null;

	return (
		<>
			<Dialog
				open={!state.minimized}
				onOpenChange={(open) => {
					if (!open) minimizeBackupProgress();
				}}
			>
				<DialogContent
					className="max-h-[calc(100dvh-(var(--spacing)*8))] overflow-y-auto"
					onEscapeKeyDown={(event) => {
						event.preventDefault();
						minimizeBackupProgress();
					}}
				>
					<DialogHeader>
						<DialogTitle>
							{state.header ?? tc("projects:dialog:backup header")}
						</DialogTitle>
					</DialogHeader>
					<div>
						{state.cancelRequested ? (
							<p>{tc("projects:manage:progress:cancelling")}</p>
						) : (
							<>
								<p>{tc("projects:dialog:creating backup...")}</p>
								<p>
									{tc("projects:dialog:proceed k/n", {
										count: state.progress.proceed,
										total: state.progress.total,
									})}
								</p>
								<p className={"overflow-hidden w-full whitespace-pre"}>
									{state.progress.last_proceed || "Collecting files..."}
								</p>
								<Progress
									value={state.progress.proceed}
									max={state.progress.total}
								/>
							</>
						)}
					</div>
					<DialogFooter>
						<Button
							className="mr-1"
							disabled={state.cancelRequested}
							onClick={() => cancelBackupProgress()}
						>
							{state.cancelRequested
								? tc("projects:manage:progress:cancelling")
								: tc("general:button:cancel")}
						</Button>
						<Button className="gap-2" onClick={minimizeBackupProgress}>
							<Minimize2 className="size-4" />
							{tc("projects:manage:progress:minimize")}
						</Button>
					</DialogFooter>
				</DialogContent>
			</Dialog>
			{state.minimized && (
				<Button
					className="fixed bottom-4 right-4 z-50 gap-2 shadow-2xl"
					onClick={restoreBackupProgress}
				>
					<RefreshCw className="size-4 animate-spin" />
					{tc("projects:dialog:backup restore")}
				</Button>
			)}
		</>
	);
}
