"use client";

import { useCallback } from "react";
import {
  ReactFlow,
  ReactFlowProvider,
  Background,
  Controls,
  MiniMap,
  BackgroundVariant,
  Panel,
} from "@xyflow/react";
import type { Node } from "@xyflow/react";

import { useGraphStore } from "../../stores/useGraphStore";
import { useAutoLayout } from "../../hooks/useAutoLayout";
import { bfsPath } from "../../lib/bfs-path";
import {
  BpmnActivityNode,
  BpmnSubprocessNode,
  BpmnStartEventNode,
  BpmnEndEventNode,
  BpmnTimerEventNode,
  BpmnMessageEventNode,
  BpmnGatewayNode,
  BpmnDataObjectNode,
  BpmnAnnotationNode,
  BpmnPoolNode,
  BpmnLaneNode,
  BpmnGroupNode,
  BpmnSequenceEdge,
  BpmnMessageEdge,
  BpmnAssociationEdge,
} from "../../types/bpmn";

const nodeTypes = {
  bpmnActivity: BpmnActivityNode,
  bpmnSubprocess: BpmnSubprocessNode,
  bpmnStartEvent: BpmnStartEventNode,
  bpmnEndEvent: BpmnEndEventNode,
  bpmnTimerEvent: BpmnTimerEventNode,
  bpmnMessageEvent: BpmnMessageEventNode,
  bpmnGateway: BpmnGatewayNode,
  bpmnDataObject: BpmnDataObjectNode,
  bpmnAnnotation: BpmnAnnotationNode,
  bpmnPool: BpmnPoolNode,
  bpmnLane: BpmnLaneNode,
  bpmnGroup: BpmnGroupNode,
};

const edgeTypes = {
  bpmnSequence: BpmnSequenceEdge,
  bpmnMessage: BpmnMessageEdge,
  bpmnAssociation: BpmnAssociationEdge,
};

/** Container node types that should not participate in BFS highlighting */
const CONTAINER_TYPES = new Set(["bpmnPool", "bpmnLane", "bpmnGroup"]);

function BpmnMarkerDefs() {
  return (
    <svg style={{ position: "absolute", width: 0, height: 0 }}>
      <defs>
        <marker
          id="bpmn-arrow-filled"
          viewBox="0 0 10 10"
          refX={10}
          refY={5}
          markerWidth={8}
          markerHeight={8}
          orient="auto-start-reverse"
        >
          <path
            d="M 0 0 L 10 5 L 0 10 Z"
            fill="var(--edge-default, #666)"
          />
        </marker>
        <marker
          id="bpmn-arrow-open"
          viewBox="0 0 10 10"
          refX={10}
          refY={5}
          markerWidth={8}
          markerHeight={8}
          orient="auto-start-reverse"
        >
          <path
            d="M 0 0 L 10 5 L 0 10"
            fill="none"
            stroke="var(--edge-default, #666)"
            strokeWidth={1.5}
          />
        </marker>
      </defs>
    </svg>
  );
}

function CanvasInner() {
  const nodes = useGraphStore((s) => s.nodes);
  const edges = useGraphStore((s) => s.edges);
  const onNodesChange = useGraphStore((s) => s.onNodesChange);
  const onEdgesChange = useGraphStore((s) => s.onEdgesChange);
  const onViewportChange = useGraphStore((s) => s.onViewportChange);
  const highlightedNodeId = useGraphStore((s) => s.highlightedNodeId);
  const setHighlightedNodeId = useGraphStore((s) => s.setHighlightedNodeId);
  const setNodes = useGraphStore((s) => s.setNodes);
  const setEdges = useGraphStore((s) => s.setEdges);

  const { isLayouting } = useAutoLayout();

  const clearHighlight = useCallback(() => {
    if (!highlightedNodeId) return;
    setHighlightedNodeId(null);
    setNodes(nodes.map((n) => ({ ...n, className: undefined })));
    setEdges(edges.map((e) => ({ ...e, className: undefined })));
  }, [highlightedNodeId, nodes, edges, setHighlightedNodeId, setNodes, setEdges]);

  const handleNodeClick = useCallback(
    (_: React.MouseEvent, node: Node) => {
      // Skip container nodes (pools, lanes, groups)
      if (CONTAINER_TYPES.has(node.type ?? "")) return;

      const store = useGraphStore.getState();

      // Toggle off if clicking the same node
      if (store.highlightedNodeId === node.id) {
        store.setHighlightedNodeId(null);
        store.setNodes(
          store.nodes.map((n) => ({ ...n, className: undefined })),
        );
        store.setEdges(
          store.edges.map((e) => ({ ...e, className: undefined })),
        );
        return;
      }

      // Compute BFS path from clicked node
      const graphEdges = store.edges
        .filter((e) => e.type !== "bpmnGroup")
        .map((e) => ({ from: e.source, to: e.target }));
      const { nodeSet, edgeSet } = bfsPath(node.id, graphEdges);

      // Apply highlight/dim classes
      store.setHighlightedNodeId(node.id);
      store.setNodes(
        store.nodes.map((n) => {
          if (CONTAINER_TYPES.has(n.type ?? "")) {
            return { ...n, className: undefined };
          }
          return {
            ...n,
            className: nodeSet.has(n.id) ? "highlighted" : "dimmed",
          };
        }),
      );
      store.setEdges(
        store.edges.map((e) => ({
          ...e,
          className: edgeSet.has(`${e.source}->${e.target}`)
            ? "highlighted"
            : "dimmed",
        })),
      );
    },
    [],
  );

  return (
    <div className="w-full h-full">
      <BpmnMarkerDefs />
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onViewportChange={onViewportChange}
        onNodeClick={handleNodeClick}
        onPaneClick={clearHighlight}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        fitView
        colorMode="system"
        proOptions={{ hideAttribution: true }}
      >
        <Background
          variant={BackgroundVariant.Dots}
          gap={20}
          size={1}
          color="var(--canvas-grid)"
        />
        <Controls showInteractive={false} />
        <MiniMap
          pannable
          zoomable
          style={{ width: 120, height: 80 }}
        />
        {isLayouting && (
          <Panel position="top-center">
            <div className="rounded-md bg-background/80 px-3 py-1.5 text-xs text-muted-foreground backdrop-blur-sm border border-border">
              Computing layout...
            </div>
          </Panel>
        )}
      </ReactFlow>
    </div>
  );
}

export function DiagramCanvas() {
  return (
    <ReactFlowProvider>
      <CanvasInner />
    </ReactFlowProvider>
  );
}
