"use client";

import * as React from "react";
import Link from "next/link";
import { ArrowDownIcon, ArrowLeftIcon, ArrowUpIcon, CircleCheckIcon, CircleXIcon, EqualIcon, OctagonAlertIcon, TriangleAlertIcon } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { cn } from "@/lib/utils";

import { ConfirmDialog, type ConfirmLevel } from "./confirm-dialog";
import { TONE_BOX } from "./tones";
import { useUnsavedChangesGuard } from "./unsaved-changes";
import { count } from "@/lib/format";

/* ------------------------------------- Types -------------------------------------- */

export type ReviewLine = {
  id: string;
  name: string;
  /** Second line under the name, e.g. batch and shelf. */
  sub?: string;
  before: number;
  after: number;
  unit: string;
  /** Value of the difference in KES (signed), from the API. */
  value: number;
  /** Marks a line with a problem; the issue explains it. */
  problem?: boolean;
};

export type ReviewIssue = { id: string; level: "blocking" | "warning"; message: string; lineId?: string };

export type ReviewTotal = { label: string; value: React.ReactNode };

export type CommitResult =
  | { ok: true; created: { label: string; href: string }[]; summary: string }
  | { ok: false; applied: string[]; failed: string[] };

/* ------------------------------------- Helpers ------------------------------------- */

