"use client";

import { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { PackageX } from "lucide-react";
import { toast } from "sonner";
import { Alert } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useCan } from "@/features/auth/use-can";
import { useI18n, useT } from "@/i18n";
import { friendlyErrorMessage } from "@/lib/errors";
import { formatDateTime } from "@/lib/format";
import { queryKeys } from "@/lib/query-keys";
import { declineGoodsReturn, markGoodsReturned } from "@/services/orders";
import type { Order } from "@/types/order";

/** Names and addresses on one line, for a sentence. */
function places(list?: { store_name: string | null; address: string | null }[]): string {
  const named = (list ?? []).map((place) => [place.store_name, place.address].filter(Boolean).join(", ")).filter((line) => line !== "");

  return named.length > 0 ? named.join(" / ") : "—";
}

/**
 * A failed or called-off order whose goods were already collected (or left at the wrapping shop): are they back?
 *
 * The order stays failed (that is terminal, by design), but the parcel is still in a captain's car until somebody says it came back, and a failure
 * is announced once and forgotten. This keeps it in front of the office until they record that the goods are back, and where they went.
 */
export function GoodsReturnPanel({ order }: { order: Order }) {
  const t = useT();
  const { locale } = useI18n();
  const { can } = useCan();
  const queryClient = useQueryClient();
  const [note, setNote] = useState("");

  const status = order.goods_return;

  const refresh = () => {
    void queryClient.invalidateQueries({ queryKey: queryKeys.orders.detail(order.uuid) });
    void queryClient.invalidateQueries({ queryKey: ["orders"] });
  };

  const record = useMutation({
    mutationFn: () => markGoodsReturned(order.uuid, note.trim() === "" ? null : note.trim()),
    onSuccess: (response) => {
      toast.success(response.Message ?? t("orders.goodsReturn.done"));
      refresh();
    },
    onError: (error) => toast.error(friendlyErrorMessage(error, t)),
  });

  const decline = useMutation({
    mutationFn: () => declineGoodsReturn(order.uuid),
    onSuccess: (response) => {
      toast.success(response.Message ?? t("orders.goodsReturn.declined"));
      refresh();
    },
    onError: (error) => toast.error(friendlyErrorMessage(error, t)),
  });

  if (!status) {
    return null;
  }

  if (!status.required) {
    return (
      <p className="text-xs text-muted-foreground">
        {t("orders.goodsReturn.returned", { at: status.returned_at ? formatDateTime(status.returned_at, locale) : "" })}
        {status.note ? ` · ${status.note}` : ""}
      </p>
    );
  }

  return (
    <Alert variant="warning" title={t("orders.goodsReturn.title")} icon={PackageX}>
      <p>
        {status.held_by === "wrapper"
          ? t("orders.goodsReturn.bodyWrapper", { places: places(status.held_at) })
          : /*
             * Not `order.status === "cancelled"`: cancelling an order whose goods are already out
             * carries it straight on to `awaiting_return` in the same request, so the order's own
             * status is never left sitting at plain `cancelled` once this panel actually has
             * something to say. `fulfillment.state` keeps the distinction the live status lost —
             * it reads `failed_at`, not the status column, so it still says "cancelled" after the
             * order has moved on to `awaiting_return` or, later, `returned`.
             */
            order.fulfillment?.state === "cancelled"
            ? t("orders.goodsReturn.bodyCancelled", { captain: order.driver?.name ?? "—", places: places(status.return_to) })
            : t("orders.goodsReturn.body", { captain: order.driver?.name ?? "—" })}
      </p>

      {/*
        The captain's own claim, which is a person saying the goods arrived, not the desk seeing
        them — `required` stays true either way, and the office still has to confirm or decline it.
        Shown above the action row so "the goods are back" is never a blind click.
      */}
      {status.declared_at ? (
        <p className="mt-2 rounded-md border border-warning/30 bg-background/60 px-3 py-2 text-sm text-foreground">
          {t("orders.goodsReturn.declared", { captain: order.driver?.name ?? "—", at: formatDateTime(status.declared_at, locale) })}
          {status.declared_note ? <span className="text-muted-foreground"> · {status.declared_note}</span> : null}
        </p>
      ) : null}

      {can("orders.cancel") ? (
        <div className="mt-3 flex flex-wrap items-center gap-2">
          <Input
            value={note}
            onChange={(event) => setNote(event.target.value)}
            maxLength={255}
            placeholder={t("orders.goodsReturn.notePlaceholder")}
            className="max-w-xs"
            aria-label={t("orders.goodsReturn.noteLabel")}
          />
          <Button type="button" size="sm" loading={record.isPending} disabled={record.isPending || decline.isPending} onClick={() => record.mutate()}>
            {record.isPending ? t("common.saving") : t("orders.goodsReturn.confirm")}
          </Button>
          {/* Only once there is a claim to take back: declining with nothing declared is refused, and offering it otherwise invites a 422 for no reason. */}
          {status.declared_at ? (
            <Button
              type="button"
              size="sm"
              variant="ghost"
              loading={decline.isPending}
              disabled={decline.isPending || record.isPending}
              onClick={() => decline.mutate()}
            >
              {decline.isPending ? t("common.saving") : t("orders.goodsReturn.decline")}
            </Button>
          ) : null}
        </div>
      ) : null}
    </Alert>
  );
}
