import { useCallback, useState } from "react";
import { Download } from "lucide-react";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { ApiError } from "@/lib/api";
import { DrillDrawerHeader } from "@/components/drill/DrillDrawerHeader";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { useAckDetail, type AckDetail } from "@/hooks/useAckDetail";
import { SegmentStatusList } from "./SegmentStatusList";

interface Props {
  /**
   * Currently-open ack id (string), or `null` when the drawer is
   * closed. The URL stores ids as strings so deep links round-trip
   * cleanly; the hook does the `Number()` coercion before calling
   * `api.getAck`.
   */
  ackId: string | null;
  /** Fired when the user dismisses the drawer (X button, Escape, etc.). */
  onClose: () => void;
}

/**
 * Roll a hex code into a "kind" so the drawer's error branch can
 * pick the right copy. Mirrors `ProviderDrawer`'s `errorKind`
 * computation.
 */
type ErrorKind = "not_found" | "network";

function AckDrawerError({
  kind,
  onRetry,
  onClose,
}: {
  kind: ErrorKind;
  onRetry?: () => void;
  onClose: () => void;
}) {
  const COPY = {
    not_found: {
      eyebrow: "NOT FOUND",
      message: "This 999 ACK doesn't exist or has been removed.",
    },
    network: {
      eyebrow: "CONNECTION",
      message:
        "Couldn't reach the server. Check your connection and try again.",
    },
  } as const;
  const { eyebrow, message } = COPY[kind];
  return (
    <div
      className="flex h-full flex-col"
      role="alert"
      data-testid={`ack-drawer-error-${kind}`}
    >
      <DrillDrawerHeader eyebrow="999 ACK" title="—" onClose={onClose} />
      <div className="flex flex-1 flex-col items-start gap-4 px-6 py-6">
        <span className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-destructive">
          {eyebrow}
        </span>
        <p className="text-sm text-muted-foreground max-w-sm">{message}</p>
        <div className="flex items-center gap-2">
          {kind === "network" && onRetry ? (
            <button
              type="button"
              onClick={onRetry}
              data-testid="error-retry"
              className="rounded-md border px-2.5 py-1 text-[12.5px] font-medium hover:bg-muted"
            >
              Retry
            </button>
          ) : null}
          <button
            type="button"
            onClick={onClose}
            data-testid="error-close"
            className="rounded-md border px-2.5 py-1 text-[12.5px] font-medium hover:bg-muted"
          >
            Close
          </button>
        </div>
      </div>
    </div>
  );
}

/**
 * Inline ack code pill — same color palette as `AcksPage`
 * (`Acks.tsx`) so the badge reads the same in both surfaces.
 */
function AckCodePill({ code }: { code: AckDetail["ackCode"] }) {
  const cfg =
    code === "A"
      ? {
          fg: "hsl(152 64% 30%)",
          bg: "hsl(152 50% 88%)",
          border: "hsl(152 64% 38% / 0.30)",
          label: "Accepted",
        }
      : code === "R"
        ? {
            fg: "hsl(358 70% 36%)",
            bg: "hsl(358 70% 92%)",
            border: "hsl(358 70% 50% / 0.30)",
            label: "Rejected",
          }
        : code === "P"
          ? {
              fg: "hsl(36 92% 30%)",
              bg: "hsl(36 82% 88%)",
              border: "hsl(36 92% 50% / 0.30)",
              label: "Partially accepted",
            }
          : {
              fg: "hsl(36 92% 30%)",
              bg: "hsl(36 82% 88%)",
              border: "hsl(36 92% 50% / 0.30)",
              label: "Accepted w/ errors",
            };
  return (
    <span
      className="inline-flex items-center rounded-sm border px-2 py-0.5 mono text-[10.5px] font-semibold uppercase tracking-[0.14em]"
      style={{ color: cfg.fg, backgroundColor: cfg.bg, borderColor: cfg.border }}
    >
      {cfg.label}
    </span>
  );
}

function StatTile({
  label,
  value,
  tone,
}: {
  label: string;
  value: number | string;
  tone: "success" | "destructive" | "ink";
}) {
  const color =
    tone === "success"
      ? "hsl(152 64% 30%)"
      : tone === "destructive"
        ? "hsl(358 70% 36%)"
        : "hsl(var(--foreground))";
  return (
    <div
      className="rounded-lg border px-3.5 py-3"
      style={{
        backgroundColor: "hsl(var(--card) / 0.4)",
        borderColor: "hsl(var(--border) / 0.5)",
      }}
      data-testid="ack-stat"
    >
      <div className="text-[10px] font-semibold uppercase tracking-[0.18em] text-muted-foreground">
        {label}
      </div>
      <div
        className="display mono tabular-nums mt-1"
        style={{ color, fontSize: 22, lineHeight: 1.1 }}
      >
        {value}
      </div>
    </div>
  );
}

