"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 { useT } from "@/i18n";
import { cn } from "@/lib/cn";
import { friendlyErrorMessage } from "@/lib/errors";
import { useFieldErrors } from "@/lib/use-field-errors";
import { queryKeys } from "@/lib/query-keys";
import { adjustCaptainLedger, toCents } from "@/services/ledger";

type Side = "credit" | "debit";

interface AdjustDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  captainUlid: string;
  captainName: string;
  defaultCurrency: string;
}

/**
 * A correction to a captain's balance.
 *
 * The server takes a **signed** amount, but asking a cashier to type `-40` to mean "the captain
 * owes us forty more" is an error waiting to happen: the minus sign is easy to drop, and dropping
 * it moves the money the wrong way by twice the amount. So the dialog asks which way in words —
 * credit or debit — takes a positive number, and applies the sign itself.
 *
 * The note is required, and not by our choice alone: the server refuses an adjustment without
 * one. A correction nobody can explain later is indistinguishable from a mistake.
 */
export function AdjustDialog(props: AdjustDialogProps) {
  return (
    <Dialog open={props.open} onOpenChange={props.onOpenChange}>
      <DialogContent>
        {/* Its own component so each open starts blank — see SettleDialog for why. */}
        <AdjustForm {...props} />
      </DialogContent>
    </Dialog>
  );
}

function AdjustForm({ onOpenChange, captainUlid, captainName, defaultCurrency }: AdjustDialogProps) {
  const t = useT();
  const queryClient = useQueryClient();

  const [side, setSide] = useState<Side>("credit");
  const [amount, setAmount] = useState("");
  const [currency, setCurrency] = useState(defaultCurrency);
  const [note, setNote] = useState("");
  const [reference, setReference] = useState("");

  const parsed = Number(amount);
  const amountError =
    amount.trim() === "" || Number.isNaN(parsed)
      ? t("ledger.errors.amountRequired")
      : toCents(parsed) < 1
        ? t("ledger.errors.amountPositive")
        : null;
  const currencyError = /^[A-Za-z]{3}$/.test(currency.trim()) ? null : t("ledger.errors.currency");
  const noteError = note.trim().length < 3 ? t("ledger.errors.noteRequired") : null;
  const invalid = amountError !== null || currencyError !== null || noteError !== null;

  // The desk's own checks run first; this is for the rules only the server knows — a currency
  // the captain holds nothing in, a note it judges too short.
  const fields = useFieldErrors({ only: ["amount", "currency", "note", "reference"] });

  /** The client's objection if there is one, otherwise the server's for that field. */
  const fieldMessage = (client: string | null, name: string) => client ?? fields.get(name);

  const adjust = useMutation({
    mutationFn: () =>
      adjustCaptainLedger(captainUlid, {
        // Credit raises what we owe the captain, which is the positive side of their balance.
        amount: side === "credit" ? parsed : -parsed,
        currency: currency.trim().toUpperCase(),
        note: note.trim(),
        reference: reference.trim() || undefined,
      }),
    onSuccess: (response) => {
      toast.success(response.Message ?? t("ledger.adjusted"));
      onOpenChange(false);
      void queryClient.invalidateQueries({ queryKey: queryKeys.ledger.all });
    },
    onMutate: fields.reset,
    onError: (err) => {
      // A refusal this dialog cannot place — a permission, a captain that no longer exists —
      // still has to be said out loud.
      if (!fields.capture(err)) toast.error(friendlyErrorMessage(err, t));
    },
  });

  return (
    <>
      <DialogHeader>
        <DialogTitle>{t("ledger.adjust.title")}</DialogTitle>
        <DialogDescription>{t("ledger.adjust.body", { name: captainName })}</DialogDescription>
      </DialogHeader>

      <form
        className="space-y-4"
        onSubmit={(event) => {
          event.preventDefault();
          if (invalid || adjust.isPending) return;
          adjust.mutate();
        }}
      >
        <fieldset className="space-y-1.5">
          <legend className="text-sm font-medium">{t("ledger.adjust.direction")}</legend>
          <div className="grid grid-cols-2 gap-2" role="radiogroup">
            {(["credit", "debit"] as const).map((option) => (
              <button
                key={option}
                type="button"
                role="radio"
                aria-checked={side === option}
                onClick={() => setSide(option)}
                className={cn(
                  "rounded-md border p-3 text-start text-sm transition-colors",
                  side === option ? "border-primary bg-primary-soft" : "border-border hover:bg-muted/50",
                )}
              >
                <span className="block font-medium">{t(`ledger.adjust.${option}`)}</span>
                <span className="block text-xs text-muted-foreground">
                  {t(`ledger.adjust.${option}Hint`)}
                </span>
              </button>
            ))}
          </div>
        </fieldset>

        <div className="grid grid-cols-3 gap-3">
          <div className="col-span-2 space-y-1.5">
            <Label htmlFor="adjust-amount">{t("ledger.amount")}</Label>
            <Input
              id="adjust-amount"
              type="number"
              inputMode="decimal"
              step="0.01"
              min="0.01"
              value={amount}
              onChange={(event) => setAmount(event.target.value)}
              aria-invalid={
                (amount !== "" && amountError !== null) || fields.get("amount") !== undefined
              }
              autoFocus
            />
            {fieldMessage(amount !== "" ? amountError : null, "amount") ? (
              <p className="text-xs text-destructive">
                {fieldMessage(amount !== "" ? amountError : null, "amount")}
              </p>
            ) : null}
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="adjust-currency">{t("ledger.currency")}</Label>
            <Input
              id="adjust-currency"
              value={currency}
              maxLength={3}
              onChange={(event) => setCurrency(event.target.value.toUpperCase())}
              aria-invalid={currencyError !== null || fields.get("currency") !== undefined}
            />
          </div>
        </div>
        {fieldMessage(currencyError, "currency") ? (
          <p className="-mt-2 text-xs text-destructive">
            {fieldMessage(currencyError, "currency")}
          </p>
        ) : null}

        <div className="space-y-1.5">
          <Label htmlFor="adjust-note">{t("ledger.adjust.reason")}</Label>
          <Input
            id="adjust-note"
            value={note}
            maxLength={255}
            onChange={(event) => setNote(event.target.value)}
            placeholder={t("ledger.adjust.reasonHint")}
            aria-invalid={(note !== "" && noteError !== null) || fields.get("note") !== undefined}
          />
          {fieldMessage(note !== "" ? noteError : null, "note") ? (
            <p className="text-xs text-destructive">
              {fieldMessage(note !== "" ? noteError : null, "note")}
            </p>
          ) : null}
        </div>

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

        <DialogFooter>
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            {t("common.cancel")}
          </Button>
          <Button type="submit" disabled={invalid || adjust.isPending}>
            {adjust.isPending ? t("common.saving") : t("ledger.adjust.confirm")}
          </Button>
        </DialogFooter>
      </form>
    </>
  );
}
