"use client";

import { useCallback, useMemo, useRef, useState } from "react";
import { Handle, Position, useStore, type NodeProps } from "@xyflow/react";
import { ImageIcon } from "lucide-react";
import BaseNodeWrapper from "./base-node-wrapper";
import CompareSurface from "./compare-surface";
import {
  resolveRenderPipelineHash,
  resolveRenderPreviewInput,
  type RenderPreviewInput,
} from "@/lib/canvas-render-preview";

interface CompareNodeData {
  leftUrl?: string;
  rightUrl?: string;
  leftLabel?: string;
  rightLabel?: string;
}

type CompareSide = "left" | "right";

type CompareSideState = {
  finalUrl?: string;
  label?: string;
  previewInput?: RenderPreviewInput;
  isStaleRenderOutput: boolean;
};

type CompareDisplayMode = "render" | "preview";

export default function CompareNode({ id, data, selected, width }: NodeProps) {
  const nodeData = data as CompareNodeData;
  const nodes = useStore((state) => state.nodes);
  const edges = useStore((state) => state.edges);
  const [sliderX, setSliderX] = useState(50);
  const [manualDisplayMode, setManualDisplayMode] = useState<CompareDisplayMode | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  const pipelineNodes = useMemo(
    () => nodes.map((node) => ({ id: node.id, type: node.type ?? "", data: node.data })),
    [nodes],
  );
  const pipelineEdges = useMemo(
    () => edges.map((edge) => ({ source: edge.source, target: edge.target })),
    [edges],
  );

  const nodesById = useMemo(() => new Map(nodes.map((node) => [node.id, node])), [nodes]);
  const incomingEdges = useMemo(
    () =>
      edges.filter(
        (edge) =>
          edge.target === id &&
          edge.className !== "temp" &&
          (edge.targetHandle === "left" || edge.targetHandle === "right"),
      ),
    [edges, id],
  );

  const resolvedSides = useMemo(() => {
    const resolveSide = (
      side: CompareSide,
      finalUrl: string | undefined,
      finalLabel: string | undefined,
      defaultLabel: string,
    ): CompareSideState => {
      const incomingEdge = incomingEdges.find((edge) => edge.targetHandle === side);
      const sourceNode = incomingEdge ? nodesById.get(incomingEdge.source) : undefined;
      const sourceData = (sourceNode?.data ?? {}) as Record<string, unknown>;
      const sourceLabel =
        typeof sourceData.label === "string" && sourceData.label.length > 0
          ? sourceData.label
          : sourceNode?.type;

      const label = finalLabel ?? sourceLabel ?? defaultLabel;

      let previewInput: RenderPreviewInput | undefined;
      let isStaleRenderOutput = false;

      if (sourceNode && sourceNode.type === "render") {
        const preview = resolveRenderPreviewInput({
          nodeId: sourceNode.id,
          nodes: pipelineNodes,
          edges: pipelineEdges,
        });

        if (preview.sourceUrl) {
          previewInput = {
            sourceUrl: preview.sourceUrl,
            steps: preview.steps,
          };

          const sourceLastUploadedHash =
            typeof sourceData.lastUploadedHash === "string"
              ? sourceData.lastUploadedHash
              : undefined;
          const sourceLastRenderedHash =
            typeof sourceData.lastRenderedHash === "string"
              ? sourceData.lastRenderedHash
              : undefined;
          const sourcePersistedOutputHash =
            sourceLastUploadedHash ?? sourceLastRenderedHash;
          const sourceCurrentHash = resolveRenderPipelineHash({
            sourceUrl: preview.sourceUrl,
            steps: preview.steps,
            data: sourceData,
          });

          isStaleRenderOutput =
            Boolean(finalUrl) &&
            Boolean(sourceCurrentHash) &&
            Boolean(sourcePersistedOutputHash) &&
            sourceCurrentHash !== sourcePersistedOutputHash;
        }
      }

      if (finalUrl) {
        return { finalUrl, label, previewInput, isStaleRenderOutput };
      }

      return { label, previewInput, isStaleRenderOutput };
    };

    return {
      left: resolveSide("left", nodeData.leftUrl, nodeData.leftLabel, "Before"),
      right: resolveSide("right", nodeData.rightUrl, nodeData.rightLabel, "After"),
    };
  }, [
    incomingEdges,
    nodeData.leftLabel,
    nodeData.leftUrl,
    nodeData.rightLabel,
    nodeData.rightUrl,
    nodesById,
    pipelineEdges,
    pipelineNodes,
  ]);

  const hasLeft = Boolean(resolvedSides.left.finalUrl || resolvedSides.left.previewInput);
  const hasRight = Boolean(resolvedSides.right.finalUrl || resolvedSides.right.previewInput);
  const hasConnectedRenderInput = useMemo(
    () =>
      incomingEdges.some((edge) => {
        const sourceNode = nodesById.get(edge.source);
        return sourceNode?.type === "render";
      }),
    [incomingEdges, nodesById],
  );
  const shouldDefaultToPreview =
    resolvedSides.left.isStaleRenderOutput || resolvedSides.right.isStaleRenderOutput;
  const effectiveDisplayMode =
    manualDisplayMode ?? (shouldDefaultToPreview ? "preview" : "render");
  const previewNodeWidth = Math.max(240, Math.min(640, Math.round(width ?? 500)));

  const handleMouseDown = useCallback((event: React.MouseEvent) => {
    event.stopPropagation();

    const move = (moveEvent: MouseEvent) => {
      if (!containerRef.current) return;
      const rect = containerRef.current.getBoundingClientRect();
      const x = Math.max(
        0,
        Math.min(1, (moveEvent.clientX - rect.left) / rect.width),
      );
      setSliderX(x * 100);
    };

    const up = () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup", up);
    };

    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup", up);
  }, []);

  const handleTouchStart = useCallback((event: React.TouchEvent) => {
    event.stopPropagation();

    const move = (moveEvent: TouchEvent) => {
      if (!containerRef.current || moveEvent.touches.length === 0) return;
      const rect = containerRef.current.getBoundingClientRect();
      const touch = moveEvent.touches[0];
      const x = Math.max(0, Math.min(1, (touch.clientX - rect.left) / rect.width));
      setSliderX(x * 100);
    };

    const end = () => {
      window.removeEventListener("touchmove", move);
      window.removeEventListener("touchend", end);
    };

    window.addEventListener("touchmove", move);
    window.addEventListener("touchend", end);
  }, []);

  return (
    <BaseNodeWrapper nodeType="compare" selected={selected} className="p-0">
      <Handle
        type="target"
        position={Position.Left}
        id="left"
        style={{ top: "35%" }}
        className="!h-3 !w-3 !border-2 !border-background !bg-blue-500"
      />
      <Handle
        type="target"
        position={Position.Left}
        id="right"
        style={{ top: "55%" }}
        className="!h-3 !w-3 !border-2 !border-background !bg-emerald-500"
      />
      <Handle
        type="source"
        position={Position.Right}
        id="compare-out"
        className="!h-3 !w-3 !border-2 !border-background !bg-muted-foreground"
      />

      <div className="grid h-full min-h-0 w-full grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
        <div className="flex items-center justify-between gap-2 px-3 py-2">
          <div className="text-xs font-medium text-muted-foreground">⚖️ Compare</div>
          {hasConnectedRenderInput && (
            <div className="nodrag inline-flex rounded-md border border-border bg-background/80 p-0.5">
              <button
                type="button"
                className={`rounded px-2 py-0.5 text-[10px] font-medium ${effectiveDisplayMode === "render" ? "bg-muted text-foreground" : "text-muted-foreground"}`}
                onClick={(event) => {
                  event.stopPropagation();
                  setManualDisplayMode("render");
                }}
              >
                Render
              </button>
              <button
                type="button"
                className={`rounded px-2 py-0.5 text-[10px] font-medium ${effectiveDisplayMode === "preview" ? "bg-muted text-foreground" : "text-muted-foreground"}`}
                onClick={(event) => {
                  event.stopPropagation();
                  setManualDisplayMode("preview");
                }}
              >
                Preview
              </button>
            </div>
          )}
        </div>

        <div
          ref={containerRef}
          className="nodrag relative min-h-0 w-full select-none overflow-hidden rounded-b-xl bg-muted"
          onMouseDown={handleMouseDown}
          onTouchStart={handleTouchStart}
        >
          {!hasLeft && !hasRight && (
            <div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-muted-foreground">
              <ImageIcon className="h-10 w-10 opacity-30" />
              <p className="px-8 text-center text-xs opacity-60">
                Connect two image nodes - left handle (blue) and right handle (green)
              </p>
            </div>
          )}

          {hasRight && (
            <CompareSurface
              finalUrl={resolvedSides.right.finalUrl}
              label={resolvedSides.right.label}
              previewInput={resolvedSides.right.previewInput}
              nodeWidth={previewNodeWidth}
              preferPreview={effectiveDisplayMode === "preview"}
            />
          )}

          {hasLeft && (
            <CompareSurface
              finalUrl={resolvedSides.left.finalUrl}
              label={resolvedSides.left.label}
              previewInput={resolvedSides.left.previewInput}
              nodeWidth={previewNodeWidth}
              clipWidthPercent={sliderX}
              preferPreview={effectiveDisplayMode === "preview"}
            />
          )}

          {hasLeft && hasRight && (
            <>
              <div
                className="pointer-events-none absolute bottom-0 top-0 z-10 w-0.5 bg-white shadow-md"
                style={{ left: `${sliderX}%` }}
              />
              <div
                className="pointer-events-none absolute top-1/2 z-20 -translate-x-1/2 -translate-y-1/2"
                style={{ left: `${sliderX}%` }}
              >
                <div className="flex h-8 w-8 items-center justify-center rounded-full border border-border bg-white shadow-lg">
                  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                    <path
                      d="M5 8H1M11 8H15M5 5L2 8L5 11M11 5L14 8L11 11"
                      stroke="currentColor"
                      strokeWidth="1.5"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                    />
                  </svg>
                </div>
              </div>
            </>
          )}

          {hasLeft && (
            <div className="pointer-events-none absolute left-2 top-2 z-10">
              <span className="rounded bg-blue-500/80 px-1.5 py-0.5 text-[10px] font-medium text-white backdrop-blur-sm">
                {resolvedSides.left.label ?? "Before"}
              </span>
            </div>
          )}

          {hasRight && (
            <div className="pointer-events-none absolute right-2 top-2 z-10">
              <span className="rounded bg-emerald-500/80 px-1.5 py-0.5 text-[10px] font-medium text-white backdrop-blur-sm">
                {resolvedSides.right.label ?? "After"}
              </span>
            </div>
          )}
        </div>
      </div>
    </BaseNodeWrapper>
  );
}
