import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
  Accordion,
  ActionIcon,
  Alert,
  Badge,
  Box,
  Button,
  Group,
  Loader,
  Paper,
  Stack,
  Tabs,
  Text,
  ThemeIcon,
  Title,
  Tooltip,
} from "@mantine/core";
import {
  AlertTriangle,
  ArrowLeft,
  ArrowLeftRight,
  Boxes,
  CalendarDays,
  CheckCircle2,
  ChevronLeft,
  ClipboardCheck,
  ChevronRight,
  Clock,
  FileSignature,
  Hourglass,
  Layers,
  Package,
  PlayCircle,
  RefreshCw,
  Ruler,
  TrainFront,
  Weight,
  XCircle,
} from "lucide-react";

import { DataTable, type ColumnDef } from "@edr/ui-common";

import { KpiStrip, PageContainer } from "@/components/page";
import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
import Breadcrumbs from "@/components/ui/Breadcrumbs";
import { TrainCompositionDiagram } from "@/components/trainScheduling/TrainCompositionDiagram";
import {
  TrainConsistView,
  CompositionBookingTabs,
} from "@/components/trainScheduling/compositionEditor";
import {
  BookingPipeline,
  HeroChip,
  totalBookingCount,
  WindowPhasePill,
  WindowStatusPill,
} from "@/components/trainScheduling/batchVisuals";
import { RouteCorridor } from "@/components/trainScheduling/scheduleVisuals";
import { BookingsManager } from "./BookingsManager";
import { useMutation, useQuery } from "@tanstack/react-query";
import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import type {
  BatchBoardBookingDetail,
  BatchBoardBookingState,
  BatchBoardScheduleDetail,
  BatchWindowGroup,
  BookingAllocationStatus,
} from "@/types/trainScheduling";

const STATE_META: Record<
  BatchBoardBookingState,
  { label: string; color: string; icon: typeof CheckCircle2 }
> = {
  ALLOCATED: { label: "Allocated", color: "edr-green", icon: CheckCircle2 },
  SELECTED_FOR_BATCH: {
    label: "Selected for batch",
    color: "orange",
    icon: Clock,
  },
  READY: { label: "Ready for batch", color: "teal", icon: Hourglass },
  WAITING: { label: "Paid · waiting", color: "blue", icon: Hourglass },
  PENDING_CONTRACT: {
    label: "Pending contract",
    color: "gray",
    icon: Hourglass,
  },
  EXPIRED: { label: "Expired", color: "red", icon: XCircle },
};

const ALLOC_META: Record<
  BookingAllocationStatus,
  { label: string; color: string }
> = {
  ASSIGNED: { label: "Wagons assigned", color: "edr-green" },
  NOT_ATTEMPTED: { label: "Not allocated", color: "gray" },
  DEFERRED: { label: "Deferred", color: "orange" },
  FAILED: { label: "Allocation failed", color: "red" },
};

const fmtTons = (n: number) =>
  `${n.toLocaleString(undefined, { maximumFractionDigits: 1 })} t`;

const fmtMeters = (n: number) =>
  `${n.toLocaleString(undefined, { maximumFractionDigits: 1 })} m`;

const fmtDateTime = (iso: string | null) =>
  iso
    ? new Intl.DateTimeFormat("en-GB", {
      day: "2-digit",
      month: "short",
      hour: "2-digit",
      minute: "2-digit",
      hour12: false,
      timeZone: "Africa/Addis_Ababa",
    }).format(new Date(iso))
    : "—";

const eatDayFmt = new Intl.DateTimeFormat("en-CA", {
  timeZone: "Africa/Addis_Ababa",
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
});

/** "11:00 EAT" if the timestamp falls on today (EAT), else "05 Jun, 11:00 EAT". */
const fmtPhaseTime = (iso: string) => {
  const date = new Date(iso);
  const time = new Intl.DateTimeFormat("en-GB", {
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
    timeZone: "Africa/Addis_Ababa",
  }).format(date);
  if (eatDayFmt.format(date) === eatDayFmt.format(new Date())) {
    return `${time} EAT`;
  }
  const day = new Intl.DateTimeFormat("en-GB", {
    day: "2-digit",
    month: "short",
    timeZone: "Africa/Addis_Ababa",
  }).format(date);
  return `${day}, ${time} EAT`;
};

