"use client";

import * as React from "react";
import { CircleCheckIcon, CircleXIcon, ClockIcon, DownloadIcon, LoaderIcon, RotateCcwIcon, TriangleAlertIcon, type LucideIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

import { TONE_BOX, type Tone } from "./tones";

/*
 * Async operation status (spec 04 §10, C64). One state model, one set of words and icons for everything
 * that finishes later: M-Pesa, KRA eTIMS, exports and imports. The state is stored on the record and shown
 * in place, never only in a toast.
 */

export type AsyncState = "waiting" | "checking" | "queued" | "done" | "attention" | "failed";

const MODEL: Record<AsyncState, { icon: LucideIcon; tone: Tone; spin?: boolean }> = {
  waiting: { icon: LoaderIcon, tone: "info", spin: true },
  checking: { icon: LoaderIcon, tone: "info", spin: true },
  queued: { icon: ClockIcon, tone: "warning" },
  done: { icon: CircleCheckIcon, tone: "success" },
  attention: { icon: TriangleAlertIcon, tone: "warning" },
  failed: { icon: CircleXIcon, tone: "error" },
};

/**
 * Status badge: icon + word, pill. `label` is the domain word ("Sent to KRA", "Queued", "Rejected by KRA");
 * `severe` makes Needs attention red (rejected) rather than amber (mismatch). The spinner holds still under
 * reduced motion.
 */
export function AsyncBadge({ state, label, severe = false, className }: { state: AsyncState; label: string; severe?: boolean; className?: string }) {
  const m = MODEL[state];
  const Icon = m.icon;
  const tone = state === "attention" && severe ? "error" : m.tone;
  return (
    <span className={cn("inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap", TONE_BOX[tone], className)}>
      <Icon className={cn("size-3.5 shrink-0", m.spin && "motion-safe:animate-spin")} aria-hidden />
      {label}
    </span>
  );
}

/** KRA eTIMS words (spec 04 §10.3). The till never waits for KRA; this badge follows the sale. */
export const ETIMS_STATUS = {
  sending: { state: "waiting", label: "Sending to KRA" },
  sent: { state: "done", label: "Sent to KRA" },
  queued: { state: "queued", label: "Queued, sends automatically" },
  rejected: { state: "attention", label: "Rejected by KRA", severe: true },
} as const satisfies Record<string, { state: AsyncState; label: string; severe?: boolean }>;

export function EtimsBadge({ status }: { status: keyof typeof ETIMS_STATUS }) {
  const s = ETIMS_STATUS[status];
  return <AsyncBadge state={s.state} label={s.label} severe={"severe" in s ? s.severe : false} />;
}

/* ---------------------------------- Background jobs ---------------------------------- */

export type JobState = { state: "idle" } | { state: "too-large"; rows: number } | { state: "running"; pct?: number } | { state: "ready"; file: string } | { state: "failed" };

/**
 * Background job at the place it was started, e.g. the report header (spec 04 §10.4). The user keeps
 * working. Running shows a progress bar (determinate when the server reports rows); Ready keeps a Download
 * link until it's used; Failed says what to try. Too large warns before starting (server cap 20,000 rows).
 */
export function JobStatus({ job, noun = "export", onDownload, onRetry, onCancel }: { job: JobState; noun?: string; onDownload: () => void; onRetry: () => void; onCancel?: () => void }) {
  const live = job.state === "failed" ? "assertive" : "polite";
  return (
    <div aria-live={live} className="min-h-0">
      {job.state === "running" ? (
        <div className="flex items-center gap-2 text-xs text-muted-foreground">
          <AsyncBadge state="waiting" label={`${noun[0].toUpperCase()}${noun.slice(1)} running`} />
          <div
            role="progressbar"
            aria-label={`${noun} progress`}
            aria-valuemin={0}
            aria-valuemax={100}
            aria-valuenow={job.pct}
            className="relative h-1.5 w-28 overflow-hidden rounded-full bg-muted"
          >
            {job.pct != null ? (
              <div className="h-full bg-primary" style={{ width: `${job.pct}%` }} />
            ) : (
              <div className="absolute inset-y-0 w-1/3 rounded-full bg-primary motion-safe:animate-[medora-indeterminate_1.2s_ease-in-out_infinite]" />
            )}
          </div>
          {job.pct != null ? <span className="tabular-nums">{job.pct}%</span> : null}
          {onCancel ? (
            <Button size="xs" variant="ghost" onClick={onCancel}>
              Cancel
            </Button>
          ) : null}
        </div>
      ) : job.state === "ready" ? (
        <div className="flex items-center gap-2">
          <AsyncBadge state="done" label="Download ready" />
          <Button size="xs" variant="outline" onClick={onDownload}>
            <DownloadIcon data-icon="inline-start" />
            {job.file}
          </Button>
        </div>
      ) : job.state === "failed" ? (
        <div className="flex flex-wrap items-center gap-2 text-xs">
          <AsyncBadge state="failed" label={`The ${noun} didn't finish`} />
          <span className="text-muted-foreground">Try again, or narrow the date range.</span>
          <Button size="xs" variant="outline" onClick={onRetry}>
            <RotateCcwIcon data-icon="inline-start" />
            Try again
          </Button>
        </div>
      ) : job.state === "too-large" ? (
        <p className={cn("flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs", TONE_BOX.warning)}>
          <TriangleAlertIcon className="size-3.5 shrink-0" aria-hidden />
          This {noun} has {job.rows.toLocaleString("en-US")} rows, more than 20,000. Narrow the date range or branch.
        </p>
      ) : null}
    </div>
  );
}
