import { Link, useParams } from "react-router-dom";
import { isAxiosError } from "axios";
import { useState } from "react";
import {
  ArrowLeft,
  CalendarClock,
  ChevronRight,
  FileText,
  Flag,
  ListChecks,
  MapPin,
  Package,
  PackageCheck,
  Pencil,
  Route,
  TrainFront,
} from "lucide-react";
import { Box, Button, Group, Loader, Menu, Stack, Text } from "@mantine/core";

import { CheckpointTimeModal } from "@/components/trainScheduling/CheckpointTimeModal";
import { CheckpointLogTable } from "@/components/trainScheduling/CheckpointLogTable";
import { JourneySpine } from "@/components/trainScheduling/JourneySpine";
import { LogPassYardWorkModal } from "@/components/trainScheduling/LogPassYardWorkModal";
import { TrackStatusCard } from "@/components/trainScheduling/TrackStatusCard";
import { Chip, SectionHead } from "@/components/trainScheduling/trackPrimitives";
import { track as T } from "@/components/trainScheduling/trackTheme";
import type {
  CheckpointHandlingTimes,
  TrackStation,
  TrainCheckpoint,
} from "@/types/trainScheduling";
import { useMutation, useQuery } from "@tanstack/react-query";
import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import { openPdfBlob } from "@/components/warehouses/pdf";
import { trainSchedulingService } from "@/services/trainScheduling.service";

type CheckpointModalValues = { occurredAt: string; note: string } & Required<
  Record<keyof CheckpointHandlingTimes, string | null>
>;

/**
 * The station-work stamps off the modal.
 *
 * On an edit a cleared picker means "remove this stamp", so nulls are sent.
 * On a first log there is nothing to remove, and the record endpoint takes no
 * nulls — the untouched pickers are dropped instead.
 */
const pickHandling = (
  values: CheckpointModalValues,
  keepNulls: boolean,
): CheckpointHandlingTimes =>
  Object.fromEntries(
    (
      [
        "unloadingStartedAt",
        "unloadingCompletedAt",
        "loadingStartedAt",
        "loadingCompletedAt",
      ] as const
    )
      .map((field) => [field, values[field]] as const)
      .filter(([, value]) => keepNulls || value !== null),
  );

const parseError = (error: unknown, fallback: string) => {
  if (isAxiosError(error)) {
    const data = error.response?.data as Record<string, unknown> | undefined;
    const message = data?.message;
    if (Array.isArray(message)) return message.join(", ");
    if (typeof message === "string") return message;
  }
  return fallback;
};