/** Countdown label for the current booking-cycle phase, e.g. "Closes 11:00 EAT". */
function phaseCountdown(data: BatchBoardScheduleDetail): string | null {
  switch (data.windowPhase) {
    case "PRE_WINDOW":
      return data.windowOpensAt
        ? `Opens ${fmtPhaseTime(data.windowOpensAt)}`
        : null;
    case "OPEN":
      return data.windowClosesAt
        ? `Closes ${fmtPhaseTime(data.windowClosesAt)}`
        : null;
    case "DOC_REVIEW":
      return data.docReviewEndsAt
        ? `Doc review ends ${fmtPhaseTime(data.docReviewEndsAt)}`
        : null;
    case "PAYMENT":
      return data.paymentPhaseEndsAt
        ? `Payment ends ${fmtPhaseTime(data.paymentPhaseEndsAt)}`
        : null;
    case "CLOSED_FOR_DAY":
      return data.windowOpensAt
        ? `Reopens ${fmtPhaseTime(data.windowOpensAt)}`
        : null;
    default:
      return null;
  }
}

const initials = (name: string) =>
  name
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((w) => w[0])
    .join("")
    .toUpperCase() || "?";

function StateBadge({ state }: { state: BatchBoardBookingState }) {
  const meta = STATE_META[state];
  const Icon = meta.icon;
  return (
    <Badge
      variant="light"
      color={meta.color}
      radius="sm"
      leftSection={<Icon size={11} />}
    >
      {meta.label}
    </Badge>
  );
}

function AllocationBadge({
  status,
  issue,
}: {
  status: BookingAllocationStatus;
  issue: string | null;
}) {
  const meta = ALLOC_META[status];
  const badge = (
    <Badge variant="light" color={meta.color} radius="sm">
      {meta.label}
    </Badge>
  );
  if (!issue) return badge;
  return (
    <Tooltip label={issue} multiline maw={320} withArrow>
      <Group gap={4} wrap="nowrap">
        {badge}
        <AlertTriangle size={14} color="var(--mantine-color-red-6)" />
      </Group>
    </Tooltip>
  );
}

const bookingCellMeta = {
  headerClassName: ruleEngineTable.headerCell,
  cellClassName: ruleEngineTable.bodyCell,
};

const BOOKING_COLUMNS: ColumnDef<BatchBoardBookingDetail>[] = [
  {
    id: "reference",
    header: "Reference",
    meta: bookingCellMeta,
    cell: ({ row }) => {
      const b = row.original;
      return (
        <Group gap={6} wrap="nowrap">
          <Text size="sm" fw={700} c="dark.5">
            {b.reference}
          </Text>
          {b.isGovernment ? (
            <Badge size="xs" variant="light" color="grape">
              Gov
            </Badge>
          ) : null}
          {b.consolidationPartnerRef ? (
            <Tooltip
              label={`Consolidated — shares one wagon with ${b.consolidationPartnerRef}`}
              withArrow
              multiline
              maw={260}
            >
              <Badge
                size="xs"
                variant="light"
                color="edr-green"
                radius="sm"
                leftSection={<ArrowLeftRight size={10} />}
                style={{ textTransform: "none" }}
              >
                shared wagon · {b.consolidationPartnerRef}
              </Badge>
            </Tooltip>
          ) : null}
        </Group>
      );
    },
  },
  {
    id: "customer",
    header: "Customer",
    meta: bookingCellMeta,
    cell: ({ row }) => {
      const b = row.original;
      return (
        <Group gap={8} wrap="nowrap">
          <Box
            style={{
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              width: 26,
              height: 26,
              borderRadius: 8,
              flexShrink: 0,
              background: "#FEF1D5",
              border: "1px solid #FBD171",
            }}
          >
            <Text size="10px" fw={800} style={{ color: "#B26C09" }}>
              {initials(b.company)}
            </Text>
          </Box>
          <Text size="sm" c="gray.7" truncate>
            {b.company}
          </Text>
        </Group>
      );
    },
  },
  {
    id: "contractSigned",
    header: "Contract signed",
    meta: bookingCellMeta,
    cell: ({ row }) => (
      <Text size="sm" style={{ whiteSpace: "nowrap" }}>
        {fmtDateTime(row.original.fullyExecutedAt)} EAT
      </Text>
    ),
  },
  {
    id: "selectedForBatch",
    header: "Selected for batch",
    meta: bookingCellMeta,
    cell: ({ row }) => {
      const b = row.original;
      if (!b.selectedForBatchAt) {
        return (
          <Text size="sm" c="dimmed">
            —
          </Text>
        );
      }
      return (
        <>
          <Text size="sm" style={{ whiteSpace: "nowrap" }}>
            {fmtDateTime(b.selectedForBatchAt)} EAT
          </Text>
          {b.paymentDeadline ? (
            <Text
              size="xs"
              c="orange.7"
              fw={600}
              style={{ whiteSpace: "nowrap" }}
            >
              Pay by {fmtDateTime(b.paymentDeadline)} EAT
            </Text>
          ) : null}
        </>
      );
    },
  },
  {
    id: "capacity",
    header: "Capacity",
    meta: bookingCellMeta,
    cell: ({ row }) => {
      const b = row.original;
      return (
        <Group gap={4} wrap="nowrap">
          <Badge variant="default" radius="sm" size="sm">
            {b.wagons}w
          </Badge>
          <Badge variant="default" radius="sm" size="sm">
            {fmtTons(b.weightTons)}
          </Badge>
        </Group>
      );
    },
  },
  {
    id: "state",
    header: "Batch state",
    meta: bookingCellMeta,
    cell: ({ row }) => <StateBadge state={row.original.state} />,
  },
  {
    id: "allocation",
    header: "Wagon allocation",
    meta: bookingCellMeta,
    cell: ({ row }) => (
      <AllocationBadge
        status={row.original.allocationStatus}
        issue={row.original.allocationIssue}
      />
    ),
  },
];

