import { AlertCircle, WifiOff, X } from "lucide-react";
import { ApiError } from "@/lib/api";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { fmt } from "@/lib/format";
import { cn } from "@/lib/utils";
import type {
  Envelope,
  ParseResult837,
  ParseResult835,
} from "@/types";

/**
 * Skeleton placeholder for the batch detail drawer body. The numbers
 * are tuned to mirror the actual content sections (header, envelope,
 * claims/payments, summary stats) so the drawer doesn't reflow on
 * load.
 */
export function BatchDetailSkeleton() {
  return (
    <div
      className="flex flex-col gap-6 p-6"
      data-testid="batch-detail-skeleton"
      aria-busy="true"
    >
      <Skeleton variant="default" height={48} />
      <Skeleton variant="row" />
      <div className="flex flex-col gap-2">
        <Skeleton variant="row" />
        <Skeleton variant="row" />
        <Skeleton variant="row" />
      </div>
      <Skeleton variant="default" height={96} />
    </div>
  );
}

const COPY = {
  not_found: {
    eyebrow: "NOT FOUND",
    message: "This batch doesn't exist or has been removed.",
  },
  network: {
    eyebrow: "CONNECTION",
    message: "Couldn't reach the server. Check your connection and try again.",
  },
} as const;

type BatchDetailErrorProps = {
  kind: "not_found" | "network";
  onRetry?: () => void;
  onClose: () => void;
};

/**
 * Two-shape error state — `not_found` (the batch id doesn't resolve,
 * e.g. a stale deep link) vs `network` (transient fetch failure).
 * Not_found has no retry affordance (retrying won't help); network
 * does when `onRetry` is supplied. Mirrors `ClaimDrawerError`.
 */
export function BatchDetailError({
  kind,
  onRetry,
  onClose,
}: BatchDetailErrorProps) {
  const { eyebrow, message } = COPY[kind];
  const Icon = kind === "network" ? WifiOff : AlertCircle;

  return (
    <div
      className="flex flex-col items-start gap-4 p-6"
      role="alert"
      data-testid={`batch-detail-error-${kind}`}
    >
      <div className="flex items-center gap-2">
        <Icon
          className="h-5 w-5 text-destructive"
          strokeWidth={1.75}
          aria-hidden
        />
        <span className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-destructive/80">
          {eyebrow}
        </span>
      </div>
      <p className="text-sm text-muted-foreground">{message}</p>
      <div className="flex items-center gap-2">
        {kind === "network" && onRetry ? (
          <Button
            variant="outline"
            size="sm"
            onClick={onRetry}
            data-testid="batch-detail-error-retry"
          >
            Retry
          </Button>
        ) : null}
        <Button
          variant="ghost"
          size="sm"
          onClick={onClose}
          data-testid="batch-detail-error-close"
        >
          Close
        </Button>
      </div>
    </div>
  );
}

/**
 * Branch the raw error into the two shapes the spec calls out:
 *   - ApiError(404) → "not_found" (no retry, the batch is gone)
 *   - anything else → "network" (retry available)
 *
 * Note: `api.getBatch` currently throws plain `Error` with the HTTP
 * status at the start of the message (e.g. `"404 Not Found — …"`),
 * not `ApiError`. The caller is expected to rewrap 404s into
 * `ApiError(404, …)` before they reach this helper — that's done in
 * the per-batch detail hook inside `Batches.tsx`.
 */
export function batchErrorKind(error: Error | null): "not_found" | "network" | null {
  if (!error) return null;
  if (error instanceof ApiError && error.status === 404) return "not_found";
  return "network";
}

// ---------------------------------------------------------------------------
// Content (rendered once data is available)
// ---------------------------------------------------------------------------

function EnvBlock({ envelope }: { envelope: Envelope | null }) {
  if (!envelope) return null;
  return (
    <div className="grid grid-cols-2 gap-x-6 gap-y-3">
      <Field label="Sender" value={envelope.sender_id} mono />
      <Field label="Receiver" value={envelope.receiver_id} mono />
      <Field label="Control #" value={envelope.control_number} mono />
      <Field
        label="Date"
        value={`${envelope.transaction_date}${
          envelope.transaction_time ? ` ${envelope.transaction_time}` : ""
        }`}
        mono
      />
    </div>
  );
}

