"use client";

import { useCallback } from "react";
import { toast } from "sonner";

import { api } from "~/lib/api/client";
import { useGraphStore } from "~/modules/diagram/stores/useGraphStore";
import { graphToFlow } from "~/modules/diagram/lib/graph-converter";

import type { DiagramType, GraphData } from "~/modules/diagram/types/graph";

interface GraphPatchData {
  meta: {
    diagramType: string;
    title: string;
    version?: string;
    layoutDirection?: "DOWN" | "RIGHT" | "LEFT" | "UP";
    edgeRouting?: "ORTHOGONAL" | "SPLINES" | "POLYLINE";
  };
  nodes: GraphData["nodes"];
  edges: GraphData["edges"];
  pools?: GraphData["pools"];
  groups?: GraphData["groups"];
}

function patchToGraphData(patch: GraphPatchData, fallbackType: DiagramType): GraphData {
  const effectiveDiagramType =
    (patch.meta.diagramType as DiagramType) ?? fallbackType;

  return {
    meta: {
      version: patch.meta.version ?? "1",
      title: patch.meta.title,
      diagramType: effectiveDiagramType,
      layoutDirection: patch.meta.layoutDirection,
      edgeRouting: patch.meta.edgeRouting,
    },
    nodes: patch.nodes,
    edges: patch.edges,
    pools: patch.pools,
    groups: patch.groups,
  };
}

export function persistGraphData(diagramId: string, graphData: GraphData) {
  api.diagrams[":id"]
    .$patch({
      param: { id: diagramId },
      json: { graphData: graphData as unknown as Record<string, unknown> },
    })
    .then((res) => {
      if (!res.ok) {
        toast.error("Failed to save diagram — changes may be lost on reload");
      }
    })
    .catch(() => {
      toast.error("Failed to save diagram — changes may be lost on reload");
    });
}

export function useGraphMutation(diagramId: string, diagramType: DiagramType) {
  const setNodes = useGraphStore((s) => s.setNodes);
  const setEdges = useGraphStore((s) => s.setEdges);
  const setLayoutDirection = useGraphStore((s) => s.setLayoutDirection);
  const setEdgeRouting = useGraphStore((s) => s.setEdgeRouting);
  const requestLayout = useGraphStore((s) => s.requestLayout);

  const applyGraphPatch = useCallback(
    (patch: GraphPatchData) => {
      const graphData = patchToGraphData(patch, diagramType);
      const { nodes, edges } = graphToFlow(graphData);

      setNodes(nodes);
      setEdges(edges);

      if (graphData.meta?.layoutDirection) {
        setLayoutDirection(graphData.meta.layoutDirection);
      }
      if (graphData.meta?.edgeRouting) {
        setEdgeRouting(graphData.meta.edgeRouting);
      }

      requestLayout();
      persistGraphData(diagramId, graphData);
    },
    [
      diagramId,
      diagramType,
      setNodes,
      setEdges,
      setLayoutDirection,
      setEdgeRouting,
      requestLayout,
    ],
  );

  const proposeGraphPatch = useCallback(
    (patch: GraphPatchData) => {
      const graphData = patchToGraphData(patch, diagramType);
      useGraphStore.getState().proposeChanges(graphData);
    },
    [diagramType],
  );

  return { applyGraphPatch, proposeGraphPatch };
}