function BookingTable({ bookings }: { bookings: BatchBoardBookingDetail[] }) {
  return (
    <DataTable
      columns={BOOKING_COLUMNS}
      data={bookings}
      status="success"
      emptyMessage="No bookings in this batch window."
      containerClassName="overflow-x-auto rounded-lg border border-edr-border"
    />
  );
}

function WindowCountChips({ counts }: { counts: BatchWindowGroup["counts"] }) {
  const chips: Array<{ value: number; color: string; label: string }> = [
    { value: counts.allocated, color: "edr-green", label: "allocated" },
    { value: counts.selectedForBatch, color: "orange", label: "selected" },
    { value: counts.ready, color: "teal", label: "ready" },
    { value: counts.waiting, color: "blue", label: "waiting" },
    { value: counts.expired, color: "red", label: "expired" },
  ].filter((c) => c.value > 0);

  return (
    <Group gap={5} wrap="nowrap">
      {chips.map((c) => (
        <Tooltip key={c.label} label={`${c.value} ${c.label}`} withArrow>
          <Group
            gap={4}
            wrap="nowrap"
            style={{
              padding: "2px 8px",
              borderRadius: 999,
              background: `var(--mantine-color-${c.color}-0)`,
              border: `1px solid var(--mantine-color-${c.color}-2)`,
            }}
          >
            <Box
              w={6}
              h={6}
              style={{
                borderRadius: 999,
                background: `var(--mantine-color-${c.color}-6)`,
              }}
            />
            <Text size="xs" fw={700} c={`${c.color}.8`}>
              {c.value}
            </Text>
          </Group>
        </Tooltip>
      ))}
    </Group>
  );
}

/** "05 Jun 2026 · 06:00 – 09:00 EAT" → "06:00 – 09:00 EAT" (date lives in the day header). */
function timeLabelOf(label: string): string {
  const idx = label.indexOf("·");
  return idx >= 0 ? label.slice(idx + 1).trim() : label;
}

const EAT_TZ = "Africa/Addis_Ababa";
const dateKeyFmt = new Intl.DateTimeFormat("en-CA", {
  timeZone: EAT_TZ,
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
});
const dateLabelFmt = new Intl.DateTimeFormat("en-GB", {
  timeZone: EAT_TZ,
  weekday: "short",
  day: "2-digit",
  month: "short",
});

/** EAT calendar date key for a window — prefers the API field, falls back to `start`. */
function windowDateKey(w: BatchWindowGroup): string {
  if (w.date) return w.date;
  if (w.start) return dateKeyFmt.format(new Date(w.start));
  return "undated";
}

/** Human day label for a window — prefers the API field, falls back to `start`. */
function windowDateLabel(w: BatchWindowGroup): string {
  if (w.dateLabel) return w.dateLabel;
  if (w.start) return dateLabelFmt.format(new Date(w.start));
  return "Undated";
}