function Field({
  label,
  value,
  mono,
}: {
  label: string;
  value: string;
  mono?: boolean;
}) {
  return (
    <div className="min-w-0">
      <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-1">
        {label}
      </div>
      <div
        className={
          mono
            ? "font-mono text-[12.5px] truncate"
            : "text-[13px] truncate"
        }
      >
        {value}
      </div>
    </div>
  );
}

/**
 * Detail content. Accepts the parser's union return type — the actual
 * shape varies between 837P and 835 — and renders the shared envelope
 * plus a kind-specific summary. We keep this minimal (envelope +
 * counts + a peek at the claim list) because the page-level scope is
 * "batches browser" rather than "full per-batch inspector".
 *
 * Discriminating 837P vs 835: both variants carry `claims`, but the
 * row shapes differ (837P = `ClaimOutput` with `claim_id`, 835 =
 * `ClaimPayment` with `payer_claim_control_number`). The presence of
 * `payer_claim_control_number` on the first row is the discriminator.
 */
export function BatchDetailContent({
  data,
  onClose,
}: {
  data: ParseResult837 | ParseResult835;
  onClose: () => void;
}) {
  const list = data.claims;
  const firstIsPayment =
    list.length > 0 &&
    "payer_claim_control_number" in (list[0] as object);

  const count = list.length;
  const summary = data.summary;

  return (
    <div
      className="flex h-full flex-col overflow-y-auto"
      data-testid="batch-detail-content"
    >
      <header className="flex items-start justify-between gap-4 p-6 border-b border-border/60">
        <div className="min-w-0">
          <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" />
            Batch detail
          </div>
          <h2 className="text-[18px] font-semibold tracking-tight truncate">
            {summary.control_number ?? "Uncontrolled"}
          </h2>
          <div className="mt-1.5 text-[13px] text-muted-foreground">
            {firstIsPayment ? "835 ERA" : "837P"} ·{" "}
            <span className="display num text-foreground">
              {fmt.num(count)}
            </span>{" "}
            {firstIsPayment ? "payments" : "claims"}
          </div>
        </div>
        <button
          type="button"
          onClick={onClose}
          aria-label="Close"
          className="rounded-md p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted/40 transition-colors"
          data-testid="batch-detail-close"
        >
          <X className="h-4 w-4" />
        </button>
      </header>

      <section className="p-6 border-b border-border/60">
        <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-3">
          Envelope
        </div>
        <EnvBlock envelope={data.envelope} />
      </section>

      <section className="p-6 border-b border-border/60">
        <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-3">
          Summary
        </div>
        <div className="grid grid-cols-3 gap-4">
          <Stat label="Total" value={fmt.num(summary.total_claims)} />
          <Stat
            label="Passed"
            value={fmt.num(summary.passed)}
            tone="success"
          />
          <Stat
            label="Failed"
            value={fmt.num(summary.failed)}
            tone={summary.failed > 0 ? "destructive" : "muted"}
          />
        </div>
      </section>

      {list.length > 0 ? (
        <section className="p-6">
          <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-3">
            {firstIsPayment ? "Payments" : "Claims"} · first{" "}
            {Math.min(10, list.length)}
          </div>
          <ul className="font-mono text-[12px] space-y-1.5">
            {list.slice(0, 10).map((c, i) => {
              const id = firstIsPayment
                ? (c as { payer_claim_control_number: string })
                    .payer_claim_control_number
                : (c as { claim_id: string }).claim_id;
              return (
                <li
                  key={id ?? i}
                  className="flex items-center gap-2 text-muted-foreground"
                >
                  <span className="text-[10.5px] uppercase tracking-[0.14em] w-6 text-right">
                    {i + 1}
                  </span>
                  <span className="truncate">{id}</span>
                </li>
              );
            })}
          </ul>
          {list.length > 10 ? (
            <div className="mt-3 text-[11px] text-muted-foreground">
              + {fmt.num(list.length - 10)} more
            </div>
          ) : null}
        </section>
      ) : null}
    </div>
  );
}

function Stat({
  label,
  value,
  tone,
}: {
  label: string;
  value: string;
  tone?: "success" | "destructive" | "muted";
}) {
  const color =
    tone === "success"
      ? "text-emerald-400"
      : tone === "destructive"
      ? "text-red-400"
      : "text-foreground";
  return (
    <div>
      <div className="text-[10.5px] font-semibold uppercase tracking-[0.18em] text-muted-foreground mb-1">
        {label}
      </div>
      <div className={cn("display num text-[20px]", color)}>{value}</div>
    </div>
  );
}
