"use client";

import {
	Background,
	type Connection,
	ReactFlow,
	useEdgesState,
	useNodesState,
} from "@xyflow/react";
import { useCallback, useEffect, useState } from "react";
import { TechSelector } from "./TechSelector";
import "@xyflow/react/dist/style.css";
import { initialNodes } from "@/lib/constant";
import { CommandDisplay } from "./CommandDisplay";
import { TechNodeComponent } from "./TechNodeComponent";

// Define initial edges with proper connections
const initialEdges = [
	{ id: "bun-hono", source: "bun", target: "hono", animated: true },
	{ id: "bun-tanstack", source: "bun", target: "tanstack", animated: true },
	{ id: "hono-sqlite", source: "hono", target: "sqlite", animated: true },
	{ id: "sqlite-drizzle", source: "sqlite", target: "drizzle", animated: true },
	{
		id: "hono-better-auth",
		source: "hono",
		target: "better-auth",
		animated: true,
	},
	{ id: "bun-tailwind", source: "bun", target: "tailwind", animated: true },
	{
		id: "tailwind-shadcn",
		source: "tailwind",
		target: "shadcn",
		animated: true,
	},
];

const nodeTypes = {
	techNode: TechNodeComponent,
};

interface ActiveNodes {
	backend: string;
	database: string;
	orm: string;
	auth: string;
	packageManager: string;
	addons: {
		docker: boolean;
		githubActions: boolean;
		seo: boolean;
		git: boolean;
	};
}

