import { Freight } from "@edr/types";
import {
  Alert,
  Button,
  Checkbox,
  Group,
  Loader,
  Modal,
  ScrollArea,
  Stack,
  Text,
} from "@mantine/core";
import { useMutation, useQuery } from "@tanstack/react-query";
import { AlertTriangle, ArrowRight, PackageCheck } from "lucide-react";
import { useMemo, useState } from "react";
import toast from "react-hot-toast";

import { api } from "@/services/api";
import type { WagonTransferRequest } from "@/services/wagon.service";

import { outstandingOn, wagonTypeLabel, yardLabel } from "./wagon-transfer-ui";

export interface TransferFulfillModalProps {
  request: WagonTransferRequest | null;
  onClose: () => void;
  onDone?: () => void;
}

/**
 * Move wagons against an open request. Any number from one up to whatever is
 * still owed — a yard that can only spare 20 of 50 sends 20 now and the request
 * stays open for the rest, so the picker caps at the OUTSTANDING count, not the
 * originally requested one.
 */
export function TransferFulfillModal({
  request,
  onClose,
  onDone,
}: TransferFulfillModalProps) {
  const [picked, setPicked] = useState<Set<string>>(new Set());
  const outstanding = request ? outstandingOn(request) : 0;

  const { data: wagons = [], isLoading } = useQuery({
    ...api.wagons.list.queryOptions({
      input: {
        filters: request
          ? {
              currentYardId: request.fromYardId,
              wagonTypeId: request.wagonTypeId,
              status: Freight.WagonStatus.Available,
            }
          : {},
      },
    }),
    enabled: Boolean(request),
  });

  const fulfill = useMutation(api.wagonTransferRequests.fulfill.mutationOptions());

  const canTake = useMemo(
    () => Math.min(outstanding, wagons.length),
    [outstanding, wagons.length],
  );

  const toggle = (id: string) =>
    setPicked((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      // Never let staff pick more than is still owed — the API rejects it too.
      else if (next.size >= outstanding) return prev;
      else next.add(id);
      return next;
    });

  const takeAllAvailable = () =>
    setPicked(new Set(wagons.slice(0, canTake).map((w) => w.id)));

  const close = () => {
    setPicked(new Set());
    onClose();
  };

  const submit = async () => {
    if (!request || picked.size === 0) return;
    try {
      const moved = picked.size;
      await fulfill.mutateAsync({ id: request.id, wagonIds: [...picked] });
      toast.success(
        moved >= outstanding
          ? `Request complete — ${moved} wagon(s) transferred`
          : `${moved} wagon(s) transferred · ${outstanding - moved} still owed`,
      );
      close();
      onDone?.();
    } catch {
      // The http interceptor surfaces the server's reason.
    }
  };

  return (
    <Modal
      opened={Boolean(request)}
      onClose={close}
      size="lg"
      radius="md"
      title={
        request ? (
          <Group gap={8} wrap="nowrap">
            <Text fw={700}>{yardLabel(request.fromYard)}</Text>
            <ArrowRight size={15} />
            <Text fw={700}>{yardLabel(request.toYard)}</Text>
            <Text c="dimmed" size="sm">
              {wagonTypeLabel(request.wagonType)}
            </Text>
          </Group>
        ) : null
      }
    >
      {!request ? null : (
        <Stack gap="sm">
          <Group justify="space-between" wrap="wrap">
            <Text size="sm">
              <Text span fw={700}>
                {outstanding}
              </Text>{" "}
              wagon(s) still owed ·{" "}
              <Text span fw={700}>
                {wagons.length}
              </Text>{" "}
              available in {yardLabel(request.fromYard)}
            </Text>
            <Button
              variant="light"
              size="xs"
              radius="md"
              disabled={canTake === 0}
              onClick={takeAllAvailable}
            >
              Select {canTake}
            </Button>
          </Group>

          {wagons.length < outstanding ? (
            <Alert color="yellow" radius="md" icon={<AlertTriangle size={15} />}>
              This yard can only cover {wagons.length} of the {outstanding}{" "}
              outstanding. Send what is here — the request stays open for the
              rest, or close it short so the requester can ask another yard.
            </Alert>
          ) : null}

          {isLoading ? (
            <Group justify="center" py="lg">
              <Loader size="sm" />
            </Group>
          ) : wagons.length === 0 ? (
            <Text c="dimmed" size="sm" py="md">
              No available wagons of this type in the source yard right now.
            </Text>
          ) : (
            <ScrollArea.Autosize mah={320}>
              <Stack gap={4}>
                {wagons.map((w) => (
                  <Checkbox
                    key={w.id}
                    checked={picked.has(w.id)}
                    onChange={() => toggle(w.id)}
                    label={w.wagonNumber}
                  />
                ))}
              </Stack>
            </ScrollArea.Autosize>
          )}

          <Group justify="flex-end" gap="sm">
            <Button variant="default" radius="md" onClick={close}>
              Cancel
            </Button>
            <Button
              color="edr-green"
              radius="md"
              leftSection={<PackageCheck size={15} />}
              loading={fulfill.isPending}
              disabled={picked.size === 0}
              onClick={() => void submit()}
            >
              Transfer {picked.size || ""}
            </Button>
          </Group>
        </Stack>
      )}
    </Modal>
  );
}

export default TransferFulfillModal;
