"use client";

import { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useI18n, useT } from "@/i18n";
import { formatCurrency } from "@/lib/format";
import { friendlyErrorMessage } from "@/lib/errors";
import { useFieldErrors } from "@/lib/use-field-errors";
import { queryKeys } from "@/lib/query-keys";
import { settleCaptainLedger, toCents } from "@/services/ledger";
import type { SettleDirection } from "@/types/ledger";

interface SettleDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  captainUlid: string;
  captainName: string;
  currency: string;
  outstanding: number;
  direction: SettleDirection;
}

/**
 * Record cash changing hands at the desk.
 *
 * **The direction is not a choice here, on purpose.** A captain who is owed can only be paid; one
 * who owes can only hand cash in. The server refuses the other movement, so offering it would
 * only let a cashier build a request that is guaranteed to fail — and a toggle defaulting to the
 * wrong side is exactly the kind of thing somebody clicks through at the end of a shift. The
 * direction comes from the balance, and the dialog says it in words.
 */
export function SettleDialog(props: SettleDialogProps) {
  return (
    <Dialog open={props.open} onOpenChange={props.onOpenChange}>
      <DialogContent>
        {/*
          The form lives in its own component so its fields are *born* with the current figures
          rather than reset to them. The dialog unmounts its content on close, so every open
          starts from the balance as it stands now — a dialog that remembered last time's
          half-typed amount would settle against a balance that has since moved.
        */}
        <SettleForm {...props} />
      </DialogContent>
    </Dialog>
  );
}

function SettleForm({
  onOpenChange,
  captainUlid,
  captainName,
  currency,
  outstanding,
  direction,
}: SettleDialogProps) {
  const t = useT();
  const { locale } = useI18n();
  const queryClient = useQueryClient();

  // The full outstanding figure, because clearing the balance is what a cashier does nine times
  // in ten; a part-payment is one edit away.
  const [amount, setAmount] = useState(String(outstanding));
  const [reference, setReference] = useState("");
  const [note, setNote] = useState("");

  const parsed = Number(amount);
  const amountError =
    amount.trim() === "" || Number.isNaN(parsed)
      ? t("ledger.errors.amountRequired")
      : toCents(parsed) < 1
        ? t("ledger.errors.amountPositive")
        : toCents(parsed) > toCents(outstanding)
          ? t("ledger.errors.moreThanOutstanding", {
              amount: formatCurrency(outstanding, currency, locale),
            })
          : null;

  // Shown under the field rather than only as a toast: an amount past the balance as it stands
  // *now*, or a currency the captain holds nothing in.
  const fields = useFieldErrors({
    only: ["amount", "currency", "direction", "reference", "note"],
  });

  const settle = useMutation({
    mutationFn: () =>
      settleCaptainLedger(captainUlid, {
        direction,
        amount: parsed,
        currency,
        reference: reference.trim() || undefined,
        note: note.trim() || undefined,
      }),
    onSuccess: (response) => {
      toast.success(response.Message ?? t("ledger.settled"));
      onOpenChange(false);
      // Both the desk list and this captain's statement show the balance that just moved.
      void queryClient.invalidateQueries({ queryKey: queryKeys.ledger.all });
    },
    onMutate: fields.reset,
    // The server's message is already in the operator's language, and it is the authority: the
    // balance can move between opening this dialog and pressing the button. A refusal naming a
    // field goes under it; anything else stays a toast.
    onError: (err) => {
      if (!fields.capture(err)) toast.error(friendlyErrorMessage(err, t));
    },
  });

  const isPayout = direction === "cash_paid_out";
  const formatted = formatCurrency(outstanding, currency, locale);

  return (
    <>
      <DialogHeader>
        <DialogTitle>
          {isPayout ? t("ledger.settle.payoutTitle") : t("ledger.settle.handInTitle")}
        </DialogTitle>
        <DialogDescription>
          {isPayout
            ? t("ledger.settle.payoutBody", { name: captainName, amount: formatted })
            : t("ledger.settle.handInBody", { name: captainName, amount: formatted })}
        </DialogDescription>
      </DialogHeader>

      <form
        className="space-y-4"
        onSubmit={(event) => {
          event.preventDefault();
          if (amountError || settle.isPending) return;
          settle.mutate();
        }}
      >
        <div className="space-y-1.5">
          <Label htmlFor="settle-amount">
            {t("ledger.amount")} ({currency})
          </Label>
          <Input
            id="settle-amount"
            type="number"
            inputMode="decimal"
            step="0.01"
            min="0.01"
            max={outstanding}
            value={amount}
            onChange={(event) => setAmount(event.target.value)}
            aria-invalid={amountError !== null || fields.get("amount") !== undefined}
            autoFocus
          />
          {amountError ?? fields.get("amount") ? (
            <p className="text-xs text-destructive">{amountError ?? fields.get("amount")}</p>
          ) : toCents(parsed) < toCents(outstanding) ? (
            // A part-payment is legal, but it should never happen by accident.
            <p className="text-xs text-muted-foreground">
              {t("ledger.settle.partial", {
                left: formatCurrency(
                  (toCents(outstanding) - toCents(parsed)) / 100,
                  currency,
                  locale,
                ),
              })}
            </p>
          ) : null}
        </div>

        <div className="space-y-1.5">
          <Label htmlFor="settle-reference">
            {t("ledger.reference")}{" "}
            <span className="text-muted-foreground">({t("common.optional")})</span>
          </Label>
          <Input
            id="settle-reference"
            value={reference}
            maxLength={100}
            onChange={(event) => setReference(event.target.value)}
            placeholder={t("ledger.referenceHint")}
            aria-invalid={fields.get("reference") !== undefined}
          />
          {fields.get("reference") ? (
            <p className="text-xs text-destructive">{fields.get("reference")}</p>
          ) : null}
        </div>

        <div className="space-y-1.5">
          <Label htmlFor="settle-note">
            {t("ledger.note")} <span className="text-muted-foreground">({t("common.optional")})</span>
          </Label>
          <Input
            id="settle-note"
            value={note}
            maxLength={255}
            onChange={(event) => setNote(event.target.value)}
            aria-invalid={fields.get("note") !== undefined}
          />
          {fields.get("note") ? (
            <p className="text-xs text-destructive">{fields.get("note")}</p>
          ) : null}
        </div>

        {/* The direction is a pair of buttons and the currency is fixed here, so a refusal
            naming either has nowhere of its own to live. */}
        {fields.get("direction") ?? fields.get("currency") ? (
          <p className="text-xs text-destructive">
            {fields.get("direction") ?? fields.get("currency")}
          </p>
        ) : null}

        <DialogFooter>
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            {t("common.cancel")}
          </Button>
          <Button type="submit" disabled={amountError !== null || settle.isPending}>
            {settle.isPending
              ? t("common.saving")
              : isPayout
                ? t("ledger.settle.payoutConfirm")
                : t("ledger.settle.handInConfirm")}
          </Button>
        </DialogFooter>
      </form>
    </>
  );
}
