import { useCallback, useEffect, useState, useMemo } from "react";
import { useProjectId, useEnv } from "@/lib/state";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@/components/ui/resizable";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useToast } from "@/hooks/use-toast";
import { Check, Ellipsis, LoaderCircle, LogsIcon, X, RefreshCw, Power } from "lucide-react";
import { XTerm } from "@/components/XTerm";

export function Logs() {
	const { toast } = useToast();
	const projectId = useProjectId();
	const env = useEnv();

	const [selectedServiceForLogs, setSelectedServiceForLogs] = useState<string | null>(null);
	const [selectedWorkerIdForLogs, setSelectedWorkerIdForLogs] = useState<string | null>(null);

	const [logs, setLogs] = useState<string>("");

	useEffect(() => {
		console.log("selectedServiceForLogs", selectedServiceForLogs);
		if (!selectedServiceForLogs || !selectedWorkerIdForLogs || !projectId) {
			setLogs("");
			return;
		}

		setLogs(""); // Clear logs for the new selection

		const eventSource = new EventSource(
			`/api/project/${projectId}/logs/${selectedServiceForLogs}/${selectedWorkerIdForLogs}`,
		);

		eventSource.onmessage = (event) => {
			try {
				const data = JSON.parse(event.data);
				if (data.logs) {
					setLogs((prevLogs) => prevLogs + data.logs + "\n");
				}
			} catch (e) {
				console.error("Failed to parse log data:", e);
			}
		};

		eventSource.onerror = () => {
			toast({
				variant: "destructive",
				title: "Log stream error",
				description: "Connection to the log stream has been closed.",
			});
			eventSource.close();
		};

		return () => {
			eventSource.close();
		};
	}, [selectedServiceForLogs, selectedWorkerIdForLogs, projectId, toast]);

	const sortedServices = useMemo(
		() => (env?.services ? [...env.services].sort((a, b) => a.name.localeCompare(b.name)) : []),
		[env?.services],
	);

	const handleViewLogsClick = useCallback(
		(serviceName: string, workerId: string) => {
			setSelectedServiceForLogs(serviceName);
			setSelectedWorkerIdForLogs(workerId);
		},
		[setSelectedServiceForLogs, setSelectedWorkerIdForLogs],
	);

	useEffect(() => {
		if (sortedServices.length > 0 && sortedServices[0].workers && sortedServices[0].workers.length > 0) {
			if (!selectedServiceForLogs || !selectedWorkerIdForLogs) {
				handleViewLogsClick(sortedServices[0].name, sortedServices[0].workers[0].id);
				return;
			}
			const service = sortedServices.find((s) => s.name === selectedServiceForLogs);
			if (service == null) {
				handleViewLogsClick(sortedServices[0].name, sortedServices[0].workers[0].id);
				return;
			}
			const worker = service.workers.find((w) => w.id === selectedWorkerIdForLogs);
			if (worker == null) {
				handleViewLogsClick(sortedServices[0].name, sortedServices[0].workers[0].id);
			}
		}
	}, [sortedServices, selectedServiceForLogs, selectedWorkerIdForLogs, handleViewLogsClick]);

	const handleReloadWorkerClick = useCallback(
		(serviceName: string, workerId: string) => {
			if (!projectId) return;
			toast({
				title: "Worker reload initiated",
				description: `Worker ${serviceName} / ${workerId} is reloading.`,
			});
			fetch(`/api/project/${projectId}/reload/${serviceName}/${workerId}`, {
				method: "POST",
			})
				.then((resp) => {
					if (!resp.ok) {
						throw new Error(`Failed to reload worker: ${resp.statusText}`);
					}
					toast({
						title: "Worker reloaded",
						description: `Successfully reloaded worker ${serviceName} / ${workerId}`,
					});
				})
				.catch((e) => {
					console.error(e);
					toast({
						variant: "destructive",
						title: "Failed to reload worker",
						description: `Failed to reload worker ${serviceName} / ${workerId} in service`,
					});
				});
		},
		[projectId, toast],
	);

	const handleQuitWorkerClick = useCallback(
		(serviceName: string, workerId: string) => {
			if (!projectId) return;
			if (!window.confirm(`Are you sure you want to terminate worker ${workerId} for service ${serviceName}?`)) {
				return;
			}
			toast({
				title: "Worker ${serviceName} / ${workerId} is being terminated.",
			});
			fetch(`/api/project/${projectId}/quitquitquit/${serviceName}/${workerId}`, {
				method: "POST",
			})
				.then((resp) => {
					if (!resp.ok) {
						toast({
							title: `Failed to terminate worker ${serviceName} / ${workerId}`,
							variant: "destructive",
						});
					} else {
						toast({
							title: `Successfully terminated worker ${serviceName} / ${workerId}`,
						});
					}
				})
				.catch((e) => {
					console.error(e);
					toast({
						title: `Failed to terminate worker ${serviceName} / ${workerId}`,
						variant: "destructive",
					});
				});
		},
		[projectId, toast],
	);

	const defaultExpandedServiceNames = useMemo(() => sortedServices.map((s) => s.name), [sortedServices]);
	const defaultExpandedFirstWorkerId = useMemo(() => {
		if (sortedServices.length > 0 && sortedServices[0].workers && sortedServices[0].workers.length > 0) {
			return sortedServices[0].workers[0].id;
		}
		return undefined;
	}, [sortedServices]);

	return (
		<ResizablePanelGroup direction="horizontal" className="h-full w-full">
			<ResizablePanel defaultSize={15}>
				<div className="flex flex-col h-full p-2 gap-2 overflow-y-auto">
					{sortedServices.length > 0 ? (
						<Accordion type="multiple" className="w-full" defaultValue={defaultExpandedServiceNames}>
							{sortedServices.map((service, serviceIndex) => (
								<AccordionItem value={service.name} key={service.name}>
									<AccordionTrigger className="py-1">{service.name}</AccordionTrigger>
									<AccordionContent className="pl-2">
										{service.workers && service.workers.length > 0 ? (
											<Accordion
												type="single"
												collapsible
												className="w-full"
												defaultValue={
													serviceIndex === 0 ? defaultExpandedFirstWorkerId : undefined
												}
											>
												{service.workers.map((worker) => (
													<AccordionItem value={worker.id} key={worker.id}>
														<AccordionTrigger className="py-1">
															{worker.id}
														</AccordionTrigger>
														<AccordionContent className="pl-2">
															<TooltipProvider>
																<div className="text-sm flex flex-col gap-1 items-start">
																	<Button
																		onClick={() =>
																			handleViewLogsClick(service.name, worker.id)
																		}
																		size="sm"
																		variant="link"
																		className="!px-0"
																	>
																		<LogsIcon className="w-4 h-4" />
																		View Logs
																	</Button>
																	<Button
																		onClick={() =>
																			handleReloadWorkerClick(
																				service.name,
																				worker.id,
																			)
																		}
																		size="sm"
																		variant="link"
																		className="!px-0"
																	>
																		<RefreshCw className="w-4 h-4" />
																		Reload Worker
																	</Button>
																	<Button
																		onClick={() =>
																			handleQuitWorkerClick(
																				service.name,
																				worker.id,
																			)
																		}
																		size="sm"
																		variant="link"
																		className="!px-0"
																	>
																		<Power className="w-4 h-4" />
																		Quit Worker
																	</Button>
																	{!worker.commit && (
																		<p className="flex items-center">
																			<FailureIcon />
																			Clone Repository
																		</p>
																	)}
																	<p>
																		Commit:
																		{worker.commit && (
																			<Tooltip>
																				<TooltipTrigger asChild>
																					<span className="inline-block">
																						<Badge
																							variant="outline"
																							className="ml-1 font-mono"
																						>
																							{worker.commit.hash.substring(
																								0,
																								8,
																							)}
																						</Badge>
																					</span>
																				</TooltipTrigger>
																				<TooltipContent
																					side="right"
																					className="flex flex-col gap-1"
																				>
																					<p>{worker.commit.message}</p>
																					<p>{worker.commit.hash}</p>
																				</TooltipContent>
																			</Tooltip>
																		)}
																	</p>
																	{worker.commands && worker.commands.length > 0 && (
																		<div>
																			Commands:
																			<ul className="list-none pl-0 font-['JetBrains_Mono']">
																				{worker.commands.map((cmd, index) => (
																					<li
																						key={index}
																						className="rounded flex items-start"
																					>
																						<span className="inline-block w-6 flex-shrink-0">
																							<CommandStateIcon
																								state={cmd.state}
																							/>
																						</span>
																						<span className="font-mono break-all">
																							{cmd.command}
																						</span>
																					</li>
																				))}
																			</ul>
																		</div>
																	)}
																	{(!worker.commands ||
																		worker.commands.length === 0) && (
																		<p className="text-xs text-gray-500">
																			No commands for this worker.
																		</p>
																	)}
																</div>
															</TooltipProvider>
														</AccordionContent>
													</AccordionItem>
												))}
											</Accordion>
										) : (
											<p className="text-sm text-gray-500 p-2">
												No workers found for this service.
											</p>
										)}
									</AccordionContent>
								</AccordionItem>
							))}
						</Accordion>
					) : (
						<div className="text-center text-gray-500 mt-4">No services available.</div>
					)}
				</div>
			</ResizablePanel>
			<ResizableHandle withHandle />
			<ResizablePanel defaultSize={85}>
				<div className="flex flex-col h-full">
					{selectedServiceForLogs && selectedWorkerIdForLogs ? (
						<>
							<div className="p-2 border-b">
								Logs for: {selectedServiceForLogs} / {selectedWorkerIdForLogs}
							</div>
							<div className="flex-1 h-full p-4 bg-muted overflow-auto">
								<XTerm logs={logs} />
							</div>
						</>
					) : (
						<div className="flex-1 flex items-center justify-center h-full p-4 bg-muted text-sm text-gray-500">
							Click 'View Logs' on a worker to display logs here.
						</div>
					)}
				</div>
			</ResizablePanel>
		</ResizablePanelGroup>
	);
}

function WaitingIcon(): JSX.Element {
	return <Ellipsis className="w-4 h-4 mr-2 inline-block align-middle" />;
}
function RunningIcon(): JSX.Element {
	return <LoaderCircle className="animate-spin w-4 h-4 mr-2 inline-block align-middle" />;
}

function SuccessIcon(): JSX.Element {
	return <Check className="w-4 h-4 mr-2 inline-block align-middle" />;
}

function FailureIcon(): JSX.Element {
	return <X className="w-4 h-4 mr-2 inline-block align-middle" />;
}

function CommandStateIcon({ state }: { state: string | undefined }): JSX.Element | null {
	switch (state?.toLowerCase()) {
		case "running":
			return <RunningIcon />;
		case "success":
			return <SuccessIcon />;
		case "failure":
			return <FailureIcon />;
		case "waiting":
			return <WaitingIcon />;
		default:
			return null;
	}
}
