import { memo, useMemo, useState } from "react";
import {
  Box,
  Group,
  Paper,
  Progress,
  SegmentedControl,
  Stack,
  Text,
  ThemeIcon,
  Tooltip,
} from "@mantine/core";
import {
  Boxes,
  CheckCircle2,
  Clock,
  Container,
  Crown,
  FlaskConical,
  Hourglass,
  Layers,
  ListOrdered,
  Radio,
  TrainFront,
  Trophy,
  XCircle,
} from "lucide-react";
import { CountdownTimer } from "@edr/ui-common";

import type {
  BatchBoardBookingDetail,
  BatchBoardBookingState,
  BatchBoardScheduleDetail,
} from "@/types/trainScheduling";
import { WindowPhasePill } from "./batchVisuals";
import { ForecastPanel } from "./ForecastPanel";
import { forecastIsLive } from "./batchForecast";

/**
 * Priority Tracking tab — live, glanceable ranking of every booking on this
 * schedule in the exact order the batch engine boards them (government first,
 * then rule-engine priority score, then oldest). Bookings above the train's
 * wagon-capacity line render as "selected" (green), below it as the waiting
 * list; during the PAYMENT phase selected bookings show a live pay-window
 * countdown. Purely presentational — data comes from the batch-board detail
 * response the page already polls (+ socket-invalidates).
 */

type Props = {
  data: BatchBoardScheduleDetail;
  bookings: BatchBoardBookingDetail[];
};

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

/** States that occupy a wagon slot on this train (i.e. are "in" the batch). */
const OCCUPIES_SLOT: BatchBoardBookingState[] = [
  "ALLOCATED",
  "SELECTED_FOR_BATCH",
  "WAITING",
];

const cardVar = (color: string, shade: number) =>
  `var(--mantine-color-${color}-${shade})`;

/** Highest score across the ranked pool → used to scale the priority mini-bar. */
function maxScore(bookings: BatchBoardBookingDetail[]): number {
  return bookings.reduce((m, b) => Math.max(m, b.priorityScore ?? 0), 0);
}

function FreightIcon({ type }: { type: string | null }) {
  const Icon = type === "BULK" ? Boxes : Container;
  return (
    <Tooltip label={type === "BULK" ? "Bulk" : "Container"} withArrow>
      <ThemeIcon size="sm" radius="sm" variant="light" color="gray">
        <Icon size={13} />
      </ThemeIcon>
    </Tooltip>
  );
}