function formatDateTime(iso?: string | null) {
  if (!iso) return "—";
  return new Date(iso).toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

const CARD = {
  background: T.surface,
  border: `1px solid ${T.border}`,
  borderRadius: 16,
  overflow: "hidden" as const,
};

export default function TrainScheduleTrackPage() {
  const { scheduleId } = useParams<{ scheduleId: string }>();
  const { toast } = useToast();
  const trackQuery = useQuery(
    api.trainScheduling.trainTrack.queryOptions({
      input: { id: scheduleId ?? "" },
      enabled: Boolean(scheduleId),
    }),
  );
  const recordCheckpoint = useMutation(
    api.trainScheduling.recordCheckpoint.mutationOptions(),
  );
  const updateCheckpoint = useMutation(
    api.trainScheduling.updateCheckpoint.mutationOptions(),
  );
  // Time-entry dialogs: logging a pass at a yard with no work (the yard-work
  // modal carries its own picker), and correcting an already-logged leg.
  const [logModal, setLogModal] = useState<{
    station: TrackStation;
    isFinal: boolean;
  } | null>(null);
  const [editModal, setEditModal] = useState<TrainCheckpoint | null>(null);
  // Yard work drives the log-pass modal: which bookings board/alight per stop.
  const yardWorkQuery = useQuery(
    api.trainScheduling.yardWork.queryOptions({
      input: { scheduleId: scheduleId ?? "" },
      enabled: Boolean(scheduleId) && trackQuery.data?.status === "DISPATCHED",
    }),
  );
  // Marshalling: the on-board list, reprinted after station work. Numbered
  // per corridor stop that actually coupled/uncoupled something (Marshalling
  // 2, 3, 4…) — falls back to the single "current position" doc when nothing
  // has happened yet.
  const marshallingStopsQuery = useQuery(
    api.trainScheduling.marshallingStops.queryOptions({
      input: { id: scheduleId ?? "" },
      enabled:
        Boolean(scheduleId) &&
        ["DISPATCHED", "ARRIVED"].includes(trackQuery.data?.status ?? ""),
    }),
  );
  const marshallingStops = marshallingStopsQuery.data ?? [];
  const intercityMarshalling = useMutation({
    mutationFn: (stopIndex?: number) =>
      stopIndex != null
        ? trainSchedulingService.downloadMarshallingDocumentAt(scheduleId ?? "", stopIndex)
        : trainSchedulingService.downloadIntercityMarshallingDocument(scheduleId ?? ""),
  });
  const openIntercityMarshalling = async (stopIndex?: number) => {
    const pdfWindow = window.open("", "_blank");
    try {
      const blob = await intercityMarshalling.mutateAsync(stopIndex);
      const filename =
        stopIndex != null ? `marshalling-${stopIndex}-${scheduleId}.pdf` : `intercity-marshalling-${scheduleId}.pdf`;
      const opened = openPdfBlob(blob, filename, pdfWindow);
      toast({
        title: stopIndex != null ? `Marshalling ${stopIndex} ready` : "Intercity marshalling ready",
        description: opened
          ? "The PDF opened in a browser tab for printing or saving."
          : "The browser blocked the preview tab, so the PDF was downloaded.",
      });
    } catch (error) {
      pdfWindow?.close();
      toast({
        title: "Could not open intercity marshalling document",
        description: parseError(error, "Please try again"),
        variant: "destructive",
      });
    }
  };
  const [yardModal, setYardModal] = useState<{
    station: TrackStation;
    isFinal: boolean;
    alreadyLogged: boolean;
  } | null>(null);

  if (trackQuery.isLoading) {
    return (
      <Group justify="center" py="xl">
        <Loader size="sm" color="edr-green" />
      </Group>
    );
  }

  const track = trackQuery.data;
  if (!track || !scheduleId) {
    return (
      <Text c="dimmed" py="xl" px="lg">
        Tracking data not found.
      </Text>
    );
  }

  const canLog = track.status === "DISPATCHED";
  const totalStations = track.stations.length;
  const reached = Math.min(track.currentSequenceNo + 1, totalStations);
  const progressPct =
    totalStations > 1 ? (track.currentSequenceNo / (totalStations - 1)) * 100 : 0;
  const clampedPct = Math.min(100, Math.max(0, progressPct));
  const currentStation =
    track.stations[Math.max(0, track.currentSequenceNo)]?.label ?? "—";
  const inTransit = track.status === "DISPATCHED";
  const arrived = track.status === "ARRIVED";

  // Yard work at a station: boarders not yet loaded, and loaded bookings that
  // alight there. When either exists, logging the pass goes through the modal
  // so the operator sees (and can act on) both lists; empty yards log directly.
  const yardWorkFor = (station: TrackStation | undefined) =>
    yardWorkQuery.data?.yards.find((y) => y.yardId === station?.yardId);
  const stationHasWork = (station: TrackStation | undefined) => {
    const yard = yardWorkFor(station);
    return Boolean(
      yard &&
        (yard.toLoad.some((r) => !r.loadedAt) || yard.toUnload.some((r) => r.canUnload)),
    );
  };

  const handleLog = (sequenceNo: number) => {
    const station = track.stations.find((s) => s.sequenceNo === sequenceNo);
    if (!station) return;
    const isFinal = sequenceNo === track.stations[totalStations - 1]?.sequenceNo;
    if (stationHasWork(station)) {
      setYardModal({ station, isFinal, alreadyLogged: false });
      return;
    }
    setLogModal({ station, isFinal });
  };

  const submitLog = (values: CheckpointModalValues) => {
    if (!logModal) return;
    const { station, isFinal } = logModal;
    recordCheckpoint.mutate(
      {
        id: scheduleId,
        payload: {
          sequenceNo: station.sequenceNo,
          occurredAt: values.occurredAt,
          ...(values.note ? { note: values.note } : {}),
          // Nothing to clear on a first log — send only what was entered.
          ...pickHandling(values, false),
        },
      },
      {
        onSuccess: () => {
          setLogModal(null);
          toast({
            title: isFinal
              ? "Train arrived — assets freed, moved to destination yard"
              : "Checkpoint logged",
          });
        },
        onError: (err) =>
          toast({
            title: "Could not log checkpoint",
            description: parseError(err, "Please try again"),
            variant: "destructive",
          }),
      },
    );
  };

  const submitEdit = (values: CheckpointModalValues) => {
    if (!editModal) return;
    updateCheckpoint.mutate(
      {
        id: scheduleId,
        sequenceNo: editModal.sequenceNo,
        payload: {
          occurredAt: values.occurredAt,
          note: values.note || null,
          // Nulls are meaningful here: clearing a picker clears the stamp.
          ...pickHandling(values, true),
        },
      },
      {
        onSuccess: () => {
          setEditModal(null);
          toast({ title: "Checkpoint updated" });
        },
        onError: (err) =>
          toast({
            title: "Could not update checkpoint",
            description: parseError(err, "Please try again"),
            variant: "destructive",
          }),
      },
    );
  };
  // Legs stay correctable for as long as the journey exists — while rolling
  // and after arrival.
  const canEdit = track.status === "DISPATCHED" || track.status === "ARRIVED";

  // "Forgot to load" catch: while the train sits at the current station, any
  // boarder there that is still unloaded can be loaded until the next pass.
  const currentStationObj = track.stations.find(
    (s) => s.sequenceNo === track.currentSequenceNo,
  );
  const currentYard = canLog ? yardWorkFor(currentStationObj) : undefined;
  const forgottenBoarders =
    currentYard?.toLoad.filter((r) => !r.loadedAt) ?? [];

  // The stop the operator acts on next — drives the left rail's action card.
  const nextStation = canLog
    ? track.stations.find((s) => s.sequenceNo === track.currentSequenceNo + 1)
    : undefined;
  const nextIsFinal =
    nextStation?.sequenceNo === track.stations[totalStations - 1]?.sequenceNo;

  return (
    <Box style={{ background: T.bg, minHeight: "100%" }}>
      {/* ── Top bar ── */}
      <Group
        gap={14}
        px={36}
        py={16}
        wrap="nowrap"
        align="center"
        style={{ background: T.surface, borderBottom: `1px solid ${T.border}` }}
      >
        <Button
          component={Link}
          to={`/dashboard/operations/train-scheduling-v2/${scheduleId}`}
          variant="default"
          radius={9}
          size="compact-sm"
          leftSection={<ArrowLeft size={15} />}
        >
          Back to schedule
        </Button>
        <Group gap={8} align="center" wrap="nowrap" visibleFrom="sm">
          <Text size="12.5px" c={T.muted}>
            Train scheduling
          </Text>
          <ChevronRight size={13} color={T.text3} />
          <Text size="12.5px" fw={600} c={T.text}>
            {track.trainNumber ?? "Schedule"} · Tracking
          </Text>
        </Group>
        <Box style={{ flex: 1 }} />
        {(inTransit || arrived) && marshallingStops.length === 0 ? (
          <Button
            variant="default"
            radius={9}
            size="compact-sm"
            leftSection={<FileText size={15} color={T.brand} />}
            loading={intercityMarshalling.isPending}
            onClick={() => void openIntercityMarshalling()}
          >
            Intercity Marshalling
          </Button>
        ) : null}
        {(inTransit || arrived) && marshallingStops.length > 0 ? (
          <Menu position="bottom-end" withinPortal>
            <Menu.Target>
              <Button
                variant="default"
                radius={9}
                size="compact-sm"
                leftSection={<FileText size={15} color={T.brand} />}
                loading={intercityMarshalling.isPending}
              >
                Marshalling
              </Button>
            </Menu.Target>
            <Menu.Dropdown>
              {marshallingStops.map((stop) => (
                <Menu.Item
                  key={stop.stopIndex}
                  onClick={() => void openIntercityMarshalling(stop.stopIndex)}
                >
                  {`Marshalling ${stop.stopIndex} — ${stop.yardLabel}`}
                </Menu.Item>
              ))}
            </Menu.Dropdown>
          </Menu>
        ) : null}
      </Group>

      {/* ── Two-column work surface ── */}
      <Group
        align="flex-start"
        gap={28}
        px={36}
        pt={28}
        pb={56}
        wrap="wrap"
        style={{ width: "100%" }}
      >
        {/* left rail */}
        <Stack gap={16} style={{ width: 352, flexShrink: 0, flexGrow: 1, maxWidth: "100%" }}>
          <TrackStatusCard
            trainNumber={track.trainNumber}
            direction={track.direction}
            status={track.status}
            progressPct={clampedPct}
            reached={reached}
            totalStations={totalStations}
            currentStation={currentStation}
            stateLine={
              arrived
                ? "Journey complete"
                : inTransit
                  ? "En route"
                  : "Awaiting dispatch"
            }
            origin={track.origin}
            destination={track.destination}
            stats={[
              {
                icon: CalendarClock,
                label: "Departed",
                value: formatDateTime(track.actualDepartureAt),
              },
              {
                icon: Flag,
                label: "Arrived",
                value: formatDateTime(track.actualArrivalAt),
              },
              { icon: MapPin, label: "Current station", value: currentStation },
              {
                icon: TrainFront,
                label: "Stations reached",
                value: `${reached} of ${totalStations}`,
              },
            ]}
          />

          {/* next action */}
          {nextStation ? (
            <Stack gap={14} p={18} style={CARD}>
              <Group gap={9} align="center" wrap="nowrap">
                <Text
                  size="9.5px"
                  fw={700}
                  tt="uppercase"
                  c={T.muted}
                  style={{ letterSpacing: 1 }}
                >
                  Next action
                </Text>
                <Box style={{ flex: 1 }} />
                <Chip bg={T.surface3} fg={T.text2}>
                  {`STOP ${reached + 1} OF ${totalStations}`}
                </Chip>
              </Group>
              <Text size="15px" fw={700} c={T.text} lh={1.3}>
                {nextIsFinal
                  ? `Mark arrived at ${nextStation.label}`
                  : `Log pass at ${nextStation.label}`}
              </Text>
              <Text size="12px" c={T.text2} lh={1.45}>
                {nextIsFinal
                  ? "Marks the train arrived: remaining bookings arrive, assets are freed."
                  : "Logging the pass marks arriving bookings and unlocks loading for cargo boarding here."}
              </Text>
              <Group gap={8} wrap="nowrap">
                <Button
                  color="edr-green"
                  radius={9}
                  size="compact-sm"
                  style={{ flex: 1 }}
                  leftSection={nextIsFinal ? <Flag size={14} /> : <MapPin size={14} />}
                  loading={
                    recordCheckpoint.isPending &&
                    recordCheckpoint.variables?.payload.sequenceNo ===
                      nextStation.sequenceNo
                  }
                  onClick={() => handleLog(nextStation.sequenceNo)}
                >
                  {nextIsFinal ? "Mark arrived" : "Log pass"}
                </Button>
                <Button
                  variant="default"
                  radius={9}
                  size="compact-sm"
                  leftSection={<Package size={14} />}
                  onClick={() =>
                    setYardModal({
                      station: nextStation,
                      isFinal: Boolean(nextIsFinal),
                      alreadyLogged: false,
                    })
                  }
                >
                  Yard work
                </Button>
              </Group>
            </Stack>
          ) : null}

          {/* forgotten boarders */}
          {currentStationObj && forgottenBoarders.length > 0 ? (
            <Stack
              gap={11}
              p={16}
              style={{
                background: T.amberDim,
                border: `1px solid ${T.amberBorder}`,
                borderRadius: 14,
              }}
            >
              <Group gap={9} align="center" wrap="nowrap">
                <PackageCheck size={16} color={T.amber} style={{ flexShrink: 0 }} />
                <Text size="13px" fw={700} c={T.amber}>
                  {forgottenBoarders.length} booking
                  {forgottenBoarders.length === 1 ? "" : "s"} not loaded
                </Text>
              </Group>
              <Text size="11.5px" c={T.amberText} lh={1.45}>
                The train is at {currentStationObj.label} — cargo boarding here can still
                be loaded before the next station is logged.
              </Text>
              <Button
                size="compact-sm"
                radius={9}
                variant="white"
                w="fit-content"
                styles={{
                  root: { borderColor: T.amberBorder, border: `1px solid ${T.amberBorder}` },
                  label: { color: T.amber, fontWeight: 700, fontSize: 12.5 },
                }}
                onClick={() =>
                  setYardModal({
                    station: currentStationObj,
                    isFinal:
                      currentStationObj.sequenceNo ===
                      track.stations[totalStations - 1]?.sequenceNo,
                    alreadyLogged: true,
                  })
                }
              >
                Open yard work
              </Button>
            </Stack>
          ) : null}
        </Stack>

        {/* main column */}
        <Stack gap={20} style={{ flex: 1, minWidth: 520 }}>
          <Box style={CARD}>
            <SectionHead
              icon={<Route size={17} />}
              title="Journey & station work"
              hint={
                canLog
                  ? "Every stop with its pass time and loading windows — the final station marks arrival."
                  : arrived
                    ? "This train has arrived at its destination."
                    : "Tracking becomes available once the train is dispatched."
              }
              right={
                <Group gap={12} wrap="nowrap" visibleFrom="md">
                  {[
                    [T.brand, "Passed"],
                    [T.amber, "Active"],
                    [T.text3, "Upcoming"],
                  ].map(([color, label]) => (
                    <Group key={label} gap={5} wrap="nowrap">
                      <Box
                        style={{
                          width: 7,
                          height: 7,
                          borderRadius: 999,
                          background: color,
                        }}
                      />
                      <Text size="11px" fw={600} c={T.muted}>
                        {label}
                      </Text>
                    </Group>
                  ))}
                </Group>
              }
            />
            <JourneySpine
              scheduleId={scheduleId}
              stations={track.stations}
              currentSequenceNo={track.currentSequenceNo}
              checkpoints={track.checkpoints}
              stationWorkLogs={track.stationWorkLogs}
              canLog={canLog}
              loggingSeq={
                recordCheckpoint.isPending
                  ? recordCheckpoint.variables?.payload.sequenceNo
                  : null
              }
              onLogCheckpoint={handleLog}
              onEditCheckpoint={canEdit ? setEditModal : undefined}
            />
          </Box>

          <Box style={CARD}>
            <SectionHead
              icon={<ListChecks size={16} />}
              title="Checkpoint log"
              hint="Raw event trail — every logged pass with its correction history"
              right={
                <Chip bg={T.surface3} fg={T.text2}>
                  {`${track.checkpoints.length} EVENT${
                    track.checkpoints.length === 1 ? "" : "S"
                  }`}
                </Chip>
              }
            />
            <CheckpointLogTable
              checkpoints={track.checkpoints}
              onEdit={canEdit ? setEditModal : undefined}
            />
          </Box>
        </Stack>
      </Group>

      <CheckpointTimeModal
        opened={logModal !== null}
        onClose={() => setLogModal(null)}
        title={
          logModal?.isFinal
            ? `Mark arrived at ${logModal.station.label}`
            : `Log pass at ${logModal?.station.label ?? "station"}`
        }
        icon={logModal?.isFinal ? <Flag size={18} /> : <MapPin size={18} />}
        description={
          logModal?.isFinal
            ? "Marks the train arrived: remaining bookings arrive, assets are freed."
            : undefined
        }
        submitLabel={logModal?.isFinal ? "Mark arrived" : "Log pass"}
        submitColor={logModal?.isFinal ? "teal" : "edr-green"}
        loading={recordCheckpoint.isPending}
        onSubmit={submitLog}
      />

      <CheckpointTimeModal
        opened={editModal !== null}
        onClose={() => setEditModal(null)}
        title={`Edit ${editModal?.label ?? "checkpoint"}`}
        icon={<Pencil size={18} />}
        description="Corrects this leg's time, station work and note only — nothing else changes."
        initialOccurredAt={editModal?.occurredAt}
        initialNote={editModal?.note}
        initialHandling={editModal}
        submitLabel="Save"
        loading={updateCheckpoint.isPending}
        onSubmit={submitEdit}
      />

      <LogPassYardWorkModal
        opened={yardModal !== null}
        onClose={() => setYardModal(null)}
        scheduleId={scheduleId}
        station={yardModal?.station ?? null}
        stations={track.stations}
        isFinal={yardModal?.isFinal ?? false}
        alreadyLogged={yardModal?.alreadyLogged ?? false}
      />
    </Box>
  );
}
