import { useEffect, useMemo, useState } from "react";
import {
  Alert,
  Badge,
  Box,
  Button,
  Divider,
  Group,
  Loader,
  Modal,
  NumberInput,
  Select,
  Stack,
  Switch,
  Text,
  ThemeIcon,
} from "@mantine/core";
import { isAxiosError } from "axios";
import { Clock, Info, Moon, Sun } from "lucide-react";
import { useMutation, useQuery } from "@tanstack/react-query";

import DurationField from "@/components/trainScheduling/DurationField";
import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import type { UpdateScheduleWindowRulePayload } from "@/types/trainScheduling";

/** Fallbacks matching the API's global-rules defaults (used when a field is null). */
const DEFAULTS = {
  windowOpenHour: 8,
  windowCloseHour: 17,
  windowDurationHours: 3,
  docReviewMinutes: 30,
  paymentWindowMinutes: 60,
  importWindowLeadDays: 3,
  exportBookingLeadHours: 24,
};

/** 12-hour label for an EAT hour 0–23, e.g. 8 → "8:00 AM", 17 → "5:00 PM". */
function hourLabel(hour: number): string {
  const period = hour < 12 ? "AM" : "PM";
  const h12 = hour % 12 === 0 ? 12 : hour % 12;
  return `${h12}:00 ${period}`;
}

const HOUR_OPTIONS = Array.from({ length: 24 }, (_, h) => ({
  value: String(h),
  label: `${hourLabel(h)}  ·  ${String(h).padStart(2, "0")}:00`,
}));

interface FormState {
  windowOpenHour: number;
  windowCloseHour: number;
  windowDurationHours: number | "";
  docReviewMinutes: number | "";
  paymentWindowMinutes: number | "";
  importWindowLeadDays: number | "";
  exportBookingLeadHours: number | "";
}

function parseError(error: unknown, fallback: string): string {
  if (isAxiosError(error)) {
    const message = error.response?.data?.message;
    if (Array.isArray(message)) return message.join(", ");
    if (typeof message === "string") return message;
  }
  return fallback;
}

export interface BookingWindowSettingsModalProps {
  scheduleId: string | null;
  opened: boolean;
  onClose: () => void;
  /** Called after a successful save (e.g. to refetch a list). */
  onSaved?: () => void;
}

/**
 * Per-schedule booking-window settings editor. Prefills from the schedule's own
 * rule snapshot, lets staff tune the daily desk hours / durations for just that
 * train, and saves an override. Only editable before the window opens.
 */