/** One ranked booking row rendered as a card, colored by its batch state. */
function RankedCard({
  rank,
  booking,
  scoreMax,
  phase,
  isPayPhase,
}: {
  rank: number;
  booking: BatchBoardBookingDetail;
  scoreMax: number;
  phase: string | null;
  isPayPhase: boolean;
}) {
  const style = STATE_STYLE[booking.state];
  const Icon = style.icon;
  const selected = booking.state === "SELECTED_FOR_BATCH";
  const allocated = booking.state === "ALLOCATED";
  const expired = booking.state === "EXPIRED";
  // Green surface for the winners (allocated + selected); muted for the rest.
  const surfaceColor = allocated
    ? "edr-green"
    : selected
      ? "edr-green"
      : expired
        ? "red"
        : "gray";
  const scorePct =
    scoreMax > 0 ? Math.max(4, Math.round((booking.priorityScore / scoreMax) * 100)) : 0;

  return (
    <Paper
      radius="md"
      p="sm"
      withBorder
      style={{
        borderColor: cardVar(surfaceColor, allocated || selected ? 4 : 2),
        background:
          allocated || selected
            ? `linear-gradient(90deg, ${cardVar("edr-green", 0)} 0%, var(--mantine-color-white) 60%)`
            : expired
              ? cardVar("red", 0)
              : "var(--mantine-color-white)",
        opacity: expired ? 0.72 : 1,
        transition: "background 200ms ease, border-color 200ms ease",
      }}
    >
      <Group justify="space-between" wrap="nowrap" gap="sm">
        <Group wrap="nowrap" gap="sm" style={{ minWidth: 0 }}>
          {/* Rank medallion */}
          <ThemeIcon
            size={34}
            radius="xl"
            variant={rank <= 3 ? "filled" : "light"}
            color={
              booking.isGovernment
                ? "grape"
                : rank <= 3
                  ? "edr-green"
                  : "gray"
            }
            style={{ flexShrink: 0, fontWeight: 800 }}
          >
            {booking.isGovernment ? (
              <Crown size={16} />
            ) : (
              <Text fw={800} size="sm">
                {rank}
              </Text>
            )}
          </ThemeIcon>

          <Stack gap={2} style={{ minWidth: 0 }}>
            <Group gap={6} wrap="nowrap">
              <Text fw={700} size="sm" truncate>
                {booking.reference}
              </Text>
              <FreightIcon type={booking.freightType} />
              {booking.isGovernment ? (
                <Tooltip label="Government — boards first" withArrow>
                  <ThemeIcon size="xs" radius="sm" variant="light" color="grape">
                    <Crown size={11} />
                  </ThemeIcon>
                </Tooltip>
              ) : null}
            </Group>
            <Text size="xs" c="dimmed" truncate>
              {booking.company}
            </Text>
          </Stack>
        </Group>

        <Group wrap="nowrap" gap="lg" style={{ flexShrink: 0 }}>
          {/* Priority score with a mini strength bar */}
          <Tooltip
            label={`Priority score ${booking.priorityScore}${booking.isGovernment ? " + government bonus" : ""}`}
            withArrow
          >
            <Stack gap={2} align="flex-end" w={92}>
              <Group gap={4} wrap="nowrap">
                <Trophy size={12} color={cardVar("edr-green", 6)} />
                <Text fw={800} size="sm" c="edr-green.7">
                  {booking.priorityScore}
                </Text>
              </Group>
              <Progress
                value={scorePct}
                size="xs"
                color="edr-green"
                w={92}
                radius="xl"
              />
            </Stack>
          </Tooltip>

          {/* Wagons */}
          <Group gap={4} wrap="nowrap" w={58} justify="flex-end">
            <TrainFront size={13} color={cardVar("gray", 6)} />
            <Text fw={700} size="sm">
              {booking.wagons}w
            </Text>
          </Group>

          {/* State chip / pay countdown */}
          <Box w={168} style={{ textAlign: "right" }}>
            {selected && isPayPhase && booking.paymentDeadline ? (
              <CountdownTimer
                deadline={booking.paymentDeadline}
                label="Pay in"
                expiredText="Window closed"
                size="sm"
              />
            ) : (
              <Group gap={5} justify="flex-end" wrap="nowrap">
                <ThemeIcon
                  size="sm"
                  radius="sm"
                  variant="light"
                  color={style.color}
                >
                  <Icon size={12} />
                </ThemeIcon>
                <Text size="xs" fw={600} c={`${style.color}.7`}>
                  {style.label}
                </Text>
              </Group>
            )}
          </Box>
        </Group>
      </Group>
      {/* phase hint only used for the a11y title; keeps `phase` referenced */}
      <span hidden aria-hidden>
        {phase}
      </span>
    </Paper>
  );
}

/** The capacity cut line drawn between "in the batch" and "waiting list". */
function CapacityDivider({ used, max }: { used: number; max: number | null }) {
  const full = max != null && used >= max;
  return (
    <Group gap="xs" my={4} wrap="nowrap">
      <Box style={{ flex: 1, height: 2, background: cardVar("orange", 3) }} />
      <Group gap={6} wrap="nowrap">
        <ThemeIcon size="sm" radius="xl" variant="light" color="orange">
          <Layers size={12} />
        </ThemeIcon>
        <Text size="xs" fw={700} c="orange.7">
          Capacity line{max != null ? ` · ${used}/${max} wagons` : ` · ${used} wagons`}
          {full ? " · FULL" : ""}
        </Text>
      </Group>
      <Box style={{ flex: 1, height: 2, background: cardVar("orange", 3) }} />
    </Group>
  );
}

