import "@xyflow/react/dist/style.css";
import {
	ReactFlow,
	Background,
	Controls,
	Connection,
	BackgroundVariant,
	Edge,
	useReactFlow,
	Panel,
	useStoreApi,
} from "@xyflow/react";
import { useStateStore, AppState, useZoom } from "@/lib/state";
import { useShallow } from "zustand/react/shallow";
import { useCallback, useEffect, useMemo } from "react";
import { NodeGatewayHttps } from "@/components/node-gateway-https";
import { NodeApp } from "@/components/node-app";
import { NodeVolume } from "./components/node-volume";
import { NodePostgreSQL } from "./components/node-postgresql";
import { NodeMongoDB } from "./components/node-mongodb";
import { NodeGithub } from "./components/node-github";
import { Actions } from "./components/actions";
import { NodeGatewayTCP } from "./components/node-gateway-tcp";
import { NodeNetwork } from "./components/node-network";
import { AppNode } from "config";

const selector = (state: AppState) => ({
	nodes: state.nodes,
	edges: state.edges,
	onNodesChange: state.onNodesChange,
	onEdgesChange: state.onEdgesChange,
	onConnect: state.onConnect,
});

export function Canvas({ className }: { className?: string }) {
	const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useStateStore(useShallow(selector));
	const store = useStateStore();
	const instance = useReactFlow();
	const flow = useStoreApi();
	const viewport = useZoom();
	useEffect(() => {
		store.setViewport({
			width: flow.getState().width,
			height: flow.getState().height,
			transformX: flow.getState().transform[0],
			transformY: flow.getState().transform[1],
			transformZoom: flow.getState().transform[2],
		});
	}, [store, flow]);
	useEffect(() => {
		instance.setViewport(viewport);
	}, [viewport, instance]);
	const nodeTypes = useMemo(
		() => ({
			network: NodeNetwork,
			app: NodeApp,
			"gateway-https": NodeGatewayHttps,
			"gateway-tcp": NodeGatewayTCP,
			volume: NodeVolume,
			postgresql: NodePostgreSQL,
			mongodb: NodeMongoDB,
			github: NodeGithub,
		}),
		[],
	);
	const isValidConnection = useCallback(
		(c: Edge | Connection) => {
			if (c.source === c.target) {
				return false;
			}
			const sn = instance.getNode(c.source)! as AppNode;
			const tn = instance.getNode(c.target)! as AppNode;

			if (sn.type === "github") {
				return c.targetHandle === "repository";
			}
			if (sn.type === "app") {
				if (c.sourceHandle === "ports" && (!sn.data.ports || sn.data.ports.length === 0)) {
					return false;
				}
			}
			if (tn.type === "gateway-https") {
				if (c.targetHandle === "https" && tn.data.https !== undefined) {
					return false;
				}
			}
			if (sn.type === "volume") {
				if (c.targetHandle !== "volume") {
					return false;
				}
				return true;
			}
			if (tn.type === "network") {
				if (c.sourceHandle !== "subdomain") {
					return false;
				}
				if (sn.type !== "gateway-https" && sn.type !== "gateway-tcp") {
					return false;
				}
			}
			return true;
		},
		[instance],
	);
	return (
		<div style={{ width: "100%", height: "100%" }} className={className}>
			<ReactFlow
				nodeTypes={nodeTypes}
				nodes={nodes}
				edges={edges}
				onNodesChange={onNodesChange}
				onEdgesChange={onEdgesChange}
				onConnect={onConnect}
				isValidConnection={isValidConnection}
				fitView
				proOptions={{ hideAttribution: true }}
			>
				<Controls showInteractive={false} />
				<Background
					variant={store.mode === "deploy" ? BackgroundVariant.Dots : BackgroundVariant.Lines}
					gap={12}
					size={1}
				/>
				<Panel position="top-right">
					<Actions />
				</Panel>
			</ReactFlow>
		</div>
	);
}

export default Canvas;
