import { NodeRect } from "./node-rect";
import {
	GithubNode,
	nodeIsConnectable,
	nodeLabel,
	serviceAnalyzisSchema,
	useStateStore,
	useGithubService,
	ServiceType,
	ServiceData,
	useGithubRepositories,
	useGithubRepositoriesLoading,
	useGithubRepositoriesError,
	useFetchGithubRepositories,
} from "@/lib/state";
import { useCallback, useEffect, useMemo, useState } from "react";
import { z } from "zod";
import { DeepPartial, EventType, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Form, FormControl, FormField, FormItem, FormMessage } from "./ui/form";
import { Handle, Position } from "@xyflow/react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
import { useProjectId } from "@/lib/state";
import { Alert, AlertDescription } from "./ui/alert";
import { AlertCircle, LoaderCircle, RefreshCw } from "lucide-react";
import { Button } from "./ui/button";
import { v4 as uuidv4 } from "uuid";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./ui/dialog";
import { Switch } from "./ui/switch";
import { Label } from "./ui/label";
import { NodeDetailsProps } from "@/lib/types";

export function NodeGithub(node: GithubNode) {
	const { id, selected } = node;
	const isConnectable = useMemo(() => nodeIsConnectable(node, "repository"), [node]);
	return (
		<NodeRect id={id} selected={selected} type={node.type} state={node.data.state}>
			<div style={{ padding: "10px 20px" }}>
				{nodeLabel(node)}
				<Handle
					id="repository"
					type={"source"}
					position={Position.Right}
					isConnectableStart={isConnectable}
					isConnectableEnd={isConnectable}
					isConnectable={isConnectable}
				/>
			</div>
		</NodeRect>
	);
}

const schema = z.object({
	repositoryId: z.number().optional(),
});