// Memoized: mounted in a keep-mounted Tabs panel, so it re-renders with every
// page render; both props keep their identity across unrelated page state
// (React Query structural sharing + the page's useMemo'd bookings).
export const PriorityTrackingTab = memo(function PriorityTrackingTab({
  data,
  bookings,
}: Props) {
  const phase = data.windowPhase;
  const isPayPhase = phase === "PAYMENT";

  // Before the batch is committed (pre-window / open / doc-review) the real
  // selection doesn't exist yet — offer a simulated forecast of who WOULD board.
  // Default to it while it's live; let staff flip to the current live state.
  const forecastAvailable = forecastIsLive(phase);
  const [view, setView] = useState<"forecast" | "live">(
    forecastAvailable ? "forecast" : "live",
  );
  const showForecast = forecastAvailable && view === "forecast";

  // Rank exactly as the batch engine does: government first, then priority score
  // desc, then oldest (fullyExecutedAt / selectedForBatchAt as the tiebreak the
  // backend uses). The board already returns them in this order, but re-sort
  // defensively so the tab is correct even if the source order ever changes.
  const ranked = useMemo(() => {
    const time = (b: BatchBoardBookingDetail) =>
      b.fullyExecutedAt ? new Date(b.fullyExecutedAt).getTime() : Number.MAX_SAFE_INTEGER;
    return [...bookings].sort((a, b) => {
      if (a.isGovernment !== b.isGovernment) return a.isGovernment ? -1 : 1;
      if (b.priorityScore !== a.priorityScore) return b.priorityScore - a.priorityScore;
      return time(a) - time(b);
    });
  }, [bookings]);

  const scoreMax = useMemo(() => maxScore(ranked), [ranked]);
  // Wagon-slot cap from the board DTO (derived from train length and the
  // shortest wagon type); null on legacy rows without a computable cap.
  const maxWagons: number | null = data.capacity.maxWagons ?? null;

  // Split the ranking at the capacity line: cumulative wagons of slot-occupying
  // bookings (allocated + selected + paid-waiting) up to the train's wagon cap.
  const capUsed = useMemo(
    () =>
      ranked
        .filter((b) => OCCUPIES_SLOT.includes(b.state))
        .reduce((sum, b) => sum + b.wagons, 0),
    [ranked],
  );

  // Group for the lane layout.
  const lanes = useMemo(() => {
    const inBatch = ranked.filter((b) => OCCUPIES_SLOT.includes(b.state));
    const waiting = ranked.filter(
      (b) => b.state === "READY" || b.state === "PENDING_CONTRACT",
    );
    const expired = ranked.filter((b) => b.state === "EXPIRED");
    return { inBatch, waiting, expired };
  }, [ranked]);

  if (ranked.length === 0) {
    return (
      <Paper radius="lg" withBorder p="xl">
        <Group justify="center" gap="sm">
          <ThemeIcon variant="light" color="gray" radius="xl" size="lg">
            <ListOrdered size={18} />
          </ThemeIcon>
          <Text c="dimmed">No bookings on this schedule yet.</Text>
        </Group>
      </Paper>
    );
  }

  let rankNo = 0;

  const viewToggle = forecastAvailable ? (
    <SegmentedControl
      value={view}
      onChange={(v) => setView(v as "forecast" | "live")}
      size="sm"
      radius="md"
      data={[
        {
          value: "forecast",
          label: (
            <Group gap={6} wrap="nowrap">
              <FlaskConical size={13} />
              <Text size="xs" fw={600}>
                Forecast
              </Text>
            </Group>
          ),
        },
        {
          value: "live",
          label: (
            <Group gap={6} wrap="nowrap">
              <Radio size={13} />
              <Text size="xs" fw={600}>
                Live state
              </Text>
            </Group>
          ),
        },
      ]}
    />
  ) : null;

  if (showForecast) {
    return (
      <Stack gap="lg">
        {viewToggle ? <Group justify="flex-end">{viewToggle}</Group> : null}
        <ForecastPanel data={data} bookings={ranked} />
      </Stack>
    );
  }

  return (
    <Stack gap="lg">
      {viewToggle ? <Group justify="flex-end">{viewToggle}</Group> : null}
      {/* Header: phase + capacity meter */}
      <Paper radius="lg" withBorder p="lg">
        <Group justify="space-between" wrap="wrap" gap="md">
          <Group gap="sm">
            <ThemeIcon variant="light" color="edr-green" radius="md" size="lg">
              <Trophy size={18} />
            </ThemeIcon>
            <Stack gap={2}>
              <Text fw={700}>Priority ranking</Text>
              <Text size="xs" c="dimmed">
                Government first, then rule-engine score, then earliest booked.
              </Text>
            </Stack>
          </Group>
          <Group gap="md">
            {phase ? (
              <WindowPhasePill phase={phase} cycleNo={data.bookingCycleNo} />
            ) : null}
            {isPayPhase && data.paymentPhaseEndsAt ? (
              <CountdownTimer
                deadline={data.paymentPhaseEndsAt}
                label="Payment window"
                expiredText="Window closed"
                size="md"
              />
            ) : phase === "DOC_REVIEW" && data.docReviewEndsAt ? (
              <CountdownTimer
                deadline={data.docReviewEndsAt}
                label="Doc review ends"
                expiredText="Review over"
                size="md"
              />
            ) : phase === "OPEN" && data.windowClosesAt ? (
              <CountdownTimer
                deadline={data.windowClosesAt}
                label="Booking closes"
                expiredText="Closed"
                size="md"
              />
            ) : null}
          </Group>
        </Group>

        {/* Capacity meter */}
        <Box mt="md">
          <Group justify="space-between" mb={4}>
            <Text size="xs" c="dimmed" fw={600}>
              Wagon capacity used
            </Text>
            <Text size="xs" fw={700}>
              {data.capacity.allocatedWagons} allocated ·{" "}
              {capUsed} in batch
              {maxWagons != null ? ` · ${maxWagons} max` : ""}
            </Text>
          </Group>
          {/* Scale against the real wagon cap when the DTO carries one; fall back
              to the in-batch total on legacy rows without a computable cap. */}
          <Progress.Root size="lg" radius="xl">
            <Progress.Section
              value={
                (maxWagons ?? capUsed) > 0
                  ? Math.min(
                      100,
                      (data.capacity.allocatedWagons / (maxWagons ?? capUsed)) * 100,
                    )
                  : 0
              }
              color="edr-green"
            />
            <Progress.Section
              value={
                (maxWagons ?? capUsed) > 0
                  ? Math.min(
                      100,
                      ((capUsed - data.capacity.allocatedWagons) /
                        (maxWagons ?? capUsed)) *
                        100,
                    )
                  : 0
              }
              color="orange"
            />
          </Progress.Root>
        </Box>
      </Paper>

      {/* Phase banner explaining what's happening now */}
      <PhaseBanner phase={phase} />

      {/* IN THE BATCH (green winners) — ranked */}
      {lanes.inBatch.length > 0 ? (
        <Stack gap={6}>
          <Group gap="xs">
            <ThemeIcon size="sm" radius="sm" variant="light" color="edr-green">
              <CheckCircle2 size={13} />
            </ThemeIcon>
            <Text fw={700} size="sm">
              In the batch{" "}
              <Text span c="dimmed" fw={500}>
                ({lanes.inBatch.length})
              </Text>
            </Text>
          </Group>
          {lanes.inBatch.map((b) => {
            rankNo += 1;
            return (
              <RankedCard
                key={b.id}
                rank={rankNo}
                booking={b}
                scoreMax={scoreMax}
                phase={phase}
                isPayPhase={isPayPhase}
              />
            );
          })}
        </Stack>
      ) : null}

      <CapacityDivider used={capUsed} max={maxWagons} />

      {/* WAITING LIST — ranked, below the line */}
      {lanes.waiting.length > 0 ? (
        <Stack gap={6}>
          <Group gap="xs">
            <ThemeIcon size="sm" radius="sm" variant="light" color="blue">
              <Hourglass size={13} />
            </ThemeIcon>
            <Text fw={700} size="sm">
              Waiting list{" "}
              <Text span c="dimmed" fw={500}>
                ({lanes.waiting.length}) — next in line if a slot frees up
              </Text>
            </Text>
          </Group>
          {lanes.waiting.map((b) => {
            rankNo += 1;
            return (
              <RankedCard
                key={b.id}
                rank={rankNo}
                booking={b}
                scoreMax={scoreMax}
                phase={phase}
                isPayPhase={isPayPhase}
              />
            );
          })}
        </Stack>
      ) : null}

      {/* EXPIRED */}
      {lanes.expired.length > 0 ? (
        <Stack gap={6}>
          <Group gap="xs">
            <ThemeIcon size="sm" radius="sm" variant="light" color="red">
              <XCircle size={13} />
            </ThemeIcon>
            <Text fw={700} size="sm" c="red.7">
              Expired{" "}
              <Text span c="dimmed" fw={500}>
                ({lanes.expired.length}) — missed the payment window
              </Text>
            </Text>
          </Group>
          {lanes.expired.map((b) => (
            <RankedCard
              key={b.id}
              rank={0}
              booking={b}
              scoreMax={scoreMax}
              phase={phase}
              isPayPhase={isPayPhase}
            />
          ))}
        </Stack>
      ) : null}
    </Stack>
  );
});

