import { useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  Alert,
  Badge,
  Button,
  Checkbox,
  Group,
  Loader,
  Modal,
  SegmentedControl,
  Stack,
  Table,
  Text,
} from "@mantine/core";

import { useToast } from "@/hooks/use-toast";
import { importOperationsService } from "@/services/importOperations.service";
import type {
  EmptyContainerReturn,
  EmptyContainerSize,
} from "@/types/importOperations";
import type { TrainScheduleDetail } from "@/types/trainScheduling";
import { packEmptiesOntoWagons, wagonsNeeded } from "./emptyContainerLoad.util";

/** Empties still on the ground — past these the box has already left the yard. */
const LOADABLE_STATUSES = ["RETURNED", "ASSIGNED_STORAGE", "DOCUMENTATION_CLEARED"];

interface LoadEmptyContainersModalProps {
  opened: boolean;
  onClose: () => void;
  schedule: TrainScheduleDetail;
}

/**
 * Loads returned empty containers onto an export departure. Wagons are filled
 * one 40ft OR two 20ft each (see `packEmptiesOntoWagons`), drawing only on
 * wagons of this train that carry no cargo booking and no empty already.
 */
export function LoadEmptyContainersModal({
  opened,
  onClose,
  schedule,
}: LoadEmptyContainersModalProps) {
  const { toast } = useToast();
  const qc = useQueryClient();
  const [selected, setSelected] = useState<string[]>([]);
  const [sizeOverrides, setSizeOverrides] = useState<Record<string, EmptyContainerSize>>({});

  const returnsQuery = useQuery({
    queryKey: ["empty-container-returns"],
    queryFn: () => importOperationsService.listEmptyReturns(),
    enabled: opened,
  });

  const returns = returnsQuery.data ?? [];
  const loaded = useMemo(
    () => returns.filter((ret) => ret.trainScheduleId === schedule.id),
    [returns, schedule.id],
  );
  const available = useMemo(
    () =>
      returns.filter(
        (ret) => !ret.trainScheduleId && LOADABLE_STATUSES.includes(ret.status),
      ),
    [returns],
  );

  const sizeOf = (ret: EmptyContainerReturn): EmptyContainerSize =>
    sizeOverrides[ret.id] ?? (ret.containerSize === "20" ? "20" : "40");

  // A wagon is up for grabs when no booking rides it and no empty sits on it.
  const freeWagons = useMemo(() => {
    const takenByEmpties = new Set(
      loaded.map((ret) => ret.wagonSequenceNo).filter((no): no is number => no != null),
    );
    return (schedule.trainSet?.wagons ?? [])
      .filter((wagon) => !wagon.allocations?.length && !takenByEmpties.has(wagon.sequenceNo))
      .map((wagon) => wagon.sequenceNo)
      .sort((a, b) => a - b);
  }, [schedule.trainSet?.wagons, loaded]);

  const picks = useMemo(
    () =>
      available
        .filter((ret) => selected.includes(ret.id))
        .map((ret) => ({ id: ret.id, containerSize: sizeOf(ret) })),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [available, selected, sizeOverrides],
  );
  const needed = wagonsNeeded(picks);
  const { assignments, unplaced } = packEmptiesOntoWagons(picks, freeWagons);

  const load = useMutation({
    mutationFn: () =>
      importOperationsService.loadEmptyContainersOnTrain({
        trainScheduleId: schedule.id,
        trainNumber: schedule.trainNumber ?? undefined,
        items: assignments,
      }),
    onSuccess: () => {
      toast({ title: `${assignments.length} empty container(s) loaded` });
      qc.invalidateQueries({ queryKey: ["empty-container-returns"] });
      qc.invalidateQueries({ queryKey: ["train-scheduling"] });
      setSelected([]);
      onClose();
    },
    onError: (error: any) => {
      toast({
        variant: "destructive",
        title: "Failed to load empty containers",
        description: error?.response?.data?.message || error?.message,
      });
    },
  });

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      title="Load Empty Containers"
      size="xl"
    >
      <Stack gap="md">
        <Text size="sm" c="dimmed">
          One 40ft or two 20ft containers per wagon. {freeWagons.length} free wagon
          {freeWagons.length === 1 ? "" : "s"} on this train.
        </Text>

        {loaded.length > 0 ? (
          <Alert color="gray" title={`${loaded.length} empty container(s) already loaded`}>
            <Group gap="xs">
              {loaded.map((ret) => (
                <Badge key={ret.id} size="sm" variant="light">
                  {ret.containerNumber} · wagon {ret.wagonSequenceNo ?? "—"}
                </Badge>
              ))}
            </Group>
          </Alert>
        ) : null}

        {returnsQuery.isLoading ? (
          <Group justify="center" py="md">
            <Loader size="sm" />
          </Group>
        ) : available.length === 0 ? (
          <Alert color="gray">
            No returned empty containers are waiting — record returns in Container Returns.
          </Alert>
        ) : (
          <Table.ScrollContainer minWidth={700}>
            <Table highlightOnHover>
              <Table.Thead>
                <Table.Tr>
                  <Table.Th w={40} />
                  <Table.Th>Container</Table.Th>
                  <Table.Th>Size</Table.Th>
                  <Table.Th>Facility</Table.Th>
                  <Table.Th>Returned</Table.Th>
                  <Table.Th>Status</Table.Th>
                </Table.Tr>
              </Table.Thead>
              <Table.Tbody>
                {available.map((ret) => {
                  const checked = selected.includes(ret.id);
                  return (
                    <Table.Tr key={ret.id}>
                      <Table.Td>
                        <Checkbox
                          checked={checked}
                          onChange={(event) =>
                            setSelected(
                              event.currentTarget.checked
                                ? [...selected, ret.id]
                                : selected.filter((id) => id !== ret.id),
                            )
                          }
                        />
                      </Table.Td>
                      <Table.Td>
                        <Text fw={600} size="sm">
                          {ret.containerNumber}
                        </Text>
                      </Table.Td>
                      <Table.Td>
                        {/* Legacy returns carry no size — the operator sets it here
                            because the wagon rule cannot be applied without it. */}
                        <SegmentedControl
                          size="xs"
                          value={sizeOf(ret)}
                          onChange={(value) =>
                            setSizeOverrides({
                              ...sizeOverrides,
                              [ret.id]: value as EmptyContainerSize,
                            })
                          }
                          data={[
                            { label: "20ft", value: "20" },
                            { label: "40ft", value: "40" },
                          ]}
                        />
                      </Table.Td>
                      <Table.Td>{ret.facility ?? "—"}</Table.Td>
                      <Table.Td>
                        {ret.returnDate ? new Date(ret.returnDate).toLocaleDateString() : "—"}
                      </Table.Td>
                      <Table.Td>
                        <Badge size="sm" variant="light">
                          {ret.status}
                        </Badge>
                      </Table.Td>
                    </Table.Tr>
                  );
                })}
              </Table.Tbody>
            </Table>
          </Table.ScrollContainer>
        )}

        {unplaced.length > 0 ? (
          <Alert color="red">
            {needed} wagon(s) needed but only {freeWagons.length} free — unselect{" "}
            {unplaced.length} container(s) or add wagons to the consist.
          </Alert>
        ) : picks.length > 0 ? (
          <Text size="sm">
            {picks.length} container(s) → wagons{" "}
            {[...new Set(assignments.map((a) => a.wagonSequenceNo))].join(", ")}
          </Text>
        ) : null}

        <Group justify="flex-end" gap="sm">
          <Button variant="default" onClick={onClose} disabled={load.isPending}>
            Cancel
          </Button>
          <Button
            disabled={!picks.length || unplaced.length > 0}
            loading={load.isPending}
            onClick={() => load.mutate()}
          >
            Load {picks.length || ""} on train
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

export default LoadEmptyContainersModal;