export function NodeGithubDetails({ node, disabled }: NodeDetailsProps<GithubNode>) {
	const { id, data } = node;
	const store = useStateStore();
	const projectId = useProjectId();
	const githubService = useGithubService();

	const storeRepos = useGithubRepositories();
	const isLoadingRepos = useGithubRepositoriesLoading();
	const repoError = useGithubRepositoriesError();
	const fetchStoreRepositories = useFetchGithubRepositories();

	const [isAnalyzing, setIsAnalyzing] = useState(false);
	const [showModal, setShowModal] = useState(false);
	const [discoveredServices, setDiscoveredServices] = useState<z.infer<typeof serviceAnalyzisSchema>[]>([]);
	const [selectedServices, setSelectedServices] = useState<Record<string, boolean>>({});

	const form = useForm<z.infer<typeof schema>>({
		resolver: zodResolver(schema),
		mode: "onChange",
		defaultValues: {
			repositoryId: data.repository?.id,
		},
	});

	useEffect(() => {
		form.reset({ repositoryId: data.repository?.id });
	}, [data.repository?.id, form]);

	useEffect(() => {
		const sub = form.watch(
			(
				value: DeepPartial<z.infer<typeof schema>>,
				{ name, type }: { name?: keyof z.infer<typeof schema> | undefined; type?: EventType | undefined },
			) => {
				if (type !== "change") {
					return;
				}
				switch (name) {
					case "repositoryId":
						if (value.repositoryId) {
							const repo = storeRepos.find((r) => r.id === value.repositoryId);
							if (repo) {
								store.updateNodeData<"github">(id, {
									repository: {
										id: repo.id,
										sshURL: repo.ssh_url,
										fullName: repo.full_name,
									},
								});
							}
						}
						break;
				}
			},
		);
		return () => sub.unsubscribe();
	}, [form, store, id, storeRepos]);

	const analyze = useCallback(async () => {
		if (!data.repository?.sshURL) return;

		setIsAnalyzing(true);
		try {
			const resp = await fetch(`/api/project/${projectId}/analyze`, {
				method: "POST",
				body: JSON.stringify({
					address: data.repository?.sshURL,
				}),
				headers: {
					"Content-Type": "application/json",
				},
			});
			const servicesResult = z.array(serviceAnalyzisSchema).safeParse(await resp.json());
			if (!servicesResult.success) {
				console.error(servicesResult.error);
				setIsAnalyzing(false);
				return;
			}

			setDiscoveredServices(servicesResult.data);
			const initialSelectedServices: Record<string, boolean> = {};
			servicesResult.data.forEach((service) => {
				initialSelectedServices[service.name] = true;
			});
			setSelectedServices(initialSelectedServices);
			setShowModal(true);
		} catch (err) {
			console.error("Analysis failed:", err);
		} finally {
			setIsAnalyzing(false);
		}
	}, [projectId, data.repository?.sshURL]);

	const handleImportServices = () => {
		discoveredServices.forEach((service) => {
			if (selectedServices[service.name]) {
				const newNodeData: Omit<ServiceData, "activeField" | "state"> = {
					label: service.name,
					repository: {
						id: data.repository!.id,
						repoNodeId: id,
					},
					info: service,
					type: "nodejs:24.0.2" as ServiceType,
					env: [],
					volume: [],
					preBuildCommands: "",
					isChoosingPortToConnect: false,
					envVars: [],
					ports: [],
				};
				const newNodeId = uuidv4();
				store.addNode({
					id: newNodeId,
					type: "app",
					data: newNodeData,
				});
				let edges = store.edges;
				edges = edges.concat({
					id: uuidv4(),
					source: id,
					sourceHandle: "repository",
					target: newNodeId,
					targetHandle: "repository",
				});
				store.setEdges(edges);
			}
		});
		setShowModal(false);
		setDiscoveredServices([]);
		setSelectedServices({});
	};

	const handleCancelModal = () => {
		setShowModal(false);
		setDiscoveredServices([]);
		setSelectedServices({});
	};

	return (
		<>
			<Form {...form}>
				<form className="space-y-2">
					<FormField
						control={form.control}
						name="repositoryId"
						render={({ field }) => (
							<FormItem>
								<div className="flex items-center gap-2 w-full">
									<div className="flex-grow">
										<Select
											onValueChange={(value) => field.onChange(Number(value))}
											value={field.value?.toString()}
											disabled={isLoadingRepos || !projectId || !githubService || disabled}
										>
											<FormControl>
												<SelectTrigger>
													<SelectValue
														placeholder={
															githubService
																? isLoadingRepos
																	? "Loading..."
																	: storeRepos.length === 0
																		? "No repositories found"
																		: "Select a repository"
																: "GitHub not configured"
														}
													/>
												</SelectTrigger>
											</FormControl>
											<SelectContent>
												{storeRepos.map((repo) => (
													<SelectItem key={repo.id} value={repo.id.toString()}>
														{repo.full_name}
														{repo.description && ` - ${repo.description}`}
													</SelectItem>
												))}
											</SelectContent>
										</Select>
									</div>
									{isLoadingRepos && (
										<Button variant="ghost" size="icon" disabled>
											<LoaderCircle className="h-5 w-5 animate-spin text-muted-foreground" />
										</Button>
									)}
									{!isLoadingRepos && githubService && (
										<Button
											variant="ghost"
											size="icon"
											onClick={fetchStoreRepositories}
											disabled={disabled}
											aria-label="Refresh repositories"
										>
											<RefreshCw className="h-5 w-5 text-muted-foreground" />
										</Button>
									)}
								</div>
								<FormMessage />
								{repoError && <p className="text-sm text-red-500 mt-1">{repoError}</p>}
								{!githubService && (
									<Alert variant="destructive" className="mt-2">
										<AlertCircle className="h-4 w-4" />
										<AlertDescription>
											Please configure Github Personal Access Token in the Integrations tab.
										</AlertDescription>
									</Alert>
								)}
							</FormItem>
						)}
					/>
				</form>
			</Form>
			<Button disabled={!data.repository?.sshURL || isAnalyzing || !githubService || disabled} onClick={analyze}>
				{isAnalyzing && <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />}
				Scan for services
			</Button>
			{showModal && (
				<Dialog open={showModal} onOpenChange={setShowModal}>
					<DialogContent className="sm:max-w-[425px]">
						<DialogHeader>
							<DialogTitle>Discovered Services</DialogTitle>
							<DialogDescription>Select the services you want to import.</DialogDescription>
						</DialogHeader>
						<div className="grid gap-4 py-4">
							{discoveredServices.map((service) => (
								<div key={service.name} className="flex flex-col space-y-2 p-2 border rounded-md">
									<div className="flex items-center space-x-2">
										<Switch
											id={service.name}
											checked={selectedServices[service.name]}
											onCheckedChange={(checked: boolean) =>
												setSelectedServices((prev) => ({
													...prev,
													[service.name]: checked,
												}))
											}
										/>
										<Label htmlFor={service.name} className="font-semibold">
											{service.name}
										</Label>
									</div>
									<div className="pl-6 text-sm text-gray-600">
										<p>
											<span className="font-medium">Location:</span> {service.location}
										</p>
										{service.configVars && service.configVars.length > 0 && (
											<div className="mt-1">
												<p className="font-medium">Environment Variables:</p>
												<ul className="list-disc list-inside pl-4">
													{service.configVars.map((envVar) => (
														<li key={envVar.name}>{envVar.name}</li>
													))}
												</ul>
											</div>
										)}
									</div>
								</div>
							))}
						</div>
						<DialogFooter>
							<Button variant="outline" onClick={handleCancelModal}>
								Cancel
							</Button>
							<Button onClick={handleImportServices}>Import</Button>
						</DialogFooter>
					</DialogContent>
				</Dialog>
			)}
		</>
	);
}