/**
 * 999 ACK drill-down drawer (SP21 Phase 5 Task 5.2).
 *
 * Mirror of `ProviderDrawer` — same right-anchored side-panel shell,
 * same `errorKind` + `useAckDetail` shape (404 vs network), same
 * skeleton-first loading state. The body is slim: rolled-up counts
 * at the top, the ack code pill, the source batch id, and a
 * per-segment status list. The header carries a "Download 999"
 * action so the user can grab the original X12 file from the drawer
 * without a second round-trip to `/api/acks/{id}/raw`.
 *
 * Layout mirrors `ProviderDrawer`/`ClaimDrawer`: Radix Dialog
 * repositioned to the right edge as a fixed-height side panel, with
 * the shared `DrillDrawerHeader` on top and a scrollable body below.
 *
 * Error branching:
 *   - `ApiError(404)` → "not_found" (no retry, the ack is gone)
 *   - anything else   → "network" (retry available)
 */
export function AckDrawer({ ackId, onClose }: Props) {
  const { data, isLoading, isError, error, refetch } = useAckDetail(ackId);

  const errorKind: ErrorKind | null = isError
    ? error instanceof ApiError && error.status === 404
      ? "not_found"
      : "network"
    : null;

  // Download affordance for the regenerated 999 X12 text. Lives in
  // the drawer (not the page) so a deep-linked user can grab the file
  // without bouncing back to /acks. `raw_999_text` may be empty for
  // older rows without the field — we silently no-op rather than
  // surfacing an error toast (the spec calls this a "low stakes"
  // affordance).
  const [downloading, setDownloading] = useState(false);
  const onDownload = useCallback(async () => {
    if (!data) return;
    const raw =
      (data as AckDetail & { raw_999_text?: string }).raw_999_text ?? "";
    if (!raw || downloading) return;
    setDownloading(true);
    try {
      const blob = new Blob([raw], { type: "text/plain" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `ack-${data.sourceBatchId}.999`;
      a.click();
      URL.revokeObjectURL(url);
    } finally {
      setDownloading(false);
    }
  }, [data, downloading]);

  const downloadAction =
    data && (data as AckDetail & { raw_999_text?: string }).raw_999_text ? (
      <button
        type="button"
        onClick={onDownload}
        disabled={downloading}
        aria-label="Download 999 file"
        title="Download 999 file"
        data-testid="ack-drawer-download"
        className={cn(
          "inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] font-medium uppercase tracking-[0.14em] mono transition-colors",
          downloading && "opacity-50 cursor-not-allowed",
        )}
        style={{
          borderColor: "hsl(var(--border) / 0.7)",
          color: "hsl(var(--foreground))",
          backgroundColor: "hsl(var(--card) / 0.5)",
        }}
      >
        <Download className="h-3.5 w-3.5" strokeWidth={1.75} aria-hidden />
        999
      </button>
    ) : null;

  return (
    <Dialog open={ackId !== null} onOpenChange={(o) => { if (!o) onClose(); }}>
      <DialogContent
        className="fixed right-0 top-0 flex h-full w-full max-w-2xl flex-col translate-x-0 translate-y-0 rounded-none border-l border-border bg-card p-0"
        aria-describedby={undefined}
        data-testid="ack-drawer"
      >
        {errorKind ? (
          <AckDrawerError
            kind={errorKind}
            onRetry={() => {
              void refetch();
            }}
            onClose={onClose}
          />
        ) : isLoading || !data ? (
          <div className="flex h-full flex-col overflow-y-auto">
            <DrillDrawerHeader
              eyebrow="999 ACK"
              title="Loading…"
              onClose={onClose}
            />
            <div className="space-y-2 p-6">
              <Skeleton variant="row" />
              <Skeleton variant="row" />
              <Skeleton variant="row" />
            </div>
          </div>
        ) : (
          <div
            className="flex h-full flex-col overflow-y-auto"
            data-testid="ack-drawer-content"
          >
            <DrillDrawerHeader
              eyebrow="999 ACK"
              title={data.sourceBatchId}
              onClose={onClose}
              action={downloadAction}
            />
            <div className="flex flex-col divide-y divide-border/40">
              <section
                className="flex flex-col gap-4 px-6 py-4"
                data-testid="ack-summary"
              >
                <div className="flex items-center gap-3 flex-wrap">
                  <AckCodePill code={data.ackCode} />
                  <span className="mono text-[12px] text-muted-foreground">
                    ID {data.id}
                  </span>
                  <span className="mono text-[12px] text-muted-foreground">
                    · {data.parsedAt ? data.parsedAt.slice(0, 10) : "—"}
                  </span>
                </div>
                <div className="grid grid-cols-3 gap-3">
                  <StatTile
                    label="Accepted"
                    value={data.acceptedCount}
                    tone="success"
                  />
                  <StatTile
                    label="Rejected"
                    value={data.rejectedCount}
                    tone="destructive"
                  />
                  <StatTile
                    label="Received"
                    value={data.receivedCount}
                    tone="ink"
                  />
                </div>
              </section>
              <SegmentStatusList segments={[]} />
            </div>
          </div>
        )}
      </DialogContent>
    </Dialog>
  );
}
