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

import DurationField from "@/components/trainScheduling/DurationField";
import { trainSchedulingService } from "@/services/trainScheduling.service";
import type { CreateScheduleWindowRulePayload } from "@/types/trainScheduling";

/** Fallbacks matching the API's global-rules defaults (used if the fetch fails). */
const DEFAULTS = {
  windowOpenHour: 8,
  // Equal to open ⇒ 24-hour desk (the default).
  windowCloseHour: 8,
  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`,
}));

export interface WindowFormState {
  windowOpenHour: number;
  windowCloseHour: number;
  windowDurationHours: number | "";
  docReviewMinutes: number | "";
  paymentWindowMinutes: number | "";
  importWindowLeadDays: number | "";
  exportBookingLeadHours: number | "";
  /** Blank = close exactly at departure. */
  closeOffsetMinutes: number | "";
}

/**
 * Builds the create payload from form state, or returns an error message when a
 * required field was left blank. The close offset is direction-scoped: only the
 * offset matching this schedule's direction is sent, since the other is never read.
 */
export function buildWindowRulePayload(
  form: WindowFormState,
  isExport: boolean,
): { payload: CreateScheduleWindowRulePayload } | { error: string } {
  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
  ) {
    return { error: "Fill every booking-window field, or turn the toggle off" };
  }

  // Blank offset = close at departure. Sent as null (not omitted) so it wins
  // over a non-null global offset.
  const offset = form.closeOffsetMinutes === "" ? null : Number(form.closeOffsetMinutes);

  return {
    payload: {
      windowOpenHour: form.windowOpenHour,
      windowCloseHour: form.windowCloseHour,
      windowDurationHours: duration,
      docReviewMinutes: doc,
      paymentWindowMinutes: pay,
      ...(isExport
        ? { exportBookingLeadHours: exportLead, exportCloseOffsetMinutes: offset }
        : { importWindowLeadDays: lead, importCloseOffsetMinutes: offset }),
    },
  };
}

export interface CreateScheduleWindowFieldsProps {
  /** Direction of the selected route — picks lead/offset semantics. */
  isExport: boolean;
  form: WindowFormState | null;
  onChange: (next: WindowFormState) => void;
}

/**
 * Booking-window settings for a schedule being created. Prefills from the live
 * global rules (so the fields show what the schedule WOULD inherit), then lets
 * staff tune them for this one train. Mirrors BookingWindowSettingsModal, plus
 * the booking-close offset.
 */
export default function CreateScheduleWindowFields({
  isExport,
  form,
  onChange,
}: CreateScheduleWindowFieldsProps) {
  const rulesQuery = useQuery({
    queryKey: ["train-scheduling", "global-rules"],
    queryFn: () => trainSchedulingService.getGlobalRules(),
    staleTime: 5 * 60_000,
  });

  // Seed once from the global rules, so the toggle opens on the values this
  // schedule would otherwise inherit rather than on hardcoded guesses.
  const [seeded, setSeeded] = useState(false);
  useEffect(() => {
    if (seeded || form != null) return;
    const r = rulesQuery.data;
    if (!r && rulesQuery.isLoading) return;
    const num = (v: unknown, fallback: number) => {
      const n = v == null || v === "" ? NaN : Number(v);
      return Number.isFinite(n) ? n : fallback;
    };
    const offset = isExport
      ? (r as { exportCloseOffsetMinutes?: number | null } | undefined)
          ?.exportCloseOffsetMinutes
      : (r as { importCloseOffsetMinutes?: number | null } | undefined)
          ?.importCloseOffsetMinutes;
    onChange({
      windowOpenHour: num(r?.windowOpenHour, DEFAULTS.windowOpenHour),
      windowCloseHour: num(r?.windowCloseHour, DEFAULTS.windowCloseHour),
      windowDurationHours: num(r?.windowDurationHours, DEFAULTS.windowDurationHours),
      docReviewMinutes: num(r?.docReviewMinutes, DEFAULTS.docReviewMinutes),
      paymentWindowMinutes: num(
        isExport
          ? (r as { exportPaymentWindowMinutes?: number } | undefined)
              ?.exportPaymentWindowMinutes
          : r?.paymentWindowMinutes,
        DEFAULTS.paymentWindowMinutes,
      ),
      importWindowLeadDays: num(r?.importWindowLeadDays, DEFAULTS.importWindowLeadDays),
      exportBookingLeadHours: num(
        r?.exportBookingLeadHours,
        DEFAULTS.exportBookingLeadHours,
      ),
      closeOffsetMinutes: offset == null || offset === 0 ? "" : Number(offset),
    });
    setSeeded(true);
  }, [seeded, form, rulesQuery.data, rulesQuery.isLoading, isExport, onChange]);

  const set = (patch: Partial<WindowFormState>) => {
    if (form) onChange({ ...form, ...patch });
  };

  const is24h = form != null && form.windowOpenHour === form.windowCloseHour;
  // Close < open is a valid OVERNIGHT desk (e.g. 08:00 → 07:00 next morning).
  const isOvernight = form != null && form.windowCloseHour < form.windowOpenHour;

  const reopenSummary = useMemo(() => {
    if (!form) return "";
    const total = (Number(form.docReviewMinutes) || 0) + (Number(form.paymentWindowMinutes) || 0);
    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]);

  if (!form) {
    return (
      <Group justify="center" py="md">
        <Loader size="sm" />
      </Group>
    );
  }

  return (
    <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 it closes. Cycle timing below
          doesn&apos;t apply.
        </Alert>
      ) : (
        <Alert variant="light" color="orange" icon={<Info size={16} />}>
          These settings apply to this train only, and can be set only for the FIRST
          train on a route and departure day. Later trains that day join its booking
          group and share the same window.
        </Alert>
      )}

      {/* ── 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 && set({ windowOpenHour: Number(v) })}
            allowDeselect={false}
            comboboxProps={{ withinPortal: true }}
          />
          <Select
            label="Closes"
            data={HOUR_OPTIONS}
            value={String(form.windowCloseHour)}
            onChange={(v) => v != null && set({ 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) =>
            set({
              // On → close == open (24h desk). Off → restore a normal ~9h day.
              windowCloseHour: e.currentTarget.checked
                ? form.windowOpenHour
                : Math.min(23, form.windowOpenHour + 9),
            })
          }
        />
      </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) => set({ 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) => set({ 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) => set({ paymentWindowMinutes: v })}
              min={1}
            />
          </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) => set({ exportBookingLeadHours: v === "" ? "" : Number(v) })}
          min={1}
          clampBehavior="none"
          allowNegative={false}
          allowDecimal={false}
        />
      ) : (
        <NumberInput
          label="Window lead (days)"
          description="How many days before departure the booking window starts"
          value={form.importWindowLeadDays}
          onChange={(v) => set({ importWindowLeadDays: v === "" ? "" : Number(v) })}
          min={0}
          clampBehavior="none"
          allowNegative={false}
          allowDecimal={false}
        />
      )}

      <Divider />

      {/* ── Booking close offset ─────────────────────────────────────── */}
      <Box>
        <Text size="sm" fw={600} mb={2}>
          Booking close offset
        </Text>
        <Text size="xs" c="dimmed" mb={8}>
          How long before departure this schedule stops accepting bookings. e.g. a
          3-hour import offset closes a 17:00 departure&apos;s window at 14:00; a 1-day
          export offset closes a Jul-10 16:00 departure at Jul-9 16:00. Leave blank to
          close exactly at departure.
        </Text>
        <DurationField
          label={isExport ? "Export close offset" : "Import close offset"}
          description={
            isExport
              ? "This export booking window closes this long before departure (blank = at departure)"
              : "This import booking window closes this long before departure (blank = at departure)"
          }
          value={form.closeOffsetMinutes}
          nativeUnit="minutes"
          onChange={(v) => set({ closeOffsetMinutes: v })}
          min={0}
        />
      </Box>
    </Stack>
  );
}
