import { useEffect, useMemo, useState } from "react";
import {
  Alert,
  Badge,
  Box,
  Button,
  Group,
  Loader,
  Modal,
  Select,
  Stack,
  Text,
  Textarea,
  ThemeIcon,
} from "@mantine/core";
import { DateInput } from "@mantine/dates";
import { CalendarClock, Info } from "lucide-react";
import { useQuery } from "@tanstack/react-query";

import { api } from "@/services/api";
import {
  useBookingDetail,
  useBookingMutations,
} from "@/hooks/bookings/useBookings";

export interface OperationRescheduleModalProps {
  bookingId: string;
  opened: boolean;
  onClose: () => void;
}

/**
 * Operations moves a pending operation request to another shipment day and,
 * for export rail, another train — instead of returning it to the customer.
 * The server re-runs the customer's own gates (open departure that day, wagon
 * that can carry the cargo, export train with room) and refuses with the
 * reason if the new day does not work.
 */
export function OperationRescheduleModal({
  bookingId,
  opened,
  onClose,
}: OperationRescheduleModalProps) {
  const detailQuery = useBookingDetail(opened ? bookingId : undefined);
  const booking = detailQuery.data;
  const mutations = useBookingMutations(bookingId);

  const isExportRail = booking ? isExportRailBooking(booking) : false;

  const [day, setDay] = useState<Date | null>(null);
  const [trainId, setTrainId] = useState<string | null>(null);
  const [note, setNote] = useState("");

  // Seed from the booking each time the modal opens: the current day and, for
  // export, the train the customer picked (the detail's requested/allocated train).
  useEffect(() => {
    if (!opened || !booking) return;
    setDay(booking.scheduledDate ? new Date(booking.scheduledDate) : null);
    setTrainId(booking.trainScheduleSummary?.id ?? null);
    setNote("");
  }, [opened, booking]);

  const dayKey = day ? eatDay(day) : null;
  const currentDayKey = booking?.scheduledDate
    ? eatDay(booking.scheduledDate)
    : null;

  // Days with an open departure on the booking's route — a planning hint; the
  // server still validates the pick.
  const daysQuery = useQuery({
    ...api.trainScheduling.availableDays.queryOptions({
      input: {
        originYardId: booking?.originYard?.id ?? null,
        destinationYardId: booking?.destinationYard?.id ?? null,
      },
    }),
    enabled:
      opened &&
      Boolean(booking?.originYard?.id && booking?.destinationYard?.id),
  });
  const availableDays = useMemo(
    () => new Set((daysQuery.data ?? []).map((d) => eatDay(d))),
    [daysQuery.data],
  );
  const dayHasDeparture = dayKey ? availableDays.has(dayKey) : false;

  // Export rail: the day's export trains with free space, so staff pick one.
  const trainsQuery = useQuery({
    ...api.trainScheduling.exportTrains.queryOptions({
      input: { bookingId, date: day ? day.toISOString() : "" },
    }),
    enabled: opened && isExportRail && Boolean(day),
  });
  const trainOptions = useMemo(
    () => (trainsQuery.data ?? []).map(exportTrainOption),
    [trainsQuery.data],
  );
  // A train belongs to one day: changing the day drops a pick from another day.
  useEffect(() => {
    if (!isExportRail || !trainsQuery.data) return;
    if (trainId && !trainsQuery.data.some((t) => t.scheduleId === trainId)) {
      setTrainId(null);
    }
  }, [isExportRail, trainsQuery.data, trainId]);

  const unchanged =
    dayKey != null &&
    dayKey === currentDayKey &&
    (!isExportRail || trainId === (booking?.trainScheduleSummary?.id ?? null));
  const canSave =
    Boolean(day) && !unchanged && (!isExportRail || Boolean(trainId));

  const handleSave = () => {
    if (!day || !canSave) return;
    mutations.rescheduleOperation.mutate(
      {
        scheduledDate: day.toISOString(),
        ...(isExportRail && trainId ? { trainScheduleId: trainId } : {}),
        ...(note.trim() ? { note: note.trim() } : {}),
      },
      { onSuccess: () => onClose() },
    );
  };

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      centered
      radius="lg"
      size="md"
      title={
        <Group gap="sm">
          <ThemeIcon variant="light" color="edr-green" radius="md" size={34}>
            <CalendarClock size={18} />
          </ThemeIcon>
          <Box>
            <Text fw={600} lh={1.2}>
              Change train / shipment day
            </Text>
            <Text size="xs" c="dimmed" lh={1.2}>
              {booking?.reference ?? "Booking"}
            </Text>
          </Box>
        </Group>
      }
    >
      {detailQuery.isLoading || !booking ? (
        <Group justify="center" py="xl">
          <Loader size="sm" />
        </Group>
      ) : (
        <Stack gap="md">
          <Alert variant="light" color="blue" icon={<Info size={16} />}>
            Sets the shipment day
            {isExportRail ? " and the export train " : " "}
            for the customer, so nothing has to go back to them. The request
            stays under review for the normal accept, and the customer is told
            the new day.
            {!isExportRail
              ? " Import and domestic trains are assigned by the batch engine on the chosen day."
              : ""}
          </Alert>

          <Group gap="xs" wrap="wrap">
            <Badge variant="light" color="gray">
              Currently {currentDayKey ?? "no day"}
            </Badge>
            {booking.trainScheduleSummary ? (
              <Badge variant="light" color="gray">
                {booking.trainScheduleSummary.trainNumber ??
                  booking.trainScheduleSummary.reference ??
                  "train"}
                {booking.trainScheduleSummary.isRequested ? " (requested)" : ""}
              </Badge>
            ) : null}
          </Group>

          <DateInput
            label="New shipment day"
            description={
              daysQuery.data && daysQuery.data.length
                ? "Days with an open departure on this route are selectable."
                : "Pick the train departure day."
            }
            value={day}
            onChange={(v) => setDay(v ? new Date(v) : null)}
            minDate={new Date()}
            excludeDate={
              daysQuery.data && daysQuery.data.length
                ? (d) => !availableDays.has(eatDay(d))
                : undefined
            }
            popoverProps={{ withinPortal: true }}
          />
          {day &&
          daysQuery.data &&
          daysQuery.data.length &&
          !dayHasDeparture ? (
            <Text size="xs" c="red">
              No open departure on this route for {dayKey}.
            </Text>
          ) : null}

          {isExportRail ? (
            <Select
              label="Export train"
              placeholder={
                !day
                  ? "Pick a day first"
                  : trainsQuery.isLoading
                    ? "Loading trains…"
                    : "Select a train with room"
              }
              data={trainOptions}
              value={trainId}
              onChange={setTrainId}
              disabled={!day || trainsQuery.isLoading}
              nothingFoundMessage="No export train on this day"
              comboboxProps={{ withinPortal: true }}
              searchable
            />
          ) : null}

          <Textarea
            label="Note to customer (optional)"
            placeholder="Why the day is changing…"
            value={note}
            onChange={(e) => setNote(e.currentTarget.value)}
            autosize
            minRows={2}
          />

          <Group justify="flex-end" mt="xs">
            <Button variant="default" radius="md" onClick={onClose}>
              Cancel
            </Button>
            <Button
              radius="md"
              color="edr-green"
              leftSection={<CalendarClock size={16} />}
              loading={mutations.rescheduleOperation.isPending}
              disabled={!canSave}
              onClick={handleSave}
            >
              Save new day
            </Button>
          </Group>
        </Stack>
      )}
    </Modal>
  );
}

export default OperationRescheduleModal;
