import { useMemo, useState } from "react";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { ApiError } from "@/lib/api";
import { cn } from "@/lib/utils";
import { useClaimDetail } from "@/hooks/useClaimDetail";
import { useDrawerKeyboard } from "@/hooks/useDrawerKeyboard";
import { useLineReconciliation } from "@/hooks/useLineReconciliation";
import { ClaimDrawerHeader } from "./ClaimDrawerHeader";
import { ValidationPanel } from "./ValidationPanel";
import { ServiceLinesTable } from "./ServiceLinesTable";
import { DiagnosesList } from "./DiagnosesList";
import { PartiesGrid } from "./PartiesGrid";
import { RawSegmentsPanel } from "./RawSegmentsPanel";
import { MatchedRemitCard } from "./MatchedRemitCard";
import { StateHistoryTimeline } from "./StateHistoryTimeline";
import { LineReconciliationTab } from "./LineReconciliationTab";
import { ClaimDrawerSkeleton } from "./ClaimDrawerSkeleton";
import { ClaimDrawerError } from "./ClaimDrawerError";

type ClaimDrawerProps = {
  /**
   * Currently-open claim id, or `null` when the drawer is closed.
   * When `null`, the drawer renders nothing (no DOM) and the keyboard
   * listener is disabled — per spec §3.4.
   */
  claimId: string | null;
  /**
   * Full ordered list of claim ids in the current view. Used to derive
   * j/k navigation targets without an extra round-trip to the server.
   * The list must contain the current `claimId` for j/k to do
   * anything; an unknown id is treated defensively as "navigation
   * disabled" rather than throwing.
   */
  claims: { id: string }[];
  /** Fired when the user dismisses the drawer (X button, header close, or Escape). */
  onClose: () => void;
  /** Fired when the user navigates via j/k — receives the new claim id. */
  onNavigate: (newId: string) => void;
  /** Fired when the user presses `?` to toggle the keyboard-help overlay. */
  onToggleHelp: () => void;
};

/**
 * Root claim-detail drawer (SP4).
 *
 * Orchestrates three concerns and delegates the rest:
 *
 *  1. **Data** — `useClaimDetail(claimId)` returns `{ data, isLoading,
 *     isError, error, refetch }`. The hook short-circuits when
 *     `claimId === null` (no fetch, no loading state) so a closed
 *     drawer is free.
 *
 *  2. **Keyboard** — `useDrawerKeyboard` wires j/k/ArrowDown/ArrowUp/
 *     Escape/`?` on `window`. The listener is only attached when the
 *     drawer is open (`enabled: claimId !== null`).
 *
 *  3. **Layout** — the dialog shell is the project's `Dialog` primitive
 *     repositioned to the right edge (side-panel style). It exists
 *     mostly for Radix's focus management + portal; the actual
 *     drawer's structure (header + scrollable sections) is composed
 *     from the leaf section components, each of which owns its own
 *     visual language.
 *
 * j/k navigation wraps around at both ends (j from last → first; k
 * from first → last). `useMemo` keeps the navigation callbacks stable
 * across renders so the keyboard effect doesn't re-subscribe on every
 * parent update.
 */
