"use client";

import { useMemo } from "react";

import type { Node, Edge } from "@xyflow/react";

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

interface ProposalDiff {
  addedCount: number;
  removedCount: number;
  modifiedCount: number;
  changeSummary: string;
}

function countNodeDiffs(
  previous: Node[],
  proposedNodes: Node[],
): { added: number; removed: number; modified: number } {
  const prevIds = new Set(previous.map((n) => n.id));
  const proposedIds = new Set(proposedNodes.map((n) => n.id));
  const prevMap = new Map(previous.map((n) => [n.id, n]));

  let added = 0;
  let removed = 0;
  let modified = 0;

  for (const n of proposedNodes) {
    if (!prevIds.has(n.id)) {
      added++;
    } else {
      const prev = prevMap.get(n.id);
      if (prev) {
        const pData = n.data as Record<string, unknown>;
        const cData = prev.data as Record<string, unknown>;
        if (
          pData.label !== cData.label ||
          pData.type !== cData.type ||
          pData.tag !== cData.tag ||
          JSON.stringify(pData.columns) !== JSON.stringify(cData.columns)
        ) {
          modified++;
        }
      }
    }
  }

  for (const n of previous) {
    if (!proposedIds.has(n.id)) {
      removed++;
    }
  }

  return { added, removed, modified };
}

function countEdgeDiffs(
  previous: Edge[],
  proposedEdges: Edge[],
): { added: number; removed: number; modified: number } {
  const prevIds = new Set(previous.map((e) => e.id));
  const proposedIds = new Set(proposedEdges.map((e) => e.id));
  const prevMap = new Map(previous.map((e) => [e.id, e]));

  let added = 0;
  let removed = 0;
  let modified = 0;

  for (const e of proposedEdges) {
    if (!prevIds.has(e.id)) {
      added++;
    } else {
      const prev = prevMap.get(e.id);
      if (prev && (e.label !== prev.label || e.type !== prev.type)) {
        modified++;
      }
    }
  }
  for (const e of previous) {
    if (!proposedIds.has(e.id)) removed++;
  }

  return { added, removed, modified };
}

function buildSummary(
  nodeAdded: number,
  nodeRemoved: number,
  nodeModified: number,
  edgeAdded: number,
  edgeRemoved: number,
  edgeModified = 0,
): string {
  const parts: string[] = [];
  const total = nodeAdded + nodeRemoved + nodeModified + edgeAdded + edgeRemoved + edgeModified;
  if (total === 0) return "No changes";

  if (nodeAdded > 0) parts.push(`Adding ${nodeAdded} node${nodeAdded !== 1 ? "s" : ""}`);
  if (nodeModified > 0) parts.push(`modifying ${nodeModified} node${nodeModified !== 1 ? "s" : ""}`);
  if (nodeRemoved > 0) parts.push(`removing ${nodeRemoved} node${nodeRemoved !== 1 ? "s" : ""}`);
  if (edgeAdded > 0) parts.push(`adding ${edgeAdded} edge${edgeAdded !== 1 ? "s" : ""}`);
  if (edgeModified > 0) parts.push(`modifying ${edgeModified} edge${edgeModified !== 1 ? "s" : ""}`);
  if (edgeRemoved > 0) parts.push(`removing ${edgeRemoved} edge${edgeRemoved !== 1 ? "s" : ""}`);

  return parts.join(", ");
}

/** Pure diff computation — exported for testing */
export { countNodeDiffs, countEdgeDiffs, buildSummary };

export function useProposalDiff(): ProposalDiff {
  const proposedPatch = useGraphStore((s) => s.proposedPatch);
  const previousGraphSnapshot = useGraphStore((s) => s.previousGraphSnapshot);

  return useMemo(() => {
    if (!proposedPatch || !previousGraphSnapshot) {
      return { addedCount: 0, removedCount: 0, modifiedCount: 0, changeSummary: "" };
    }

    const proposed = graphToFlow(proposedPatch);

    const nodeDiffs = countNodeDiffs(previousGraphSnapshot.nodes, proposed.nodes);
    const edgeDiffs = countEdgeDiffs(previousGraphSnapshot.edges, proposed.edges);

    return {
      addedCount: nodeDiffs.added + edgeDiffs.added,
      removedCount: nodeDiffs.removed + edgeDiffs.removed,
      modifiedCount: nodeDiffs.modified + edgeDiffs.modified,
      changeSummary: buildSummary(
        nodeDiffs.added,
        nodeDiffs.removed,
        nodeDiffs.modified,
        edgeDiffs.added,
        edgeDiffs.removed,
        edgeDiffs.modified,
      ),
    };
  }, [proposedPatch, previousGraphSnapshot]);
}