const CustomizableStack = () => {
	const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
	const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
	const [activeNodes, setActiveNodes] = useState<ActiveNodes>({
		backend: "hono",
		database: "sqlite",
		orm: "drizzle",
		auth: "better-auth",
		packageManager: "npm",
		addons: {
			docker: false,
			githubActions: false,
			seo: false,
			git: true,
		},
	});
	const [windowSize, setWindowSize] = useState("lg");
	const [command, setCommand] = useState("npx create-better-t-stack my-app -y");

	useEffect(() => {
		const handleResize = () => {
			if (window.innerWidth < 1024 && window.innerWidth > 768) {
				setWindowSize("md");
			} else if (window.innerWidth < 768) {
				setWindowSize("sm");
			} else {
				setWindowSize("lg");
			}
		};

		handleResize();
		window.addEventListener("resize", handleResize);
		return () => window.removeEventListener("resize", handleResize);
	}, []);

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		// Generate command whenever activeNodes changes and update the command state
		setCommand(generateCommand());
	}, [activeNodes]);

	// Function to remove connections related to specific category
	const removeConnectionsByCategory = useCallback(
		(category: string) => {
			setEdges((eds) => {
				return eds.filter((edge) => {
					// Find source and target nodes
					const sourceNode = nodes.find((n) => n.id === edge.source);
					const targetNode = nodes.find((n) => n.id === edge.target);

					if (!sourceNode || !targetNode) return true;

					// Remove edges connected to the category being changed
					if (targetNode.data.category === category) return false;

					// Remove edges that connect from the category being changed
					if (sourceNode.data.category === category) return false;

					// For database changes, also remove ORM connections
					if (category === "database" && targetNode.data.category === "orm")
						return false;

					return true;
				});
			});
		},
		[nodes, setEdges],
	);

	const handleTechSelect = useCallback(
		(category: string, techId: string) => {
			// Update active nodes state
			setActiveNodes((prev) => ({
				...prev,
				[category]: techId,
				...(category === "addons" && {
					addons: {
						...prev.addons,
						[techId]: !prev.addons[techId as keyof typeof prev.addons],
					},
				}),
			}));

			// Update node active states
			setNodes((nds) =>
				nds.map((node) => ({
					...node,
					data: {
						...node.data,
						isActive: node.data.isStatic
							? true
							: node.data.category === category
								? node.id === techId
								: node.data.isActive,
					},
				})),
			);

			// Remove old connections for this category
			removeConnectionsByCategory(category);

			// Create new connections based on the selected tech
			if (category === "backend") {
				// Connect backend to database, auth, and other core components
				const database = activeNodes.database;
				const auth = activeNodes.auth;

				setEdges((eds) => [
					...eds,
					{
						id: `bun-${techId}`,
						source: "bun",
						target: techId,
						animated: true,
					},
					{
						id: `${techId}-${database}`,
						source: techId,
						target: database,
						animated: true,
					},
					{
						id: `${techId}-${auth}`,
						source: techId,
						target: auth,
						animated: true,
					},
					{
						id: `${techId}-tanstack`,
						source: techId,
						target: "tanstack",
						animated: true,
					},
				]);
			} else if (category === "database") {
				// Connect backend to database and database to ORM
				const orm = activeNodes.orm;

				setEdges((eds) => [
					...eds,
					{
						id: `hono-${techId}`,
						source: "hono",
						target: techId,
						animated: true,
					},
					// Only add ORM connection if database is not "no-database"
					...(techId !== "no-database"
						? [
								{
									id: `${techId}-${orm}`,
									source: techId,
									target: orm,
									animated: true,
								},
							]
						: []),
				]);
			} else if (category === "orm") {
				// Connect database to ORM
				const database = activeNodes.database;

				// Only add connection if database is not "no-database"
				if (database !== "no-database") {
					setEdges((eds) => [
						...eds,
						{
							id: `${database}-${techId}`,
							source: database,
							target: techId,
							animated: true,
						},
					]);
				}
			} else if (category === "auth") {
				// Connect backend to auth
				setEdges((eds) => [
					...eds,
					{
						id: `hono-${techId}`,
						source: "hono",
						target: techId,
						animated: true,
					},
				]);
			}
		},
		[activeNodes, setNodes, setEdges, removeConnectionsByCategory],
	);

	const isValidConnection = useCallback(
		(connection: Connection) => {
			const sourceNode = nodes.find((n) => n.id === connection.source);
			const targetNode = nodes.find((n) => n.id === connection.target);

			if (!sourceNode || !targetNode) return false;

			// Define valid connection patterns
			if (sourceNode.id === "hono" && targetNode.data.category === "database") {
				return ["postgres", "sqlite", "no-database"].includes(targetNode.id);
			}

			if (sourceNode.id === "hono" && targetNode.data.category === "auth") {
				return ["better-auth", "no-auth"].includes(targetNode.id);
			}

			if (
				["postgres", "sqlite"].includes(sourceNode.id) &&
				targetNode.data.category === "orm"
			) {
				return ["drizzle", "prisma"].includes(targetNode.id);
			}

			return false;
		},
		[nodes],
	);

	const onConnect = useCallback(
		(connection: Connection) => {
			if (!isValidConnection(connection)) return;

			const targetNode = nodes.find((n) => n.id === connection.target);
			if (!targetNode || !targetNode.data.category) return;

			// Remove existing connections for the category we're connecting to
			removeConnectionsByCategory(targetNode.data.category);

			// Update active nodes state
			setActiveNodes((prev) => ({
				...prev,
				[targetNode.data.category]: connection.target,
			}));

			// Update node active states
			setNodes((nds) =>
				nds.map((node) => ({
					...node,
					data: {
						...node.data,
						isActive: node.data.isStatic
							? true
							: node.data.category === targetNode.data.category
								? node.id === connection.target
								: node.data.isActive,
					},
				})),
			);

			// Add the new connection
			setEdges((eds) => [
				...eds,
				{
					id: `${connection.source}-${connection.target}`,
					source: connection.source,
					target: connection.target,
					animated: true,
				},
			]);

			// If connecting to database, also connect to the active ORM
			if (
				targetNode.data.category === "database" &&
				targetNode.id !== "no-database"
			) {
				const activeOrm = nodes.find(
					(n) => n.data.category === "orm" && n.data.isActive,
				);
				if (activeOrm) {
					setEdges((eds) => [
						...eds,
						{
							id: `${connection.target}-${activeOrm.id}`,
							source: connection.target,
							target: activeOrm.id,
							animated: true,
						},
					]);
				}
			}
		},
		[nodes, setEdges, setNodes, removeConnectionsByCategory, isValidConnection],
	);

	const generateCommand = useCallback(() => {
		// Start with the base command
		const command = "npx create-better-t-stack my-app";
		const flags: string[] = [];

		const isAllDefaults =
			activeNodes.database === "sqlite" &&
			activeNodes.auth === "better-auth" &&
			activeNodes.orm === "drizzle" &&
			activeNodes.packageManager === "npm" &&
			activeNodes.addons.git === true &&
			!activeNodes.addons.docker &&
			!activeNodes.addons.githubActions &&
			!activeNodes.addons.seo;

		// If using all defaults, just use -y flag
		if (isAllDefaults) {
			return `${command} -y`;
		}

		// Database options
		if (activeNodes.database === "postgres") {
			flags.push("--postgres");
		} else if (activeNodes.database === "sqlite") {
			flags.push("--sqlite");
		} else if (activeNodes.database === "no-database") {
			flags.push("--no-database");
		}

		// Authentication options
		if (activeNodes.auth === "better-auth") {
			flags.push("--auth");
		} else if (activeNodes.auth === "no-auth") {
			flags.push("--no-auth");
		}

		// ORM options
		if (activeNodes.orm === "drizzle") {
			flags.push("--drizzle");
		} else if (activeNodes.orm === "prisma") {
			flags.push("--prisma");
		}

		// Package manager options
		if (activeNodes.packageManager !== "npm") {
			flags.push(`--${activeNodes.packageManager}`);
		}

		// Feature flags
		if (activeNodes.addons.docker) {
			flags.push("--docker");
		}

		if (activeNodes.addons.githubActions) {
			flags.push("--github-actions");
		}

		if (activeNodes.addons.seo) {
			flags.push("--seo");
		}

		if (!activeNodes.addons.git) {
			flags.push("--no-git");
		}

		return flags.length > 0 ? `${command} ${flags.join(" ")}` : command;
	}, [activeNodes]);

	return (
		<div className="relative w-full max-w-5xl mx-auto z-50 mt-24">
			<div className="absolute -top-16 left-0 right-0 mx-auto flex justify-center z-50">
				<CommandDisplay command={command} />
			</div>

			<div className="relative rounded-xl border border-gray-800 overflow-hidden">
				<div className="absolute inset-0 backdrop-blur-3xl bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-pink-500/10" />

				<div className="absolute left-0 top-0 bottom-0 lg:w-52 md:w-44 w-36 z-50 bg-gray-950/30 border-r border-gray-800/50">
					<TechSelector onSelect={handleTechSelect} activeNodes={activeNodes} />
				</div>

				<div className="max-sm:hidden bg-gray-950/30 lg:p-4 p-1 absolute lg:top-4 top-2 lg:right-4 right-2 z-50 w-80 rounded-xl border border-gray-800 backdrop-blur-3xl">
					<div className="lg:text-sm text-xs text-gray-300 text-center">
						Select technologies from the left panel to customize your stack. The
						graph will automatically update connections.
					</div>
				</div>

				<div className="h-[600px] lg:pl-52 md:pl-44 pl-36 relative backdrop-blur-sm bg-gray-950/50">
					<ReactFlow
						nodes={nodes}
						edges={edges}
						onNodesChange={onNodesChange}
						onEdgesChange={onEdgesChange}
						onConnect={onConnect}
						nodeTypes={nodeTypes}
						fitView
						maxZoom={windowSize === "sm" ? 0.6 : windowSize === "md" ? 0.6 : 1}
						zoomOnScroll={false}
						zoomOnPinch={false}
						preventScrolling={false}
						nodesConnectable={true}
						nodesDraggable={true}
						connectOnClick={true}
						deleteKeyCode="Delete"
						selectionKeyCode="Shift"
						proOptions={{
							hideAttribution: true,
						}}
					>
						<Background
							className="bg-gray-950/5"
							color="#1e293b"
							gap={12}
							size={1}
						/>
					</ReactFlow>
				</div>
			</div>
		</div>
	);
};

export default CustomizableStack;