export function ClaimDrawer({
  claimId,
  claims,
  onClose,
  onNavigate,
  onToggleHelp,
}: ClaimDrawerProps) {
  const { data, isLoading, isError, error, refetch } = useClaimDetail(claimId);
  // SP7: tab state. The Line Reconciliation tab lazy-fetches the
  // per-line projection when first activated.
  const [activeTab, setActiveTab] = useState<"details" | "line-reconciliation">("details");
  const lr = useLineReconciliation(
    activeTab === "line-reconciliation" ? claimId : null
  );

  // j/k navigation: derive next/prev IDs based on the current claim's
  // index in the parent list. Wrap around at both ends. If the current
  // claim isn't in the list (stale deep link, etc.) the callbacks are
  // no-ops — defensive against bad input from the parent.
  const { onNext, onPrev } = useMemo(() => {
    if (claimId === null) {
      return { onNext: () => {}, onPrev: () => {} };
    }
    const idx = claims.findIndex((c) => c.id === claimId);
    if (idx === -1 || claims.length === 0) {
      return { onNext: () => {}, onPrev: () => {} };
    }
    return {
      onNext: () => {
        const nextId = claims[(idx + 1) % claims.length].id;
        onNavigate(nextId);
      },
      onPrev: () => {
        // Add `claims.length` before the modulo so the negative index
        // (first → wrap to last) wraps correctly without a separate
        // branch.
        const prevId = claims[(idx - 1 + claims.length) % claims.length].id;
        onNavigate(prevId);
      },
    };
  }, [claimId, claims, onNavigate]);

  useDrawerKeyboard({
    enabled: claimId !== null,
    onNext,
    onPrev,
    onClose,
    onToggleHelp,
  });

  // Closed drawer: render nothing. The Dialog's `open` prop must also
  // reflect this (so Radix tears down its portal / focus trap), and the
  // early-return keeps a closed drawer free of any DOM at all.
  if (claimId === null) return null;

  // Branch the error into the two shapes the spec calls out:
  //   - ApiError(404) → "not_found" (no retry, the claim is gone)
  //   - anything else → "network" (retry available)
  const errorKind: "not_found" | "network" | null = isError
    ? error instanceof ApiError && error.status === 404
      ? "not_found"
      : "network"
    : null;

  return (
    <Dialog
      open={claimId !== null}
      onOpenChange={(open) => {
        if (!open) onClose();
      }}
    >
      <DialogContent
        // Right-anchored side panel. We override the Dialog primitive's
        // default centered positioning (`left-1/2 top-1/2 -translate-x-1/2
        // -translate-y-1/2`) by re-asserting `right-0 top-0 translate-x-0
        // translate-y-0`. `h-full` + `rounded-none` + the left border
        // give it the visual identity of a drawer rather than a modal.
        // `aria-describedby={undefined}` suppresses the Radix warning
        // about a missing description — the drawer content is its own
        // description and there's nothing useful to point at.
        className="fixed right-0 top-0 h-full w-full max-w-2xl translate-x-0 translate-y-0 rounded-none border-l border-[color:var(--m-border-heavy)]/60 bg-[color:var(--m-surface)] p-0 shadow-[-24px_0_60px_-12px_rgba(0,0,0,0.6)]"
        data-testid="claim-drawer"
        aria-describedby={undefined}
      >
        {isLoading ? (
          <ClaimDrawerSkeleton />
        ) : errorKind ? (
          <ClaimDrawerError
            kind={errorKind}
            onRetry={() => {
              void refetch();
            }}
            onClose={onClose}
          />
        ) : data ? (
          <div
            className="flex h-full flex-col overflow-y-auto"
            data-testid="claim-drawer-content"
          >
            <ClaimDrawerHeader claim={data} onClose={onClose} />
            <div
              className="flex gap-0 px-6 border-b border-[color:var(--surface-line)]/40"
              data-testid="claim-drawer-tabs"
            >
              <button
                type="button"
                onClick={() => setActiveTab("details")}
                className={cn(
                  "relative px-3 py-2.5 text-[10.5px] font-semibold uppercase tracking-[0.18em] transition-colors",
                  activeTab === "details"
                    ? "text-[color:var(--surface-ink)]"
                    : "text-[color:var(--surface-ink-3)] hover:text-[color:var(--surface-ink-2)]"
                )}
                data-testid="tab-button-details"
                data-active={activeTab === "details" ? "true" : "false"}
              >
                Details
                {activeTab === "details" ? (
                  <span className="absolute inset-x-0 -bottom-px h-[2px] bg-[color:var(--accent)]" />
                ) : null}
              </button>
              <button
                type="button"
                onClick={() => setActiveTab("line-reconciliation")}
                className={cn(
                  "relative px-3 py-2.5 text-[10.5px] font-semibold uppercase tracking-[0.18em] transition-colors",
                  activeTab === "line-reconciliation"
                    ? "text-[color:var(--surface-ink)]"
                    : "text-[color:var(--surface-ink-3)] hover:text-[color:var(--surface-ink-2)]"
                )}
                data-testid="tab-button-line-reconciliation"
                data-active={activeTab === "line-reconciliation" ? "true" : "false"}
              >
                Line Reconciliation
                {activeTab === "line-reconciliation" ? (
                  <span className="absolute inset-x-0 -bottom-px h-[2px] bg-[color:var(--accent)]" />
                ) : null}
              </button>
            </div>
            {activeTab === "line-reconciliation" ? (
              lr.loading ? (
                <p
                  className="px-6 py-4 text-sm text-[color:var(--m-ink-tertiary)]"
                  data-testid="line-reconciliation-loading"
                >
                  Loading line reconciliation…
                </p>
              ) : lr.error ? (
                <p
                  className="px-6 py-4 text-sm text-[color:var(--m-ink-tertiary)]"
                  data-testid="line-reconciliation-error"
                >
                  Failed to load line reconciliation.
                </p>
              ) : lr.data ? (
                <LineReconciliationTab data={lr.data} />
              ) : null
            ) : (
              <div className="flex flex-col divide-y divide-[color:var(--surface-line)]/12">
                <ValidationPanel validation={data.validation} />
                <ServiceLinesTable
                  serviceLines={data.serviceLines}
                  lineReconciliation={data.lineReconciliation}
                />
                <DiagnosesList diagnoses={data.diagnoses} />
                <PartiesGrid parties={data.parties} />
                {data.matchedRemittance ? (
                  <MatchedRemitCard matchedRemittance={data.matchedRemittance} />
                ) : null}
                <RawSegmentsPanel rawSegments={data.rawSegments} />
                <StateHistoryTimeline history={data.stateHistory} />
              </div>
            )}
          </div>
        ) : null}
      </DialogContent>
    </Dialog>
  );
}
