"use client";

import * as React from "react";
import { BellRingIcon, CheckIcon, ShieldCheckIcon, ShieldXIcon, XIcon } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
import { Textarea } from "@/components/ui/textarea";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { cn } from "@/lib/utils";

import { Modal, ModalClose } from "./modal";
import { Money } from "./money";
import { PasscodeInput } from "./passcode-input";
import { TONE_BOX } from "./tones";

export type ApprovalAction = "discount" | "void" | "refund" | "below-cost" | "price-override" | "waste";

export type ApprovalRequest = {
  id: string;
  action: ApprovalAction;
  /** What is being asked for, e.g. "25% discount on Augmentin 625 mg". */
  title: string;
  /** Why approval is needed, e.g. "Above your 10% discount limit". */
  why: string;
  details?: { label: string; value: React.ReactNode }[];
  amount?: number;
  requestedBy: string;
  requestedAt: Date;
};

export type Approver = { id: number; name: string; role: string };

export type ApprovalDecision = {
  status: "approved" | "declined";
  approver: Approver;
  reason: string;
  note?: string;
  via: "PIN at the till" | "Manager's device";
  at: Date;
};

/** Preset reasons per action; the first is not pre-selected, so the approver always chooses one. */
export const APPROVAL_REASONS: Record<ApprovalAction, string[]> = {
  discount: ["Loyal or regular customer", "Price match", "Damaged packaging", "Staff purchase", "Other"],
  void: ["Entered in error", "Customer changed their mind", "Payment failed", "Duplicate sale", "Other"],
  refund: ["Product returned unopened", "Wrong product dispensed", "Adverse reaction", "Overcharged", "Other"],
  "below-cost": ["Clearing near-expiry stock", "Supplier price drop", "Promotion", "Other"],
  "price-override": ["Price on shelf differs", "Agreed special price", "Other"],
  waste: ["Expired", "Damaged", "Recalled", "Cold chain failure", "Other"],
};

const ACTION_LABEL: Record<ApprovalAction, string> = {
  discount: "Discount",
  void: "Void sale",
  refund: "Refund",
  "below-cost": "Sell below cost",
  "price-override": "Price change",
  waste: "Write-off",
};

const MAX_ATTEMPTS = 3;
const LOCK_SECONDS = 30;
const hhmm = (d: Date) => d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });

function RequestSummary({ request }: { request: ApprovalRequest }) {
  return (
    <div className="space-y-2 rounded-lg border border-border bg-muted/40 p-3 text-sm">
      <div className="flex items-start justify-between gap-3">
        <div>
          <p className="font-medium text-foreground">{request.title}</p>
          <p className="text-xs text-status-warning-text">{request.why}</p>
        </div>
        {request.amount !== undefined ? <Money amount={request.amount} className="font-semibold" /> : null}
      </div>
      {request.details?.length ? (
        <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 border-t border-border pt-2 text-xs">
          {request.details.map((d) => (
            <React.Fragment key={d.label}>
              <dt className="text-muted-foreground">{d.label}</dt>
              <dd className="text-right tabular-nums">{d.value}</dd>
            </React.Fragment>
          ))}
        </dl>
      ) : null}
      <p className="text-xs text-muted-foreground">
        Asked by {request.requestedBy} at {hhmm(request.requestedAt)}
      </p>
    </div>
  );
}

/**
 * Manager approval at the till. "Approve here": the manager picks a reason and enters their PIN on this till;
 * a wrong PIN shows attempts left and three in a row lock the pad for 30 s. "Send to a manager": the request
 * goes to their queue and the till waits. `verifyPin` and `sendRemote` are the API calls; the defaults simulate
 * them for previews (PIN 1234, a remote manager approves after 4 s).
 */