function WindowAccordionItem({ window }: { window: BatchWindowGroup }) {
  const total = window.bookings.length;
  const hasIssues = window.bookings.some(
    (b) => b.allocationStatus === "FAILED" || b.allocationStatus === "DEFERRED",
  );

  return (
    <Accordion.Item value={window.key}>
      <Accordion.Control>
        <Group justify="space-between" wrap="nowrap" pr="md" gap="sm">
          <Group gap="sm" wrap="nowrap" style={{ minWidth: 0 }}>
            <Box
              style={{
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: 34,
                height: 34,
                borderRadius: 10,
                flexShrink: 0,
                background: total ? "#FEF1D5" : "var(--mantine-color-gray-0)",
                border: total
                  ? "1px solid #FBD171"
                  : "1px solid var(--mantine-color-gray-2)",
                color: total ? "#B26C09" : "var(--mantine-color-gray-5)",
              }}
            >
              <Clock size={16} />
            </Box>
            <Box style={{ minWidth: 0 }}>
              <Text fw={700} size="sm" truncate>
                {timeLabelOf(window.label)}
              </Text>
              <Text size="xs" c="dimmed">
                {total
                  ? `${total} booking${total === 1 ? "" : "s"}`
                  : "Empty window"}
              </Text>
            </Box>
          </Group>
          <Group gap={6} wrap="nowrap">
            {hasIssues ? (
              <Badge
                variant="light"
                color="red"
                size="sm"
                leftSection={<AlertTriangle size={10} />}
              >
                Issues
              </Badge>
            ) : null}
            <WindowCountChips counts={window.counts} />
          </Group>
        </Group>
      </Accordion.Control>
      <Accordion.Panel>
        <BookingTable bookings={window.bookings} />
      </Accordion.Panel>
    </Accordion.Item>
  );
}