/** Contextual banner describing the current window phase in plain language. */
function PhaseBanner({ phase }: { phase: string | null }) {
  const meta: Record<string, { color: string; text: string; icon: typeof Clock }> = {
    OPEN: {
      color: "edr-green",
      icon: Clock,
      text: "Booking window OPEN — new bookings are ranked live as they arrive and get accepted.",
    },
    DOC_REVIEW: {
      color: "yellow",
      icon: Hourglass,
      text: "Document review — staff accept/reject; un-accepted bookings expire when review ends, then the batch runs.",
    },
    PAYMENT: {
      color: "blue",
      icon: Clock,
      text: "Payment window — selected bookings must pay before their countdown ends; unpaid slots pass to the waiting list.",
    },
    PRE_WINDOW: {
      color: "gray",
      icon: Hourglass,
      text: "Window not open yet — bookings are pre-ranked and will compete when it opens.",
    },
    CLOSED_FOR_DAY: {
      color: "gray",
      icon: Hourglass,
      text: "Window closed for the day — reopens for the next cycle if the train isn't full.",
    },
    DONE: {
      color: "gray",
      icon: CheckCircle2,
      text: "Booking cycles finished for this train.",
    },
  };
  const m = phase ? meta[phase] : null;
  if (!m) return null;
  const Icon = m.icon;
  return (
    <Paper
      radius="md"
      p="sm"
      withBorder
      style={{
        background: cardVar(m.color, 0),
        borderColor: cardVar(m.color, 2),
      }}
    >
      <Group gap="sm" wrap="nowrap">
        <ThemeIcon variant="light" color={m.color} radius="md">
          <Icon size={16} />
        </ThemeIcon>
        <Text size="sm" fw={500} c={`${m.color}.8`}>
          {m.text}
        </Text>
      </Group>
    </Paper>
  );
}

export default PriorityTrackingTab;