export function ApprovalDialog({
  request,
  approvers,
  open,
  onOpenChange,
  onDecision,
  verifyPin = async (_approver, pin) => {
    await new Promise((r) => setTimeout(r, 500));
    return pin === "1234";
  },
  sendRemote = async (approver, reason) => {
    await new Promise((r) => setTimeout(r, 4000));
    return { status: "approved", approver, reason, via: "Manager's device", at: new Date() };
  },
}: {
  request: ApprovalRequest;
  approvers: Approver[];
  open: boolean;
  onOpenChange: (o: boolean) => void;
  onDecision: (d: ApprovalDecision) => void;
  verifyPin?: (approver: Approver, pin: string) => Promise<boolean>;
  sendRemote?: (approver: Approver, reason: string) => Promise<ApprovalDecision>;
}) {
  const [mode, setMode] = React.useState<"here" | "remote">("here");
  const [approverId, setApproverId] = React.useState<string>(String(approvers[0]?.id ?? ""));
  const [reason, setReason] = React.useState("");
  const [note, setNote] = React.useState("");
  const [attempts, setAttempts] = React.useState(0);
  const [lockedFor, setLockedFor] = React.useState(0);
  const [checking, setChecking] = React.useState(false);
  const [pinKey, setPinKey] = React.useState(0);
  const [error, setError] = React.useState("");
  const [waiting, setWaiting] = React.useState(false);
  const run = React.useRef(0);

  const approver = approvers.find((a) => String(a.id) === approverId) ?? approvers[0];
  const reasonText = reason === "Other" ? note.trim() : reason;
  const needsNote = reason === "Other" && !note.trim();

  React.useEffect(() => {
    if (lockedFor <= 0) return;
    const t = setTimeout(() => setLockedFor((s) => s - 1), 1000);
    return () => clearTimeout(t);
  }, [lockedFor]);

  const reset = () => {
    run.current++;
    setMode("here");
    setReason("");
    setNote("");
    setAttempts(0);
    setLockedFor(0);
    setChecking(false);
    setError("");
    setWaiting(false);
    setPinKey((k) => k + 1);
  };
  const close = (o: boolean) => {
    onOpenChange(o);
    if (!o) reset();
  };

  const onPin = async (pin: string) => {
    setChecking(true);
    const ok = await verifyPin(approver, pin);
    setChecking(false);
    if (ok) {
      onDecision({ status: "approved", approver, reason: reasonText, note: note.trim() || undefined, via: "PIN at the till", at: new Date() });
      close(false);
      return;
    }
    const n = attempts + 1;
    setAttempts(n);
    setPinKey((k) => k + 1);
    if (n >= MAX_ATTEMPTS) {
      setLockedFor(LOCK_SECONDS);
      setAttempts(0);
      setError("");
    } else {
      setError(`Wrong PIN for ${approver.name}. ${MAX_ATTEMPTS - n} ${MAX_ATTEMPTS - n === 1 ? "attempt" : "attempts"} left.`);
    }
  };

  const send = async () => {
    const id = ++run.current;
    setWaiting(true);
    const d = await sendRemote(approver, reasonText);
    if (id !== run.current) return; // cancelled meanwhile
    setWaiting(false);
    onDecision(d);
    close(false);
  };

  const padBlocked = !reason || needsNote || lockedFor > 0 || checking;

  return (
    <Modal
      open={open}
      onOpenChange={close}
      size="md"
      icon={ShieldCheckIcon}
      title="Manager approval needed"
      description={`${ACTION_LABEL[request.action]} needs a manager. Nothing changes until it is approved.`}
      dismissible={!waiting}
      footer={
        waiting ? (
          <Button
            variant="outline"
            onClick={() => {
              run.current++;
              setWaiting(false);
            }}
          >
            Cancel request
          </Button>
        ) : (
          <>
            <ModalClose render={<Button variant="outline" />}>Cancel</ModalClose>
            {mode === "remote" ? (
              <Button onClick={send} disabled={!reason || needsNote}>
                <BellRingIcon data-icon="inline-start" />
                Send to {approver.name.split(" ")[0]}
              </Button>
            ) : null}
          </>
        )
      }
    >
      <div className="space-y-4">
        <RequestSummary request={request} />

        {waiting ? (
          <div role="status" className={cn("flex items-center gap-3 rounded-lg border p-3 text-sm", TONE_BOX.info)}>
            <Spinner className="size-4" />
            <span>
              Waiting for <span className="font-semibold">{approver.name}</span> to approve on their device. The sale stays open.
            </span>
          </div>
        ) : (
          <>
            <ToggleGroup
              variant="outline"
              size="sm"
              spacing={0}
              className="w-full"
              aria-label="How to approve"
              value={[mode]}
              onValueChange={(v: string[]) => v[0] && setMode(v[0] as "here" | "remote")}
            >
              <ToggleGroupItem value="here" className="flex-1">
                Manager is here
              </ToggleGroupItem>
              <ToggleGroupItem value="remote" className="flex-1">
                Send to a manager
              </ToggleGroupItem>
            </ToggleGroup>

            <div className="grid gap-3">
              <Field>
                <FieldLabel>Manager</FieldLabel>
                <Select
                  value={approverId}
                  onValueChange={(v) => {
                    setApproverId(String(v));
                    setError("");
                    setAttempts(0);
                    setPinKey((k) => k + 1);
                  }}
                >
                  <SelectTrigger className="w-full">
                    <SelectValue>{approver ? `${approver.name}` : null}</SelectValue>
                  </SelectTrigger>
                  <SelectContent>
                    {approvers.map((a) => (
                      <SelectItem key={a.id} value={String(a.id)}>
                        {a.name} <span className="text-muted-foreground">· {a.role}</span>
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </Field>
              <Field>
                <FieldLabel>Reason</FieldLabel>
                <Select value={reason || null} onValueChange={(v) => setReason(String(v ?? ""))}>
                  <SelectTrigger className="w-full" aria-invalid={!reason && attempts > 0}>
                    <SelectValue placeholder="Choose a reason" />
                  </SelectTrigger>
                  <SelectContent>
                    {APPROVAL_REASONS[request.action].map((r) => (
                      <SelectItem key={r} value={r}>
                        {r}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </Field>
            </div>
            {reason === "Other" ? (
              <Field>
                <FieldLabel htmlFor="approval-note">Describe the reason</FieldLabel>
                <Textarea id="approval-note" rows={2} value={note} onChange={(e) => setNote(e.target.value)} />
              </Field>
            ) : null}

            {mode === "here" ? (
              <div className="space-y-2 border-t border-border pt-3">
                <p className="text-center text-sm font-medium">
                  {lockedFor > 0 ? (
                    <span className="text-status-error-text">Too many wrong PINs. Try again in {lockedFor} s.</span>
                  ) : !reason || needsNote ? (
                    <span className="text-muted-foreground">Choose a reason, then {approver.name.split(" ")[0]} enters their PIN</span>
                  ) : (
                    <>{approver.name.split(" ")[0]}, enter your PIN</>
                  )}
                </p>
                <PasscodeInput key={pinKey} label={`${approver.name}'s PIN`} disabled={padBlocked} error={error} onComplete={onPin} />
                {checking ? (
                  <p className="flex items-center justify-center gap-2 text-xs text-muted-foreground">
                    <Spinner className="size-3" /> Checking
                  </p>
                ) : null}
              </div>
            ) : (
              <p className="text-xs text-muted-foreground">
                {approver.name} gets the request with the reason and amount, and approves or declines it from their device.
              </p>
            )}
          </>
        )}
      </div>
    </Modal>
  );
}

/* ----------------------------------- Approval record --------------------------------- */

/** The logged outcome of an approval: requested, then approved or declined, by whom, how and why. */
export function ApprovalRecord({ request, decision, className }: { request: ApprovalRequest; decision?: ApprovalDecision; className?: string }) {
  const status = decision?.status ?? "pending";
  const tone = status === "approved" ? "success" : status === "declined" ? "error" : "warning";
  const steps = [
    { label: "Requested", who: request.requestedBy, at: request.requestedAt, state: "done" as const },
    decision
      ? { label: decision.status === "approved" ? "Approved" : "Declined", who: `${decision.approver.name} · ${decision.via}`, at: decision.at, state: decision.status === "approved" ? ("done" as const) : ("failed" as const) }
      : { label: "Awaiting a manager", who: "", at: undefined, state: "current" as const },
  ];
  return (
    <article className={cn("space-y-3 rounded-lg border border-border bg-card p-3", className)} aria-label={`Approval ${request.id}`}>
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="truncate text-sm font-medium">{request.title}</p>
          <p className="text-xs text-muted-foreground">
            {ACTION_LABEL[request.action]} · {request.id}
          </p>
        </div>
        <div className="flex shrink-0 flex-col items-end gap-1">
          <Badge variant="outline" className={cn("rounded-md capitalize", TONE_BOX[tone])}>
            {status}
          </Badge>
          {request.amount !== undefined ? <Money amount={request.amount} size="sm" /> : null}
        </div>
      </div>
      <ol className="space-y-2">
        {steps.map((s) => (
          <li key={s.label} className="flex gap-2.5">
            <span
              className={cn(
                "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full border",
                s.state === "done" && "border-primary bg-primary text-primary-foreground",
                s.state === "failed" && "border-status-error-border bg-status-error-bg text-status-error-text",
                s.state === "current" && "border-status-warning-border bg-status-warning-bg text-status-warning-text",
              )}
              aria-hidden
            >
              {s.state === "done" ? <CheckIcon className="size-3" /> : s.state === "failed" ? <XIcon className="size-3" /> : <span className="size-1.5 rounded-full bg-current" />}
            </span>
            <span className="min-w-0 text-xs">
              <span className="font-medium text-foreground">{s.label}</span>
              {s.at ? <span className="ml-1 text-muted-foreground tabular-nums">{hhmm(s.at)}</span> : null}
              {s.who ? <span className="block text-muted-foreground">{s.who}</span> : null}
            </span>
          </li>
        ))}
      </ol>
      {decision?.reason ? (
        <p className="border-t border-border pt-2 text-xs">
          <span className="text-muted-foreground">Reason:</span> {decision.reason}
        </p>
      ) : null}
    </article>
  );
}

/* ---------------------------------- Manager's queue ---------------------------------- */

/** A request in a manager's queue: approve, or decline with a note the cashier will see. */
export function ApprovalQueueItem({
  request,
  onApprove,
  onDecline,
}: {
  request: ApprovalRequest;
  onApprove: () => void;
  onDecline: (note: string) => void;
}) {
  const [declining, setDeclining] = React.useState(false);
  const [note, setNote] = React.useState("");
  return (
    <div className="space-y-3 rounded-lg border border-border bg-card p-3">
      <RequestSummary request={request} />
      {declining ? (
        <div className="space-y-2">
          <Field>
            <FieldLabel htmlFor={`decline-${request.id}`}>Why are you declining? The cashier sees this.</FieldLabel>
            <Textarea id={`decline-${request.id}`} rows={2} value={note} onChange={(e) => setNote(e.target.value)} />
          </Field>
          <div className="flex justify-end gap-2">
            <Button variant="ghost" size="sm" onClick={() => setDeclining(false)}>
              Back
            </Button>
            <Button variant="destructive" size="sm" disabled={!note.trim()} onClick={() => onDecline(note.trim())}>
              <ShieldXIcon data-icon="inline-start" />
              Decline
            </Button>
          </div>
        </div>
      ) : (
        <div className="flex justify-end gap-2">
          <Button variant="outline" size="sm" onClick={() => setDeclining(true)}>
            Decline
          </Button>
          <Button size="sm" onClick={onApprove}>
            <ShieldCheckIcon data-icon="inline-start" />
            Approve
          </Button>
        </div>
      )}
    </div>
  );
}
