"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { FormField } from "@/components/ui/form-field";
import { Textarea } from "@/components/ui/input";
import { useT } from "@/i18n";
import { applyFieldErrors, friendlyErrorMessage } from "@/lib/errors";
import { queryKeys } from "@/lib/query-keys";
import { acceptOrderShortage, cancelOrderItem } from "@/services/orders";

// The server's own limit; saying it here keeps the refusal on this side of the wire.
const REASON_MAX = 255;

const schema = z.object({
  reason: z.string().trim().min(1, "errors.required").max(REASON_MAX, "errors.generic"),
});

type Values = z.infer<typeof schema>;

interface Props {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  orderUuid: string;
  /** The line being called off; null while the dialog is closed. */
  item: { uuid: string; name: string } | null;
  /**
   * `cancel` calls the line off before the goods are collected; `shortage` accepts that the supplier gave less than was
   * ordered, after the count. Same reason box and the same warning that the price is worked out again.
   */
  mode?: "cancel" | "shortage";
}

/**
 * Call off one line of an order a supplier cannot give, without calling off the order.
 *
 * **The reason is required**, for the reason it is on an order: it is the only thing that answers "why is
 * this not here?" later, to the customer and to whoever reads the order after us. The delivery is priced
 * again from what is left, so what the customer pays can change, and the body says so before it happens.
 */
export function CancelItemDialog(props: Props) {
  return (
    <Dialog open={props.open} onOpenChange={props.onOpenChange}>
      <DialogContent>
        {/* Its own component, so every open starts with an empty reason, not last time's. */}
        {props.item ? <CancelItemForm {...props} item={props.item} /> : null}
      </DialogContent>
    </Dialog>
  );
}

function CancelItemForm({ onOpenChange, orderUuid, item, mode = "cancel" }: Props & { item: { uuid: string; name: string } }) {
  const t = useT();
  const prefix = mode === "shortage" ? "orders.items.shortage" : "orders.items.cancel";
  const queryClient = useQueryClient();
  const form = useForm<Values>({ resolver: zodResolver(schema), defaultValues: { reason: "" } });

  const cancel = useMutation({
    mutationFn: (values: Values) =>
      mode === "shortage"
        ? acceptOrderShortage(orderUuid, item.uuid, values.reason.trim())
        : cancelOrderItem(orderUuid, item.uuid, values.reason.trim()),
    onSuccess: (response) => {
      toast.success(response.Message ?? t(`${prefix}.done` as never));
      onOpenChange(false);
      // The order itself (its fee and amount to collect just moved), and every list that shows it.
      void queryClient.invalidateQueries({ queryKey: queryKeys.orders.detail(orderUuid) });
      void queryClient.invalidateQueries({ queryKey: ["orders"] });
    },
    onError: (error) => {
      // A refused reason belongs under the box. Whether the line may still be called off is not a field:
      // the goods can be picked up while this dialog is open, and that answer stays a toast.
      const shown = applyFieldErrors(error, (field, message) => form.setError(field as never, { message }), {
        only: ["reason"],
      });
      if (shown === 0) toast.error(friendlyErrorMessage(error, t));
    },
  });

  return (
    <>
      <DialogHeader>
        <DialogTitle>{t(`${prefix}.title` as never, { item: item.name })}</DialogTitle>
        <DialogDescription>{t(`${prefix}.body` as never)}</DialogDescription>
      </DialogHeader>

      <form
        className="space-y-5"
        onSubmit={form.handleSubmit((values) => {
          if (cancel.isPending) return;
          cancel.mutate(values);
        })}
      >
        <FormField
          label={t(`${prefix}.reason` as never)}
          htmlFor="cancel-item-reason"
          required
          error={form.formState.errors.reason?.message}
          hint={t(`${prefix}.reasonHint` as never)}
        >
          <Textarea
            id="cancel-item-reason"
            rows={3}
            maxLength={REASON_MAX}
            placeholder={t(`${prefix}.reasonPlaceholder` as never)}
            {...form.register("reason")}
          />
        </FormField>

        <DialogFooter>
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            {t("common.back")}
          </Button>
          <Button type="submit" variant="destructive" loading={cancel.isPending} disabled={cancel.isPending}>
            {cancel.isPending ? t("common.saving") : t(`${prefix}.confirm` as never)}
          </Button>
        </DialogFooter>
      </form>
    </>
  );
}
