import '@xyflow/react/dist/style.css';
import { ReactFlow, Background, Controls, Connection, BackgroundVariant, Edge, useReactFlow, Panel } from '@xyflow/react';
import { useStateStore, AppState, AppNode, useEnv } 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 './node-volume';
import { NodePostgreSQL } from './node-postgresql';
import { NodeMongoDB } from './node-mongodb';
import { NodeGithub } from './node-github';
import { Actions } from './actions';
import { NodeGatewayTCP } from './node-gateway-tcp';
import { NodeNetwork } from './node-network';

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

export function Canvas() {
    const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useStateStore(
        useShallow(selector),
    );
    const store = useStateStore();
    const flow = useReactFlow();
    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 = flow.getNode(c.source)! as AppNode;
        const tn = flow.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;
    }, [flow]);
    const env = useEnv();
    useEffect(() => {
        const networkNodes: AppNode[] = env.networks.map((n) => ({
            id: n.domain,
            type: "network",
            position: {
                x: 0,
                y: 0,
            },
            isConnectable: true,
            data: {
                domain: n.domain,
                label: n.domain,
                envVars: [],
                ports: [],
                state: "success", // TODO(gio): monitor network health
            },
        }));
        const prevNodes = store.nodes;
        const newNodes = networkNodes.concat(prevNodes.filter((n) => n.type !== "network"));
        // TODO(gio): actually compare
        if (prevNodes.length !== newNodes.length) {
            store.setNodes(newNodes);
        }
    }, [env, store]);
    return (
        <div style={{ width: '100%', height: '100%' }}>
            <ReactFlow
                nodeTypes={nodeTypes}
                nodes={nodes}
                edges={edges}
                onNodesChange={onNodesChange}
                onEdgesChange={onEdgesChange}
                onConnect={onConnect}
                isValidConnection={isValidConnection}
                fitView
                proOptions={{ hideAttribution: true }}
            >
                <Controls />
                <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
                <Panel position="bottom-right">
                    <Actions />
                </Panel>
            </ReactFlow>
        </div>
    );
}

export default Canvas;