/** Units are given in the plural ("tablets", "boxes"); use the singular for exactly 1. */
const singular = (unit: string) => (/(ches|shes|xes|sses)$/.test(unit) ? unit.slice(0, -2) : unit.replace(/s$/, ""));
const fmtQty = (n: number, unit: string) => `${n.toLocaleString("en-US")} ${Math.abs(n) === 1 ? singular(unit) : unit}`;
const fmtKes = (n: number) => `${n < 0 ? "−" : n > 0 ? "+" : ""}KES ${Math.abs(n).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;

function Change({ line }: { line: ReviewLine }) {
  const d = line.after - line.before;
  // Sign and word, never colour alone (spec 12, Behaviour).
  if (d === 0) return <span className="text-muted-foreground">No change</span>;
  const up = d > 0;
  return (
    <span className="inline-flex items-center gap-1 font-medium tabular-nums">
      {up ? <ArrowUpIcon className="size-3.5" aria-hidden /> : <ArrowDownIcon className="size-3.5" aria-hidden />}
      {up ? "+" : "−"}
      {fmtQty(Math.abs(d), line.unit)}
    </span>
  );
}

function StatusBadge({ line }: { line: ReviewLine }) {
  const d = line.after - line.before;
  const [label, tone, Icon] = line.problem
    ? (["Problem", "error", OctagonAlertIcon] as const)
    : d === 0
      ? (["No change", "neutral", EqualIcon] as const)
      : d > 0
        ? (["Increase", "info", ArrowUpIcon] as const)
        : (["Decrease", "warning", ArrowDownIcon] as const);
  return (
    <Badge variant="outline" className={cn("rounded-md", TONE_BOX[tone])}>
      <Icon />
      {label}
    </Badge>
  );
}

/* ------------------------------------ Component ------------------------------------ */

/**
 * Review before commit (spec 04-overlays-and-feedback §12). Shows the totals first, then any issues (each
 * linked to its line), then every change, before anything is written. Commit validates: blocking problems
 * move focus to the issues; otherwise a confirmation restates the warnings. While applying, the page cannot
 * be left. The result is a summary with links to what was created, or an exact list of what failed.
 */
export function ReviewBeforeCommit({
  title,
  steps,
  currentStep = 1,
  totals,
  issues = [],
  lines,
  beforeLabel = "Before",
  afterLabel = "After",
  commitLabel,
  busyLabel = "Applying…",
  confirm,
  loading = false,
  onBack,
  onCommit,
}: {
  title: string;
  steps: string[];
  /** Index of the review step in `steps`. */
  currentStep?: number;
  totals: ReviewTotal[];
  issues?: ReviewIssue[];
  lines: ReviewLine[];
  beforeLabel?: string;
  afterLabel?: string;
  /** Names the effect with the count: "Apply 14 adjustments". */
  commitLabel: string;
  busyLabel?: string;
  confirm: { level: ConfirmLevel; title: string; consequences: React.ReactNode[]; reasons?: string[]; confirmText?: string };
  loading?: boolean;
  onBack?: () => void;
  onCommit: (input: { reason?: string; details?: string }) => Promise<CommitResult>;
}) {
  const [onlyChanges, setOnlyChanges] = React.useState(true);
  const [onlyProblems, setOnlyProblems] = React.useState(false);
  const [confirming, setConfirming] = React.useState(false);
  const [committing, setCommitting] = React.useState(false);
  const [result, setResult] = React.useState<CommitResult | null>(null);
  const [flash, setFlash] = React.useState<string | null>(null);
  const issuesRef = React.useRef<HTMLDivElement>(null);
  const id = React.useId();

  // While applying, leaving the page is guarded (spec 12 → 13).
  const { dialog: leaveGuard } = useUnsavedChangesGuard({ dirty: committing, changes: ["the adjustments being applied"] });

  const blocking = issues.filter((i) => i.level === "blocking");
  const warnings = issues.filter((i) => i.level === "warning");
  const shown = lines.filter((l) => (onlyProblems ? l.problem : onlyChanges ? l.after !== l.before || l.problem : true));

  const goToLine = (lineId: string) => {
    setOnlyChanges(false);
    setOnlyProblems(false);
    setFlash(lineId);
    setTimeout(() => {
      document.getElementById(`${id}-line-${lineId}`)?.scrollIntoView({ block: "center", behavior: "smooth" });
      document.getElementById(`${id}-line-${lineId}`)?.focus();
    }, 50);
    setTimeout(() => setFlash(null), 2000);
  };

  const commit = () => {
    if (blocking.length) {
      issuesRef.current?.scrollIntoView({ block: "center" });
      issuesRef.current?.focus();
      return;
    }
    setConfirming(true);
  };

  if (result?.ok) {
    return (
      <section role="status" aria-label="Applied" className={cn("space-y-3 rounded-lg border p-4", TONE_BOX.success)}>
        <p className="flex items-center gap-2 text-base font-semibold">
          <CircleCheckIcon className="size-5" aria-hidden /> {result.summary}
        </p>
        <ul className="space-y-1 text-sm">
          {result.created.map((c) => (
            <li key={c.href}>
              <Link href={c.href} className="font-medium underline underline-offset-2">
                {c.label}
              </Link>
            </li>
          ))}
        </ul>
      </section>
    );
  }

  return (
    <div className="space-y-4">
      {/* 1. Step header */}
      <header className="flex flex-wrap items-center justify-between gap-3">
        <h3 className="text-lg leading-6 font-semibold">{title}</h3>
        <ol className="flex items-center gap-1.5 text-xs" aria-label="Steps">
          {steps.map((s, i) => {
            const current = i === currentStep;
            const done = i < currentStep;
            return (
              <li key={s} className="flex items-center gap-1.5">
                <span
                  aria-current={current ? "step" : undefined}
                  className={cn("rounded-full px-2 py-0.5 font-medium", current ? "bg-primary text-primary-foreground" : done ? "bg-accent text-accent-foreground" : "bg-muted text-muted-foreground")}
                >
                  {s}
                </span>
                {i < steps.length - 1 ? <span className="text-muted-foreground" aria-hidden>→</span> : null}
              </li>
            );
          })}
        </ol>
      </header>

      {/* 2. Totals summary */}
      <dl aria-label="Totals" className="grid grid-cols-2 gap-px overflow-hidden rounded-lg border border-border bg-border sm:grid-cols-4">
        {totals.map((t) => (
          <div key={t.label} className="bg-card px-3 py-2.5">
            <dt className="text-xs text-muted-foreground">{t.label}</dt>
            <dd className="mt-0.5 text-base font-semibold tabular-nums">{loading ? <Skeleton className="h-5 w-20" /> : t.value}</dd>
          </div>
        ))}
      </dl>

      {/* Partly failed: exactly what the server applied and didn't */}
      {result && !result.ok ? (
        <div role="alert" className={cn("space-y-2 rounded-lg border p-3 text-sm", TONE_BOX.error)}>
          <p className="flex items-center gap-2 font-semibold">
            <CircleXIcon className="size-4" aria-hidden /> Partly applied. Fix the failed lines and apply them again.
          </p>
          <p>
            <span className="font-medium">Applied ({result.applied.length}):</span> {result.applied.join(", ") || "none"}
          </p>
          <p>
            <span className="font-medium">Not applied ({result.failed.length}):</span> {result.failed.join("; ")}
          </p>
        </div>
      ) : null}

      {/* 3. Issues */}
      {issues.length && !loading ? (
        <div
          ref={issuesRef}
          tabIndex={-1}
          role="region"
          aria-label="Issues to resolve"
          className={cn("space-y-2 rounded-lg border p-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring", blocking.length ? TONE_BOX.error : TONE_BOX.warning)}
        >
          <p className="flex items-center gap-2 font-semibold">
            {blocking.length ? <OctagonAlertIcon className="size-4" aria-hidden /> : <TriangleAlertIcon className="size-4" aria-hidden />}
            {blocking.length
              ? `${blocking.length} ${blocking.length === 1 ? "problem stops" : "problems stop"} this being applied`
              : `${warnings.length} ${warnings.length === 1 ? "warning" : "warnings"} to check`}
          </p>
          <ul className="space-y-1">
            {[...blocking, ...warnings].map((i) => (
              <li key={i.id} className="flex gap-2">
                <span className="shrink-0 font-medium">{i.level === "blocking" ? "Must fix:" : "Check:"}</span>
                {i.lineId ? (
                  <button type="button" className="cursor-pointer text-left underline underline-offset-2" onClick={() => goToLine(i.lineId!)}>
                    {i.message}
                  </button>
                ) : (
                  <span>{i.message}</span>
                )}
              </li>
            ))}
          </ul>
        </div>
      ) : null}

      {/* 4. Change table */}
      <div className="rounded-lg border border-border bg-card">
        <div className="flex flex-wrap items-center gap-4 border-b border-border px-3 py-2 text-sm">
          <label className="flex cursor-pointer items-center gap-2">
            <Switch checked={onlyChanges} onCheckedChange={setOnlyChanges} disabled={onlyProblems} /> Only lines with changes
          </label>
          <label className="flex cursor-pointer items-center gap-2">
            <Switch checked={onlyProblems} onCheckedChange={setOnlyProblems} /> Only problems
          </label>
          <span className="ml-auto text-xs text-muted-foreground tabular-nums">
            Showing {shown.length} of {count(lines.length, "line")}
          </span>
        </div>
        <Table className="text-sm">
          <TableHeader>
            <TableRow>
              <TableHead>Item</TableHead>
              <TableHead className="text-right">{beforeLabel}</TableHead>
              <TableHead className="text-right">{afterLabel}</TableHead>
              <TableHead className="text-right">Difference</TableHead>
              <TableHead className="text-right">Value</TableHead>
              <TableHead>Status</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {loading
              ? Array.from({ length: 5 }, (_, i) => (
                  <TableRow key={i}>
                    {Array.from({ length: 6 }, (_, j) => (
                      <TableCell key={j} className="py-2">
                        <Skeleton className="h-4 w-16" />
                      </TableCell>
                    ))}
                  </TableRow>
                ))
              : shown.map((l) => (
                  <TableRow
                    key={l.id}
                    id={`${id}-line-${l.id}`}
                    tabIndex={-1}
                    className={cn("outline-none", flash === l.id && "bg-status-warning-bg", l.problem && "bg-status-error-bg/40")}
                  >
                    <TableCell className="py-2">
                      <div className="font-medium">{l.name}</div>
                      {l.sub ? <div className="text-xs text-muted-foreground">{l.sub}</div> : null}
                    </TableCell>
                    <TableCell className="py-2 text-right tabular-nums">{fmtQty(l.before, l.unit)}</TableCell>
                    <TableCell className="py-2 text-right tabular-nums">{fmtQty(l.after, l.unit)}</TableCell>
                    <TableCell className="py-2 text-right">
                      <Change line={l} />
                    </TableCell>
                    <TableCell className="py-2 text-right tabular-nums">{l.value === 0 ? "–" : fmtKes(l.value)}</TableCell>
                    <TableCell className="py-2">
                      <StatusBadge line={l} />
                    </TableCell>
                  </TableRow>
                ))}
            {!loading && shown.length === 0 ? (
              <TableRow>
                <TableCell colSpan={6} className="py-8 text-center text-muted-foreground">
                  {onlyProblems ? "No problems." : "No lines changed."}
                </TableCell>
              </TableRow>
            ) : null}
          </TableBody>
        </Table>
      </div>

      {/* 5. Sticky action bar */}
      <div role="region" aria-label="Review actions" className="sticky bottom-0 z-10 -mx-4 flex items-center justify-between gap-3 border-t border-border bg-card/95 px-4 py-3 shadow-[0_-4px_12px_-6px_rgb(0_17_16/0.18)] backdrop-blur-sm">
        <Button variant="outline" onClick={onBack} disabled={committing}>
          <ArrowLeftIcon data-icon="inline-start" />
          Back to edit
        </Button>
        <Button onClick={commit} disabled={loading || committing}>
          {committing ? busyLabel : commitLabel}
        </Button>
      </div>

      <ConfirmDialog
        open={confirming}
        onOpenChange={setConfirming}
        level={confirm.level}
        title={confirm.title}
        consequences={[...confirm.consequences, ...warnings.map((w) => `Warning: ${w.message}`)]}
        reasons={confirm.reasons}
        confirmText={confirm.confirmText}
        actionLabel={commitLabel}
        busyLabel={busyLabel}
        reasonHelp="Saved with each adjustment and shown in stock movements."
        onConfirm={async (input) => {
          setCommitting(true);
          try {
            const r = await onCommit(input);
            setResult(r);
          } finally {
            setCommitting(false);
          }
        }}
      />
      {leaveGuard}
    </div>
  );
}
