import { useCallback, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import { GitCompareArrows, CircleDashed, AlertCircle } from "lucide-react";
import { ApiError } from "@/lib/api";
import { Button } from "@/components/ui/button";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { EmptyState } from "@/components/ui/empty-state";
import { ErrorState } from "@/components/ui/error-state";
import { BatchDiffView, BatchDiffViewSkeleton } from "@/components/BatchDiffView";
import { useBatches } from "@/hooks/useBatches";
import { useBatchDiff } from "@/hooks/useBatchDiff";
import type { BatchSummary as ApiBatchSummary } from "@/lib/api";
import { cn } from "@/lib/utils";

// ---------------------------------------------------------------------------
// Inline URL state — uses react-router's useSearchParams so the page
// participates in the standard routing lifecycle (MemoryRouter in
// tests, BrowserRouter in production). `replace: true` keeps the
// picker tweaks from polluting the back-button stack.
// ---------------------------------------------------------------------------

function readIdsFromParams(params: URLSearchParams): {
  a: string | null;
  b: string | null;
} {
  const a = params.get("a");
  const b = params.get("b");
  return {
    a: a && a.length > 0 ? a : null,
    b: b && b.length > 0 ? b : null,
  };
}

// ---------------------------------------------------------------------------
// Batch picker — one Select dropdown for each side. Keeps the same
// kind-colored badge inline so the operator can spot which batch is
// which at a glance.
// ---------------------------------------------------------------------------

function KindBadge({ kind }: { kind: ApiBatchSummary["kind"] }) {
  const cls =
    kind === "837p"
      ? "text-sky-300 border-sky-400/30 bg-sky-400/10"
      : "text-amber-300 border-amber-400/30 bg-amber-400/10";
  return (
    <span
      data-testid={`diff-picker-kind-${kind}`}
      className={cn(
        "inline-flex items-center gap-1 rounded-full border px-1.5 py-px text-[9.5px] font-semibold uppercase tracking-[0.14em] font-mono",
        cls,
      )}
    >
      {kind}
    </span>
  );
}

function BatchPicker({
  label,
  value,
  onChange,
  items,
  excludeId,
  testid,
}: {
  label: string;
  value: string | null;
  onChange: (id: string) => void;
  items: ApiBatchSummary[];
  excludeId: string | null;
  testid: string;
}) {
  // Filter out the value already chosen on the *other* side so the
  // operator can't accidentally pick the same batch twice (which
  // produces a meaningless diff). Disabled rather than hidden so the
  // selection stays transparent.
  const visible = useMemo(
    () => items.filter((it) => it.id !== excludeId),
    [items, excludeId],
  );
  const selected = items.find((it) => it.id === value);
  return (
    <div className="space-y-1.5" data-testid={testid}>
      <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground">
        {label}
      </div>
      <Select value={value ?? ""} onValueChange={(v) => onChange(v)}>
        <SelectTrigger>
          {selected ? (
            <span className="flex items-center gap-2 truncate">
              <KindBadge kind={selected.kind} />
              <span className="font-mono num text-[12.5px]">{selected.id}</span>
              <span className="text-muted-foreground text-[12px] truncate">
                · {selected.inputFilename}
              </span>
            </span>
          ) : (
            <SelectValue placeholder="Pick a batch…" />
          )}
        </SelectTrigger>
        <SelectContent>
          {visible.length === 0 ? (
            <div className="px-3 py-2 text-xs text-muted-foreground">
              No other batches available.
            </div>
          ) : (
            visible.map((b) => (
              <SelectItem
                key={b.id}
                value={b.id}
                data-testid={`diff-picker-option-${b.id}`}
              >
                <span className="flex items-center gap-2">
                  <KindBadge kind={b.kind} />
                  <span className="font-mono num text-[12px]">{b.id}</span>
                  <span className="text-muted-foreground text-[12px] truncate">
                    · {b.inputFilename}
                  </span>
                </span>
              </SelectItem>
            ))
          )}
        </SelectContent>
      </Select>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Not-found branch — surfaces when the backend reports 404 for one of
// the picks. Most likely cause: a bookmark referencing a now-deleted
// batch, or another agent deleting between the picker load and the
// diff fetch.
// ---------------------------------------------------------------------------

function NotFoundState({
  a,
  b,
  onReset,
}: {
  a: string;
  b: string;
  onReset: () => void;
}) {
  return (
    <ErrorState
      message="One of these batches was not found."
      detail={`The backend couldn't find batch ${a} or ${b}. It may have been deleted between picker load and diff fetch.`}
      onRetry={onReset}
    />
  );
}

// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

export function BatchDiff() {
  // URL → state via react-router. Keeps MemoryRouter (tests) and
  // BrowserRouter (production) symmetric: both update the
  // useSearchParams hook on every navigation.
  const [searchParams, setSearchParams] = useSearchParams();
  const { a, b } = useMemo(
    () => readIdsFromParams(searchParams),
    [searchParams],
  );

  const updateIds = useCallback(
    (next: { a: string | null; b: string | null }) => {
      const params = new URLSearchParams(searchParams);
      if (next.a === null) params.delete("a");
      else params.set("a", next.a);
      if (next.b === null) params.delete("b");
      else params.set("b", next.b);
      setSearchParams(params, { replace: true });
    },
    [searchParams, setSearchParams],
  );

  const setA = useCallback(
    (id: string) => updateIds({ a: id, b }),
    [b, updateIds],
  );
  const setB = useCallback(
    (id: string) => updateIds({ a, b: id }),
    [a, updateIds],
  );
  const reset = useCallback(() => updateIds({ a: null, b: null }), [updateIds]);

  const { data: batches, isLoading: loadingBatches, isError: batchesError, error: batchesErrorMsg, refetch: refetchBatches } =
    useBatches(100);

  // Only fire the diff once both pickers have a value. The hook also
  // gates on this internally (defense-in-depth), so a half-picked URL
  // never hits the backend.
  const diff = useBatchDiff(a, b);
  const ready = a !== null && b !== null;

  // --- error / status derivation ------------------------------------
  const errKind: "network" | "not_found" | null = diff.error
    ? diff.error instanceof ApiError && diff.error.status === 404
      ? "not_found"
      : "network"
    : null;

  // --- render -------------------------------------------------------
  return (
    <div className="space-y-6 md:space-y-8 animate-fade-in" data-testid="batch-diff-page">
      <header>
        <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-2 flex items-center gap-2">
          <span className="inline-block h-px w-6 bg-border" />
          Diff
        </div>
        <h1 className="text-[22px] sm:text-[26px] font-semibold tracking-tight">
          Batch diff
        </h1>
        <p className="text-muted-foreground mt-1.5 text-[14px]">
          Pick two parsed batches — typically a submitted 837P and its
          corrected follow-up — and see what was added, removed, or changed
          between them.
        </p>
      </header>

      <div className="surface rounded-xl p-4 grid grid-cols-1 md:grid-cols-2 gap-3 md:gap-4">
        <BatchPicker
          label="A · left"
          value={a}
          onChange={setA}
          items={batches ?? []}
          excludeId={b}
          testid="diff-picker-a"
        />
        <BatchPicker
          label="B · right"
          value={b}
          onChange={setB}
          items={batches ?? []}
          excludeId={a}
          testid="diff-picker-b"
        />
      </div>

      {batchesError ? (
        <ErrorState
          message="Couldn't load batches from the backend."
          detail={
            batchesErrorMsg instanceof Error
              ? batchesErrorMsg.message
              : String(batchesErrorMsg)
          }
          onRetry={() => refetchBatches()}
        />
      ) : null}

      {!ready ? (
        <div className="surface rounded-xl">
          <EmptyState
            icon={<GitCompareArrows className="h-4 w-4" strokeWidth={1.5} />}
            eyebrow="Batch diff · awaiting picks"
            message="Choose one batch for A and one for B to compute the diff."
          />
        </div>
      ) : loadingBatches ? (
        <BatchDiffViewSkeleton />
      ) : errKind === "not_found" ? (
        <NotFoundState a={a as string} b={b as string} onReset={reset} />
      ) : diff.isError ? (
        <ErrorState
          message="Couldn't compute the diff between these two batches."
          detail={
            diff.error instanceof Error
              ? diff.error.message
              : String(diff.error)
          }
          onRetry={() => diff.refetch()}
        />
      ) : diff.isLoading || !diff.data ? (
        <BatchDiffViewSkeleton />
      ) : (
        <BatchDiffView data={diff.data} />
      )}

      {/* Action footer — always visible so the operator can clear or
          force-refresh without scrolling back to the picker. */}
      {ready ? (
        <div className="flex items-center justify-end gap-2 pt-2">
          {diff.isFetching ? (
            <span className="text-[12px] text-muted-foreground inline-flex items-center gap-1.5">
              <CircleDashed className="h-3 w-3 animate-spin" strokeWidth={1.75} />
              Refreshing…
            </span>
          ) : null}
          <Button
            variant="outline"
            size="sm"
            onClick={() => diff.refetch()}
            data-testid="diff-refresh-button"
          >
            Refresh
          </Button>
          <Button
            variant="ghost"
            size="sm"
            onClick={reset}
            data-testid="diff-clear-button"
          >
            <AlertCircle className="h-3.5 w-3.5 mr-1" strokeWidth={1.75} />
            Clear picks
          </Button>
        </div>
      ) : null}
    </div>
  );
}
