import { nodeLabelFull, useMessages, useProjectId, useStateStore } from "@/lib/state";
import { Button } from "./ui/button";
import { useCallback, useEffect, useState } from "react";
import { AppNode } from "config";
import { useNodes, useReactFlow } from "@xyflow/react";
import { useToast } from "@/hooks/use-toast";
import {
	DropdownMenuGroup,
	DropdownMenuItem,
	DropdownMenu,
	DropdownMenuContent,
	DropdownMenuTrigger,
} from "./ui/dropdown-menu";
import { Ellipsis, LoaderCircle, Plus } from "lucide-react";
import { ImportModal } from "./import-modal";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog";
import { Resources } from "./resources";

function toNodeType(t: string): string {
	if (t === "ingress") {
		return "gateway-https";
	} else if (t === "service") {
		return "app";
	} else {
		return t;
	}
}

export function Actions() {
	const { toast } = useToast();
	const store = useStateStore();
	const projectId = useProjectId();
	const nodes = useNodes<AppNode>();
	const messages = useMessages();
	const instance = useReactFlow();
	const [ok, setOk] = useState(false);
	const [loading, setLoading] = useState(false);
	const [reloading, setReloading] = useState(false);
	const [showImportModal, setShowImportModal] = useState(false);
	const [showResourcesModal, setShowResourcesModal] = useState(false);
	const info = useCallback(
		(title: string, description?: string, duration?: number) => {
			return toast({
				title,
				description,
				duration: duration ?? 2000,
			});
		},
		[toast],
	);
	const error = useCallback(
		(title: string, description?: string, duration?: number) => {
			return toast({
				variant: "destructive",
				title,
				description,
				duration: duration ?? 5000,
			});
		},
		[toast],
	);
	useEffect(() => {
		setOk(!messages.some((m) => m.type === "FATAL"));
	}, [messages, setOk]);
	const monitor = useCallback(async () => {
		const m = async function () {
			const resp = await fetch(`/api/project/${projectId}/status`, {
				method: "GET",
				headers: {
					"Content-Type": "application/json",
				},
			});
			if (resp.status !== 200) {
				return;
			}
			const data: { type: string; name: string; status: string }[] = await resp.json();
			for (const n of nodes) {
				if (n.type === "network") {
					continue;
				}
				const d = data.find((d) => n.type === toNodeType(d.type) && nodeLabelFull(n) === d.name);
				if (d !== undefined) {
					store.updateNodeData(n.id, {
						state: d?.status,
					});
				}
			}
			if (data.find((d) => d.status !== "success" && d.status != "failure") !== undefined) {
				setTimeout(m, 1000);
			}
		};
		setTimeout(m, 100);
	}, [projectId, nodes, store]);
	const deploy = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		setLoading(true);
		try {
			const resp = await fetch(`/api/project/${projectId}/deploy`, {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
				body: JSON.stringify({
					type: "graph",
					graph: instance.toObject(),
				}),
			});
			if (resp.ok) {
				store.setMode("deploy");
				info("Deployment succeeded");
				monitor();
			} else {
				error("Deployment failed");
			}
			store.refreshEnv();
		} catch {
			error("Deployment failed");
		} finally {
			setLoading(false);
		}
	}, [projectId, instance, setLoading, info, error, monitor, store]);
	const save = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		const resp = await fetch(`/api/project/${projectId}/saved`, {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
			},
			body: JSON.stringify({
				type: "graph",
				graph: instance.toObject(),
			}),
		});
		if (resp.ok) {
			info("Save succeeded");
		} else {
			error("Save failed", await resp.text());
		}
	}, [projectId, instance, info, error]);
	const restoreSaved = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		const resp = await fetch(`/api/project/${projectId}/saved/${store.mode === "deploy" ? "deploy" : "draft"}`, {
			method: "GET",
		});
		const inst = await resp.json();
		const { x = 0, y = 0, zoom = 1 } = inst.state.viewport;
		store.setNodes(inst.state.nodes || []);
		store.setEdges(inst.state.edges || []);
		instance.setViewport({ x, y, zoom });
	}, [projectId, instance, store]);
	const clear = useCallback(() => {
		store.setEdges([]);
		store.setNodes(store.nodes.filter((n) => n.type === "network"));
	}, [store]);
	const edit = useCallback(async () => {
		store.setMode("edit");
	}, [store]);
	// TODO(gio): refresh projects
	const deleteProject = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		if (!confirm("Are you sure you want to delete this project? This action cannot be undone.")) {
			return;
		}
		const resp = await fetch(`/api/project/${projectId}`, {
			method: "DELETE",
			headers: {
				"Content-Type": "application/json",
			},
		});
		if (resp.ok) {
			clear();
			store.setProject(undefined);
			info("Project deleted");
		} else {
			error("Failed to delete project", await resp.text());
		}
	}, [store, clear, projectId, info, error]);
	const reload = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		setReloading(true);
		const { dismiss } = info("Reloading services", "This may take a while...", Infinity);
		try {
			const resp = await fetch(`/api/project/${projectId}/reload`, {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
			});
			if (resp.ok) {
				dismiss();
				info("Reloaded services successfully");
			} else {
				dismiss();
				error("Reload failed", await resp.text());
			}
		} catch (e) {
			dismiss();
			error("Reload failed", e instanceof Error ? e.message : undefined);
		} finally {
			setReloading(false);
		}
	}, [projectId, info, error]);
	const removeDeployment = useCallback(async () => {
		if (projectId == null) {
			return;
		}
		if (!confirm("Are you sure you want to remove this deployment? This action cannot be undone.")) {
			return;
		}
		setReloading(true);
		try {
			const resp = await fetch(`/api/project/${projectId}/remove-deployment`, {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
			});
			if (resp.ok) {
				info("Deployment removed successfully");
				store.setMode("edit");
			} else {
				const errorData = await resp.json();
				error("Failed to remove deployment", errorData.error || "Unknown error");
			}
		} catch (e) {
			error("Failed to remove deployment", e instanceof Error ? e.message : undefined);
		} finally {
			store.refreshEnv();
			setReloading(false);
		}
	}, [projectId, info, error, store]);
	const [deployProps, setDeployProps] = useState<{ loading?: boolean; disabled?: boolean }>({
		loading: false,
		disabled: false,
	});
	const [reloadProps, setReloadProps] = useState<{ loading?: boolean; disabled?: boolean }>({
		loading: false,
		disabled: false,
	});
	useEffect(() => {
		if (loading) {
			setDeployProps({ loading: true, disabled: true });
		} else if (ok) {
			setDeployProps({ disabled: false });
		} else {
			setDeployProps({ disabled: true });
		}

		if (reloading) {
			setReloadProps({ loading: true, disabled: true });
		} else {
			setReloadProps({ disabled: projectId === undefined });
		}
	}, [ok, loading, reloading, projectId]);
	if (store.mode === "deploy") {
		return (
			<div className="flex flex-row gap-1 items-center">
				<Button
					onClick={() => store.setBuildMode(store.buildMode === "overview" ? "canvas" : "overview")}
					{...reloadProps}
				>
					{store.buildMode === "overview" ? "Canvas" : "Overview"}
				</Button>
				<Button onClick={edit} {...reloadProps}>
					Edit
				</Button>
				<DropdownMenu>
					<DropdownMenuTrigger>
						<Button size="icon">
							<Ellipsis />
						</Button>
					</DropdownMenuTrigger>
					<DropdownMenuContent className="w-56">
						<DropdownMenuGroup>
							<DropdownMenuItem
								onClick={reload}
								className="cursor-pointer hover:bg-gray-200"
								{...reloadProps}
							>
								{reloadProps.loading ? (
									<>
										<LoaderCircle className="animate-spin" />
										Reloading...
									</>
								) : (
									"Reload services"
								)}
							</DropdownMenuItem>
							<DropdownMenuItem
								onClick={removeDeployment}
								disabled={projectId === undefined}
								className="cursor-pointer hover:bg-gray-200"
							>
								Remove deployment
							</DropdownMenuItem>
							<DropdownMenuItem
								onClick={deleteProject}
								disabled={projectId === undefined}
								className="cursor-pointer hover:bg-gray-200"
							>
								Delete project
							</DropdownMenuItem>
						</DropdownMenuGroup>
					</DropdownMenuContent>
				</DropdownMenu>
			</div>
		);
	} else {
		return (
			<>
				<div className="flex flex-row gap-1 items-center">
					<Button onClick={() => store.setBuildMode(store.buildMode === "overview" ? "canvas" : "overview")}>
						{store.buildMode === "overview" ? "Canvas" : "Overview"}
					</Button>
					<Button onClick={() => setShowResourcesModal(true)}>
						<Plus className="w-4 h-4 mr-1" />
						Add
					</Button>
					<Button onClick={deploy} {...deployProps}>
						{deployProps.loading ? (
							<>
								<LoaderCircle className="animate-spin" />
								Deploying...
							</>
						) : (
							"Deploy"
						)}
					</Button>
					<Button onClick={save}>Save</Button>
					<Button onClick={() => setShowImportModal(true)}>Import</Button>
					<DropdownMenu>
						<DropdownMenuTrigger>
							<Button size="icon">
								<Ellipsis />
							</Button>
						</DropdownMenuTrigger>
						<DropdownMenuContent className="w-56">
							<DropdownMenuGroup>
								<DropdownMenuItem
									onClick={restoreSaved}
									disabled={projectId === undefined}
									className="cursor-pointer hover:bg-gray-200"
								>
									Restore
								</DropdownMenuItem>
								<DropdownMenuItem onClick={clear} className="cursor-pointer hover:bg-gray-200">
									Clear
								</DropdownMenuItem>
								<DropdownMenuItem
									onClick={deleteProject}
									disabled={projectId === undefined}
									className="cursor-pointer hover:bg-gray-200"
								>
									Delete project
								</DropdownMenuItem>
							</DropdownMenuGroup>
						</DropdownMenuContent>
					</DropdownMenu>
				</div>
				<ImportModal open={showImportModal} onOpenChange={setShowImportModal} />
				<Dialog open={showResourcesModal} onOpenChange={setShowResourcesModal}>
					<DialogContent className="sm:max-w-md">
						<DialogHeader>
							<DialogTitle>Add Resources</DialogTitle>
						</DialogHeader>
						<div className="py-4">
							<Resources onResourceAdded={() => setShowResourcesModal(false)} />
						</div>
					</DialogContent>
				</Dialog>
			</>
		);
	}
}