export default function BatchScheduleDetailPage() {
  const { scheduleId } = useParams<{ scheduleId: string }>();
  const navigate = useNavigate();
  const { toast } = useToast();
  const { data, isLoading, isFetching, refetch } = useQuery(
    api.trainScheduling.batchBoardDetail.queryOptions({
      input: { scheduleId: scheduleId ?? "" },
      enabled: Boolean(scheduleId),
      refetchInterval: 30_000,
    }),
  );
  const runAllocation = useMutation(
    api.trainScheduling.runAllocation.mutationOptions(),
  );
  const completeDocReview = useMutation(
    api.trainScheduling.completeDocReview.mutationOptions(),
  );

  const hasAssignedWagons = useMemo(
    () =>
      Boolean(
        data?.windows.some((w) =>
          w.bookings.some((b) => b.allocationStatus === "ASSIGNED"),
        ) ||
        data?.pendingContract.bookings.some(
          (b) => b.allocationStatus === "ASSIGNED",
        ),
      ),
    [data],
  );

  const scheduleDetailQuery = useQuery(
    api.trainScheduling.scheduleDetail.queryOptions({
      input: { id: scheduleId ?? "", freightType: "CONTAINER" },
      enabled: Boolean(scheduleId),
    }),
  );

  // Every booking on this schedule, flattened across windows + pending-contract,
  // de-duplicated (a booking only appears once). Feeds the management table.
  const allBookings = useMemo(() => {
    if (!data) return [] as BatchBoardBookingDetail[];
    const merged = [
      ...data.windows.flatMap((w) => w.bookings),
      ...data.pendingContract.bookings,
    ];
    const byId = new Map<string, BatchBoardBookingDetail>();
    for (const b of merged) if (!byId.has(b.id)) byId.set(b.id, b);
    return [...byId.values()];
  }, [data]);

  // Batch bookings by state for the composition side panel (payment / expired lists).
  const batchBookings = useMemo(() => {
    const all = allBookings;
    return {
      awaitingPayment: all.filter((b) => b.state === "SELECTED_FOR_BATCH"),
      expired: all.filter((b) => b.state === "EXPIRED"),
    };
  }, [allBookings]);

  const bookingsReadOnly = useMemo(
    () => ["DISPATCHED", "ARRIVED"].includes(data?.status ?? ""),
    [data?.status],
  );

  // Group the flat window list into per-day sections (one per EAT calendar date).
  const dayGroups = useMemo(() => {
    if (!data) return [];
    const byDate = new Map<
      string,
      {
        date: string;
        dateLabel: string;
        windows: BatchWindowGroup[];
        totalBookings: number;
        counts: BatchWindowGroup["counts"];
        hasIssues: boolean;
      }
    >();
    for (const w of data.windows) {
      const dateKey = windowDateKey(w);
      let group = byDate.get(dateKey);
      if (!group) {
        group = {
          date: dateKey,
          dateLabel: windowDateLabel(w),
          windows: [],
          totalBookings: 0,
          counts: {
            allocated: 0,
            selectedForBatch: 0,
            ready: 0,
            waiting: 0,
            expired: 0,
            pendingContract: 0,
          },
          hasIssues: false,
        };
        byDate.set(dateKey, group);
      }
      group.windows.push(w);
      group.totalBookings += w.bookings.length;
      group.counts.allocated += w.counts.allocated;
      group.counts.selectedForBatch += w.counts.selectedForBatch;
      group.counts.ready += w.counts.ready;
      group.counts.waiting += w.counts.waiting;
      group.counts.expired += w.counts.expired;
      group.counts.pendingContract += w.counts.pendingContract;
      group.hasIssues =
        group.hasIssues ||
        w.bookings.some(
          (b) =>
            b.allocationStatus === "FAILED" ||
            b.allocationStatus === "DEFERRED",
        );
    }
    return [...byDate.values()];
  }, [data]);

  // Windows with bookings open by default (inside an expanded day).
  const openWindowKeys = useMemo(
    () =>
      data
        ? data.windows.filter((w) => w.bookings.length > 0).map((w) => w.key)
        : [],
    [data],
  );

  const todayEat = useMemo(
    () =>
      new Intl.DateTimeFormat("en-CA", {
        timeZone: "Africa/Addis_Ababa",
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
      }).format(new Date()),
    [],
  );

  // Date-stepper: which day is currently shown. Default to today, else the first
  // day with bookings, else the first day. Keep the selection if still valid.
  const [selectedDate, setSelectedDate] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<string | null>("overview");
  const [selectedBookingId, setSelectedBookingId] = useState<string | null>(
    null,
  );
  useEffect(() => {
    if (!dayGroups.length) return;
    if (selectedDate && dayGroups.some((d) => d.date === selectedDate)) return;
    const preferred =
      dayGroups.find((d) => d.date === todayEat) ??
      dayGroups.find((d) => d.totalBookings > 0) ??
      dayGroups[0];
    setSelectedDate(preferred.date);
  }, [dayGroups, selectedDate, todayEat]);

  const selectedIndex = Math.max(
    0,
    dayGroups.findIndex((d) => d.date === selectedDate),
  );
  const selectedDay = dayGroups[selectedIndex];

  const handleCompleteDocReview = () => {
    completeDocReview
      .mutateAsync(scheduleId ?? "")
      .then(() => {
        toast({
          title: "Document review complete",
          description: "Batch is running for this route-day group",
        });
        void refetch();
      })
      .catch(() => {
        toast({
          title: "Could not complete document review",
          variant: "destructive",
        });
      });
  };

  const handleRunAllocation = () => {
    runAllocation
      .mutateAsync({ scheduleId: scheduleId ?? "" })
      .then((result) => {
        const failed = result.issues.filter(
          (i) => i.status === "FAILED",
        ).length;
        const deferred = result.deferred.length;
        toast({
          title: "Allocation run complete",
          description:
            failed || deferred
              ? `${result.assignedBookingIds.length} assigned · ${deferred} deferred · ${failed} failed`
              : `${result.assignedBookingIds.length} booking(s) assigned to wagons`,
          variant: failed ? "destructive" : "default",
        });
        void refetch();
      })
      .catch(() => {
        toast({ title: "Allocation failed", variant: "destructive" });
      });
  };

  if (isLoading || !data) {
    return (
      <PageContainer fluid>
        <Group justify="center" py="xl">
          <Loader color="edr-green" />
        </Group>
      </PageContainer>
    );
  }

  const totalBookings = totalBookingCount(data.counts);
  const countdown = phaseCountdown(data);

  return (
    <PageContainer fluid>
      <Breadcrumbs
        items={[
          { label: "Operations" },
          { label: "Batch board", href: "/dashboard/operations/batch-board" },
          { label: data.trainNumber ?? data.routeName ?? "Schedule" },
        ]}
      />

      <Tabs value={activeTab} onChange={setActiveTab}>
        <Tabs.List>
          <Tabs.Tab value="overview">Overview</Tabs.Tab>
          <Tabs.Tab value="composition">
            Train Composition{" "}
            {scheduleDetailQuery.data?.trainSet?.wagons &&
              scheduleDetailQuery.data.trainSet.wagons.length > 0 &&
              `(${scheduleDetailQuery.data.trainSet.wagons.length})`}
          </Tabs.Tab>
        </Tabs.List>

        <Tabs.Panel value="overview" pt="lg">
          <Stack gap="lg">
            <Group
              justify="space-between"
              align="flex-start"
              wrap="wrap"
              gap="md"
            >
              <Stack gap={9} style={{ minWidth: 0 }}>
                <Group gap="sm" wrap="wrap" align="center">
                  <Button
                    variant="subtle"
                    color="gray"
                    radius="md"
                    px="sm"
                    leftSection={<ArrowLeft size={16} />}
                    onClick={() =>
                      navigate("/dashboard/operations/batch-board")
                    }
                  >
                    Back
                  </Button>
                  <Title order={2} fw={800}>
                    {data.trainNumber ?? data.routeName ?? "Schedule"}
                  </Title>
                  <WindowStatusPill status={data.bookingWindowStatus} />
                  {data.windowPhase ? (
                    <WindowPhasePill
                      phase={data.windowPhase}
                      cycleNo={data.bookingCycleNo}
                    />
                  ) : null}
                  <HeroChip>{data.status}</HeroChip>
                </Group>
                <RouteCorridor
                  origin={data.origin}
                  destination={data.destination}
                />
                <Group gap={6} wrap="wrap">
                  <HeroChip icon={<CalendarDays size={12} />}>
                    {data.scheduleDate
                      ? new Intl.DateTimeFormat("en-GB", {
                        weekday: "short",
                        day: "2-digit",
                        month: "short",
                        year: "numeric",
                        hour: "2-digit",
                        minute: "2-digit",
                        hour12: false,
                        timeZone: "Africa/Addis_Ababa",
                      }).format(new Date(data.scheduleDate)) + " EAT"
                      : "No date"}
                  </HeroChip>
                  {data.locomotive ? (
                    <HeroChip icon={<TrainFront size={12} />}>
                      Loco {data.locomotive.code} ·{" "}
                      {fmtTons(data.locomotive.maxPullWeightTons)} ·{" "}
                      {data.locomotive.maxTrainLengthMeters} m
                    </HeroChip>
                  ) : null}
                  {data.windowPhase ? (
                    <HeroChip icon={<Clock size={12} />}>
                      Cycle {data.bookingCycleNo}
                      {countdown ? ` · ${countdown}` : ""}
                    </HeroChip>
                  ) : null}
                </Group>
              </Stack>

              <Group gap="sm">
                <Button
                  variant="default"
                  radius="md"
                  leftSection={<RefreshCw size={16} />}
                  loading={isFetching}
                  onClick={() => void refetch()}
                >
                  Refresh
                </Button>
                {data.windowPhase === "DOC_REVIEW" ? (
                  <Button
                    color="yellow"
                    radius="md"
                    leftSection={<ClipboardCheck size={16} />}
                    loading={completeDocReview.isPending}
                    onClick={handleCompleteDocReview}
                  >
                    Doc review complete — run batch
                  </Button>
                ) : null}
                <Button
                  color="edr-green"
                  radius="md"
                  leftSection={<PlayCircle size={16} />}
                  loading={runAllocation.isPending}
                  onClick={handleRunAllocation}
                >
                  Run allocation
                </Button>
                <Button
                  variant="light"
                  color="edr-green"
                  radius="md"
                  leftSection={<Layers size={16} />}
                  onClick={() =>
                    navigate(
                      `/dashboard/operations/train-scheduling-v2/${data.scheduleId}`,
                    )
                  }
                >
                  Open schedule
                </Button>
              </Group>
            </Group>

            {!data.locomotive ? (
              <Alert color="red" radius="md" icon={<AlertTriangle size={16} />}>
                No locomotive assigned — wagon allocation cannot run.
              </Alert>
            ) : null}

            <KpiStrip
              items={[
                {
                  label: "Allocated wagons",
                  value: data.capacity.allocatedWagons,
                  hint: "on this train",
                  icon: Boxes,
                },
                {
                  label: "Train length",
                  value: data.capacity.maxLengthMeters
                    ? `${fmtMeters(data.capacity.allocatedLengthMeters)} / ${fmtMeters(data.capacity.maxLengthMeters)}`
                    : fmtMeters(data.capacity.allocatedLengthMeters),
                  icon: Ruler,
                },
                {
                  label: "Weight",
                  value: data.capacity.maxWeightTons
                    ? `${fmtTons(data.capacity.usedWeightTons)} / ${fmtTons(data.capacity.maxWeightTons)}`
                    : fmtTons(data.capacity.usedWeightTons),
                  icon: Weight,
                },
                {
                  label: "Bookings",
                  value: totalBookings,
                  hint: `${data.counts.allocated} allocated · ${data.counts.expired} expired`,
                  icon: Package,
                },
              ]}
            />

            {/* Booking pipeline */}
            <Paper
              radius="lg"
              withBorder
              p="lg"
              mt="lg"
              style={{ borderColor: "var(--mantine-color-gray-2)" }}
            >
              <Group justify="space-between" mb="sm">
                <Group gap={8} wrap="nowrap">
                  <ThemeIcon
                    size={32}
                    radius="md"
                    variant="light"
                    color="#F2A516"
                  >
                    <Package size={16} />
                  </ThemeIcon>
                  <Text fw={700}>Booking pipeline</Text>
                </Group>
                <Text size="sm" fw={700} c="dark.4">
                  {totalBookings} booking{totalBookings === 1 ? "" : "s"}
                </Text>
              </Group>
              <BookingPipeline counts={data.counts} size={14} />
            </Paper>

            {data.allocationViolations.length ? (
              <Alert
                color="red"
                mt="lg"
                radius="lg"
                icon={<AlertTriangle size={16} />}
                title="Allocation constraints"
              >
                <Stack gap={4}>
                  {data.allocationViolations.map((v) => (
                    <Text key={v} size="sm">
                      {v}
                    </Text>
                  ))}
                </Stack>
              </Alert>
            ) : null}

            {/* Manage bookings — search, filter, remove / re-assign (bulk too) */}
            <Paper
              radius="lg"
              withBorder
              p="lg"
              mt="lg"
              style={{ borderColor: "var(--mantine-color-gray-2)" }}
            >
              <Group gap="sm" mb="md" wrap="nowrap" align="flex-start">
                <ThemeIcon
                  size={38}
                  radius="md"
                  variant="light"
                  color="#F2A516"
                >
                  <Package size={19} />
                </ThemeIcon>
                <Box>
                  <Title order={4}>Manage bookings</Title>
                  <Text size="sm" c="dimmed">
                    Search and filter every booking on this train. Remove an
                    allocated booking to free its wagons, or re-assign one that
                    is not yet allocated — individually or in bulk.
                  </Text>
                </Box>
              </Group>
              <BookingsManager
                scheduleId={scheduleId ?? ""}
                bookings={allBookings}
                onChanged={() => void refetch()}
                readOnly={bookingsReadOnly}
              />
            </Paper>

            {/* Batch windows */}
            <Paper
              radius="lg"
              withBorder
              p="lg"
              mt="lg"
              style={{ borderColor: "var(--mantine-color-gray-2)" }}
            >
              <Group gap="sm" mb={4} wrap="nowrap" align="flex-start">
                <ThemeIcon
                  size={38}
                  radius="md"
                  variant="light"
                  color="#F2A516"
                >
                  <Clock size={19} />
                </ThemeIcon>
                <Box>
                  <Title order={4}>Batch windows (EAT)</Title>
                  <Text size="sm" c="dimmed">
                    3-hour windows for every day from when the booking window
                    opened through the departure date. Bookings appear under the
                    date their contract was signed — open a day to see its
                    windows.
                  </Text>
                </Box>
              </Group>

              {dayGroups.length && selectedDay ? (
                <>
                  {/* Date stepper — page back/forward through each day in the range */}
                  <Group
                    justify="center"
                    align="center"
                    wrap="nowrap"
                    gap="md"
                    mt="md"
                  >
                    <ActionIcon
                      variant="light"
                      color="#F2A516"
                      size="xl"
                      radius="xl"
                      aria-label="Previous day"
                      disabled={selectedIndex <= 0}
                      onClick={() =>
                        setSelectedDate(
                          dayGroups[selectedIndex - 1]?.date ?? null,
                        )
                      }
                    >
                      <ChevronLeft size={20} />
                    </ActionIcon>

                    <Paper
                      withBorder
                      radius="xl"
                      px="xl"
                      py="xs"
                      style={{
                        flex: 1,
                        maxWidth: 360,
                        textAlign: "center",
                        background: selectedDay.totalBookings
                          ? "#FEF1D5"
                          : "white",
                        borderColor: selectedDay.totalBookings
                          ? "#FBD171"
                          : "var(--mantine-color-gray-2)",
                      }}
                    >
                      <Group justify="center" gap={8} wrap="nowrap">
                        <CalendarDays size={15} color="#B26C09" />
                        <Text
                          fw={800}
                          style={{
                            color: selectedDay.totalBookings
                              ? "#8A5304"
                              : "#0f172a",
                          }}
                        >
                          {selectedDay.dateLabel}
                        </Text>
                        {selectedDay.date === todayEat ? (
                          <Badge size="xs" variant="light" color="#F2A516">
                            Today
                          </Badge>
                        ) : null}
                      </Group>
                      <Text size="xs" c="dimmed" mt={2}>
                        {selectedDay.totalBookings
                          ? `${selectedDay.totalBookings} booking${selectedDay.totalBookings === 1 ? "" : "s"} · ${selectedDay.windows.length} windows`
                          : `${selectedDay.windows.length} windows · no bookings`}
                      </Text>
                    </Paper>

                    <ActionIcon
                      variant="light"
                      color="#F2A516"
                      size="xl"
                      radius="xl"
                      aria-label="Next day"
                      disabled={selectedIndex >= dayGroups.length - 1}
                      onClick={() =>
                        setSelectedDate(
                          dayGroups[selectedIndex + 1]?.date ?? null,
                        )
                      }
                    >
                      <ChevronRight size={20} />
                    </ActionIcon>
                  </Group>

                  <Group justify="space-between" align="center" mt="sm">
                    <Text size="xs" c="dimmed">
                      Day {selectedIndex + 1} of {dayGroups.length}
                    </Text>
                    <Group gap={6} wrap="nowrap">
                      {selectedDay.hasIssues ? (
                        <Badge
                          variant="light"
                          color="red"
                          size="sm"
                          leftSection={<AlertTriangle size={10} />}
                        >
                          Issues
                        </Badge>
                      ) : null}
                      <WindowCountChips counts={selectedDay.counts} />
                    </Group>
                  </Group>

                  <Accordion
                    key={selectedDay.date}
                    multiple
                    defaultValue={openWindowKeys}
                    variant="separated"
                    radius="md"
                    mt="md"
                    className="bb-window-accordion"
                  >
                    {selectedDay.windows.map((window) => (
                      <WindowAccordionItem key={window.key} window={window} />
                    ))}
                  </Accordion>
                </>
              ) : (
                <Text size="sm" c="dimmed" ta="center" py="lg">
                  No batch windows for this schedule.
                </Text>
              )}

              {data.pendingContract.bookings.length ? (
                <Accordion
                  multiple
                  defaultValue={["pending-contract"]}
                  variant="separated"
                  radius="md"
                  mt="md"
                  className="bb-window-accordion"
                >
                  <Accordion.Item value="pending-contract">
                    <Accordion.Control>
                      <Group
                        justify="space-between"
                        wrap="nowrap"
                        pr="md"
                        gap="sm"
                      >
                        <Group gap="sm" wrap="nowrap">
                          <Box
                            style={{
                              display: "flex",
                              alignItems: "center",
                              justifyContent: "center",
                              width: 34,
                              height: 34,
                              borderRadius: 10,
                              flexShrink: 0,
                              background: "var(--mantine-color-gray-0)",
                              border: "1px solid var(--mantine-color-gray-2)",
                              color: "var(--mantine-color-gray-6)",
                            }}
                          >
                            <FileSignature size={16} />
                          </Box>
                          <Box>
                            <Text fw={700} size="sm">
                              Pending contract
                            </Text>
                            <Text size="xs" c="dimmed">
                              Contract not signed yet — not in any window
                            </Text>
                          </Box>
                        </Group>
                        <Badge variant="outline" color="gray" size="sm">
                          {data.pendingContract.bookings.length} booking
                          {data.pendingContract.bookings.length === 1
                            ? ""
                            : "s"}
                        </Badge>
                      </Group>
                    </Accordion.Control>
                    <Accordion.Panel>
                      <BookingTable bookings={data.pendingContract.bookings} />
                    </Accordion.Panel>
                  </Accordion.Item>
                </Accordion>
              ) : null}
            </Paper>

            {/* Train composition diagram */}
            {hasAssignedWagons && scheduleDetailQuery.data ? (
              <Box mt="lg">
                <TrainCompositionDiagram
                  locomotive={scheduleDetailQuery.data.trainSet?.locomotive}
                  wagons={scheduleDetailQuery.data.trainSet?.wagons ?? []}
                  freightType="CONTAINER"
                  trainNumber={scheduleDetailQuery.data.trainNumber}
                  totalLengthMeters={
                    scheduleDetailQuery.data.trainSet?.totalLengthMeters
                  }
                />
              </Box>
            ) : null}
          </Stack>
        </Tabs.Panel>

        <Tabs.Panel value="composition" pt="lg">
          {scheduleDetailQuery.data && scheduleDetailQuery.data.trainSet ? (
            <Group align="stretch" gap="md" wrap="nowrap">
              <Box style={{ flex: 1, minWidth: 0 }}>
                <TrainConsistView
                  scheduleDetail={scheduleDetailQuery.data}
                  scheduleId={scheduleId ?? ""}
                  maxWagons={53}
                  highlightBookingId={selectedBookingId}
                />
              </Box>
              <Box style={{ width: 380, flexShrink: 0, minHeight: 520 }}>
                <CompositionBookingTabs
                  scheduleDetail={scheduleDetailQuery.data}
                  scheduleId={scheduleId ?? ""}
                  awaitingPayment={batchBookings.awaitingPayment}
                  expired={batchBookings.expired}
                  selectedBookingId={selectedBookingId}
                  onSelectBooking={setSelectedBookingId}
                />
              </Box>
            </Group>
          ) : (
            <Paper
              radius="lg"
              withBorder
              py={64}
              style={{ borderColor: "var(--mantine-color-gray-2)" }}
            >
              <Group justify="center">
                <Loader color="edr-green" size="sm" />
                <Text size="sm" c="dimmed">
                  Loading train composition…
                </Text>
              </Group>
            </Paper>
          )}
        </Tabs.Panel>
      </Tabs>
    </PageContainer>
  );
}