export default function BookingWindowSettingsModal({
  scheduleId,
  opened,
  onClose,
  onSaved,
}: BookingWindowSettingsModalProps) {
  const { toast } = useToast();

  const detailQuery = useQuery({
    ...api.trainScheduling.scheduleDetail.queryOptions({
      input: { id: scheduleId ?? "" },
    }),
    enabled: opened && Boolean(scheduleId),
  });
  const schedule = detailQuery.data;

  const save = useMutation(
    api.trainScheduling.updateScheduleWindowRule.mutationOptions(),
  );

  const [form, setForm] = useState<FormState | null>(null);

  // Seed the form from the schedule's snapshot once it loads (or when reopened).
  useEffect(() => {
    if (!opened || !schedule) return;
    const r = schedule.windowRule;
    setForm({
      windowOpenHour: r?.windowOpenHour ?? DEFAULTS.windowOpenHour,
      windowCloseHour: r?.windowCloseHour ?? DEFAULTS.windowCloseHour,
      windowDurationHours: r?.windowDurationHours ?? DEFAULTS.windowDurationHours,
      docReviewMinutes: r?.docReviewMinutes ?? DEFAULTS.docReviewMinutes,
      paymentWindowMinutes:
        r?.paymentWindowMinutes ?? DEFAULTS.paymentWindowMinutes,
      importWindowLeadDays:
        r?.importWindowLeadDays ?? DEFAULTS.importWindowLeadDays,
      exportBookingLeadHours:
        r?.exportBookingLeadHours ?? DEFAULTS.exportBookingLeadHours,
    });
  }, [opened, schedule]);

  const isExport = schedule?.direction === "EXPORT";
  const canEdit = schedule?.windowPhase === "PRE_WINDOW";
  const is24h =
    form != null && form.windowOpenHour === form.windowCloseHour;
  // Close < open is a valid OVERNIGHT desk (e.g. 08:00 → 07:00 next morning),
  // not an error — the engine wraps it across midnight.
  const isOvernight =
    form != null && form.windowCloseHour < form.windowOpenHour;

  const reopenSummary = useMemo(() => {
    if (!form) return "";
    const doc = Number(form.docReviewMinutes) || 0;
    const pay = Number(form.paymentWindowMinutes) || 0;
    const total = doc + pay;
    const h = Math.floor(total / 60);
    const m = total % 60;
    const parts = [h ? `${h}h` : "", m ? `${m}m` : ""].filter(Boolean);
    return parts.length ? parts.join(" ") : "0m";
  }, [form]);

  const handleSave = async () => {
    if (!scheduleId || !form) return;
    // Numeric fields must hold real values.
    const duration = Number(form.windowDurationHours);
    const doc = Number(form.docReviewMinutes);
    const pay = Number(form.paymentWindowMinutes);
    const lead = Number(form.importWindowLeadDays);
    const exportLead = Number(form.exportBookingLeadHours);
    const leadInvalid = isExport
      ? form.exportBookingLeadHours === "" ||
        !Number.isFinite(exportLead) ||
        exportLead < 1
      : form.importWindowLeadDays === "" || !Number.isFinite(lead);
    if (
      form.windowDurationHours === "" ||
      form.docReviewMinutes === "" ||
      form.paymentWindowMinutes === "" ||
      !Number.isFinite(duration) ||
      !Number.isFinite(doc) ||
      !Number.isFinite(pay) ||
      leadInvalid
    ) {
      toast({
        title: "Fill every field before saving",
        variant: "destructive",
      });
      return;
    }
    const payload: UpdateScheduleWindowRulePayload = {
      windowOpenHour: form.windowOpenHour,
      windowCloseHour: form.windowCloseHour,
      windowDurationHours: duration,
      docReviewMinutes: doc,
      paymentWindowMinutes: pay,
      ...(isExport
        ? { exportBookingLeadHours: exportLead }
        : { importWindowLeadDays: lead }),
    };

    try {
      await save.mutateAsync({ id: scheduleId, payload });
      toast({ title: "Booking window settings updated" });
      onSaved?.();
      onClose();
    } catch (err) {
      toast({
        title: "Update failed",
        description: parseError(err, "Could not update booking window"),
        variant: "destructive",
      });
    }
  };

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      centered
      radius="lg"
      size="lg"
      title={
        <Group gap="sm">
          <ThemeIcon variant="light" color="edr-green" radius="md" size={34}>
            <Clock size={18} />
          </ThemeIcon>
          <Box>
            <Text fw={600} lh={1.2}>
              Booking window settings
            </Text>
            <Text size="xs" c="dimmed" lh={1.2}>
              {schedule?.route?.name ?? "This schedule only"}
            </Text>
          </Box>
        </Group>
      }
    >
      {detailQuery.isLoading || !form ? (
        <Group justify="center" py="xl">
          <Loader size="sm" />
        </Group>
      ) : !canEdit ? (
        <Alert
          variant="light"
          color="yellow"
          icon={<Info size={16} />}
          title="Window already open"
        >
          Booking window settings can only be changed before the window opens.
          This schedule is currently{" "}
          <b>{String(schedule?.windowPhase ?? "not window-managed")}</b>.
        </Alert>
      ) : (
        <Stack gap="lg">
          {isExport ? (
            <Alert variant="light" color="blue" icon={<Info size={16} />}>
              Export schedules use a single first-come-first-served window: it
              opens the export lead time before departure — shifted to the next
              desk opening if that lands outside desk hours — and stays open
              until departure. Cycle timing below doesn't apply.
            </Alert>
          ) : null}

          {/* ── Daily desk hours ─────────────────────────────────────────── */}
          <Box>
            <Group justify="space-between" align="center" mb={6}>
              <Text size="sm" fw={600}>
                Daily desk hours (EAT)
              </Text>
              {is24h ? (
                <Badge
                  variant="light"
                  color="grape"
                  leftSection={<Moon size={12} />}
                >
                  24-hour desk
                </Badge>
              ) : (
                <Badge
                  variant="light"
                  color="edr-green"
                  leftSection={<Sun size={12} />}
                >
                  {hourLabel(form.windowOpenHour)} – {hourLabel(form.windowCloseHour)}
                </Badge>
              )}
            </Group>
            <Group grow align="flex-start">
              <Select
                label="Opens"
                data={HOUR_OPTIONS}
                value={String(form.windowOpenHour)}
                onChange={(v) =>
                  v != null &&
                  setForm((f) => f && { ...f, windowOpenHour: Number(v) })
                }
                allowDeselect={false}
                comboboxProps={{ withinPortal: true }}
              />
              <Select
                label="Closes"
                data={HOUR_OPTIONS}
                value={String(form.windowCloseHour)}
                onChange={(v) =>
                  v != null &&
                  setForm((f) => f && { ...f, windowCloseHour: Number(v) })
                }
                allowDeselect={false}
                comboboxProps={{ withinPortal: true }}
              />
            </Group>
            {isOvernight && !is24h ? (
              <Text size="xs" c="dimmed" mt={4}>
                Overnight desk — opens {form.windowOpenHour}:00 and runs past
                midnight, closing {form.windowCloseHour}:00 the next morning.
              </Text>
            ) : null}
            <Switch
              mt="sm"
              size="sm"
              color="grape"
              label="Run 24 hours a day (never pause overnight)"
              checked={is24h}
              onChange={(e) => {
                const checked = e.currentTarget.checked;
                setForm((f) => {
                  if (!f) return f;
                  // On → close == open (24h desk). Off → restore a normal ~9h
                  // day, always kept ≥ open hour so it never lands invalid.
                  const close = checked
                    ? f.windowOpenHour
                    : Math.min(23, f.windowOpenHour + 9);
                  return { ...f, windowCloseHour: close };
                });
              }}
            />
            <Text size="xs" c="dimmed" mt={6}>
              {isExport
                ? "If the export lead time lands while the desk is shut, booking opens at the next desk opening instead."
                : "A not-yet-full train pauses at the close hour and resumes the next morning at the open hour, every day until it fills or departs."}
            </Text>
          </Box>

          <Divider />

          {/* ── Cycle timing ─────────────────────────────────────────────── */}
          <Box>
            <Text size="sm" fw={600} mb={6}>
              Cycle timing
            </Text>
            <Stack gap="sm">
              <DurationField
                label="Window duration"
                description="How long each booking cycle stays open before it closes for review"
                value={form.windowDurationHours}
                nativeUnit="hours"
                onChange={(v) =>
                  setForm((f) => f && { ...f, windowDurationHours: v })
                }
                min={0.0166}
                disabled={isExport}
              />
              <Group grow align="flex-start">
                <DurationField
                  label="Document review"
                  description="Staff time to accept documents after the window closes"
                  value={form.docReviewMinutes}
                  nativeUnit="minutes"
                  onChange={(v) =>
                    setForm((f) => f && { ...f, docReviewMinutes: v })
                  }
                  min={0}
                  disabled={isExport}
                />
                <DurationField
                  label="Payment window"
                  description="Time a selected customer has to pay"
                  value={form.paymentWindowMinutes}
                  nativeUnit="minutes"
                  onChange={(v) =>
                    setForm((f) => f && { ...f, paymentWindowMinutes: v })
                  }
                  min={1}
                  disabled={isExport}
                />
              </Group>
              {!isExport ? (
                <Text size="xs" c="dimmed">
                  Reopen gap after each cycle = document review + payment ={" "}
                  <b>{reopenSummary}</b>.
                </Text>
              ) : null}
            </Stack>
          </Box>

          <Divider />

          {/* ── Lead time ────────────────────────────────────────────────── */}
          {isExport ? (
            <NumberInput
              label="Export booking lead (hours)"
              description="How many hours before departure the export booking window opens"
              value={form.exportBookingLeadHours}
              onChange={(v) =>
                setForm(
                  (f) =>
                    f && {
                      ...f,
                      exportBookingLeadHours: v === "" ? "" : Number(v),
                    },
                )
              }
              min={1}
              clampBehavior="none"
              allowDecimal={false}
            />
          ) : (
            <NumberInput
              label="Window lead (days)"
              description="How many days before departure the booking window starts"
              value={form.importWindowLeadDays}
              onChange={(v) =>
                setForm(
                  (f) =>
                    f && {
                      ...f,
                      importWindowLeadDays: v === "" ? "" : Number(v),
                    },
                )
              }
              min={0}
              clampBehavior="none"
              allowDecimal={false}
            />
          )}

          <Group justify="flex-end" mt="xs">
            <Button variant="default" onClick={onClose} disabled={save.isPending}>
              Cancel
            </Button>
            <Button onClick={handleSave} loading={save.isPending}>
              Save settings
            </Button>
          </Group>
        </Stack>
      )}
    </Modal>
  );
}
