import {
  Alert,
  Badge,
  Button,
  Divider,
  Group,
  Loader,
  Paper,
  Stack,
  Table,
  Text,
  Tooltip,
} from "@mantine/core";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertCircle, MapPin, PackageCheck, PackageOpen, TrainFront } from "lucide-react";
import { Freight } from "@edr/types";

import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge";
import type { YardWorkBookingRow, YardWorkYard } from "@/types/trainScheduling";

const parseError = (error: unknown, fallback: string) => {
  const message = (error as { response?: { data?: { message?: string | string[] } } })
    ?.response?.data?.message;
  if (Array.isArray(message)) return message.join("; ");
  return message || (error as Error)?.message || fallback;
};

const fmtDate = (iso: string) => {
  const d = new Date(iso);
  return Number.isNaN(d.getTime()) ? iso : d.toLocaleString();
};

const DIRECTION_COLORS: Record<string, string> = {
  IMPORT: "blue",
  EXPORT: "teal",
  DOMESTIC: "violet",
};

/** DOMESTIC displays as "Intercity" — shared with the rest of the platform. */
const DIRECTION_LABELS: Record<string, string> = Freight.TRADE_DIRECTION_LABELS;

function DirectionChip({ direction }: { direction: string }) {
  return (
    <Badge size="sm" variant="light" color={DIRECTION_COLORS[direction] ?? "gray"}>
      {DIRECTION_LABELS[direction] ?? direction}
    </Badge>
  );
}

function BookingCell({ row }: { row: YardWorkBookingRow }) {
  return (
    <Group gap={6} wrap="nowrap">
      <Text size="sm" fw={600}>
        {row.reference ?? row.id.slice(0, 8)}
      </Text>
      {row.isGovernment && (
        <Badge size="xs" variant="light" color="grape">
          GOV
        </Badge>
      )}
    </Group>
  );
}

function WorkTable({
  rows,
  side,
  trainHere,
  onLoad,
  onUnload,
  pendingBookingId,
}: {
  rows: YardWorkBookingRow[];
  side: "load" | "unload";
  trainHere: boolean;
  onLoad: (bookingId: string) => void;
  onUnload: (bookingId: string) => void;
  pendingBookingId: string | null;
}) {
  if (rows.length === 0) {
    return (
      <Text size="sm" c="dimmed">
        {side === "load" ? "No bookings board here." : "No bookings alight here."}
      </Text>
    );
  }
  return (
    <Table.ScrollContainer minWidth={720}>
      <Table verticalSpacing="xs" highlightOnHover>
        <Table.Thead>
          <Table.Tr>
            <Table.Th>Booking</Table.Th>
            <Table.Th>Customer</Table.Th>
            <Table.Th>Direction</Table.Th>
            <Table.Th>Status</Table.Th>
            <Table.Th>{side === "load" ? "Loaded" : "Arrived"}</Table.Th>
            <Table.Th />
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>
          {rows.map((row) => {
            const timestamp = side === "load" ? row.loadedAt : row.arrivedAt;
            const canAct = side === "load" ? row.canLoad : row.canUnload;
            return (
              <Table.Tr key={row.id}>
                <Table.Td>
                  <BookingCell row={row} />
                </Table.Td>
                <Table.Td>
                  <Text size="sm">{row.customer}</Text>
                </Table.Td>
                <Table.Td>
                  <DirectionChip direction={row.tradeDirection} />
                </Table.Td>
                <Table.Td>
                  <BookingStatusBadge status={row.status} />
                </Table.Td>
                <Table.Td>
                  {timestamp ? (
                    <Text size="xs" c="dimmed">
                      {fmtDate(timestamp)}
                    </Text>
                  ) : (
                    <Text size="xs" c="dimmed">
                      —
                    </Text>
                  )}
                </Table.Td>
                <Table.Td>
                  <Group gap="xs" justify="flex-end">
                    {side === "load" ? (
                      <Tooltip
                        label={
                          trainHere
                            ? "Confirm cargo loaded at this yard"
                            : "Train must be at this yard"
                        }
                        disabled={!canAct && Boolean(row.loadedAt)}
                      >
                        <Button
                          size="compact-xs"
                          variant="light"
                          leftSection={<PackageCheck size={13} />}
                          disabled={!canAct || !trainHere}
                          loading={pendingBookingId === row.id}
                          onClick={() => onLoad(row.id)}
                        >
                          Load
                        </Button>
                      </Tooltip>
                    ) : (
                      <Tooltip
                        label={
                          trainHere
                            ? "Confirm cargo unloaded at this yard"
                            : "Train must be at this yard"
                        }
                        disabled={!canAct && Boolean(row.arrivedAt)}
                      >
                        <Button
                          size="compact-xs"
                          variant="light"
                          color="orange"
                          leftSection={<PackageOpen size={13} />}
                          disabled={!canAct || !trainHere}
                          loading={pendingBookingId === row.id}
                          onClick={() => onUnload(row.id)}
                        >
                          Unload
                        </Button>
                      </Tooltip>
                    )}
                  </Group>
                </Table.Td>
              </Table.Tr>
            );
          })}
        </Table.Tbody>
      </Table>
    </Table.ScrollContainer>
  );
}

/**
 * Per-yard load/unload worklist for one schedule — every trade direction. Each
 * booking boards at its origin yard and alights at its destination yard; the
 * operator confirms both while the train's last recorded checkpoint is at that
 * yard (the server validates the position). Unloading stamps the booking's own
 * arrival — ARRIVED for import/export, COMPLETED for intercity.
 */
export function YardWorkPanel({ scheduleId }: { scheduleId: string }) {
  const { toast } = useToast();
  const queryClient = useQueryClient();

  const yardWorkQuery = useQuery(
    api.trainScheduling.yardWork.queryOptions({
      input: { scheduleId },
      refetchInterval: 60_000,
    }),
  );

  // Loading/unloading changes booking status on the schedule detail and the
  // intercity panel too — refresh all three so no surface shows a stale state.
  const invalidate = () => {
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.yardWork.queryKey({ scheduleId }),
    });
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.intercityCandidates.queryKey({ scheduleId }),
    });
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.scheduleDetail.queryKey({ id: scheduleId }),
    });
  };

  const load = useMutation(
    api.trainScheduling.loadScheduleBooking.mutationOptions({
      onSuccess: () => {
        void invalidate();
        toast({ title: "Cargo loaded" });
      },
      onError: (err) =>
        toast({
          title: "Load failed",
          description: parseError(err, "Could not confirm loading"),
          variant: "destructive",
        }),
    }),
  );

  const unload = useMutation(
    api.trainScheduling.unloadScheduleBooking.mutationOptions({
      onSuccess: (result) => {
        void invalidate();
        toast({
          title:
            result.status === "COMPLETED"
              ? "Cargo unloaded — booking completed"
              : "Cargo unloaded — booking arrived",
        });
      },
      onError: (err) =>
        toast({
          title: "Unload failed",
          description: parseError(err, "Could not confirm unloading"),
          variant: "destructive",
        }),
    }),
  );

  const data = yardWorkQuery.data;
  const yards: YardWorkYard[] = data?.yards ?? [];
  const trainAtYardId = data?.trainAtYardId ?? null;
  const pendingLoadId = load.isPending ? (load.variables?.bookingId ?? null) : null;
  const pendingUnloadId = unload.isPending ? (unload.variables?.bookingId ?? null) : null;

  return (
    <Paper withBorder radius="lg" p="lg" mt="md">
      <Stack gap="md">
        <Group gap="xs">
          <MapPin size={18} />
          <Text fw={700}>Yard load / unload</Text>
        </Group>

        {yardWorkQuery.isLoading ? (
          <Group gap="xs">
            <Loader size="xs" />
            <Text size="sm" c="dimmed">
              Loading yard worklists…
            </Text>
          </Group>
        ) : yardWorkQuery.isError ? (
          <Alert color="red" variant="light" icon={<AlertCircle size={16} />}>
            {parseError(yardWorkQuery.error, "Could not load the yard worklist")}
          </Alert>
        ) : yards.length === 0 ? (
          <Text size="sm" c="dimmed">
            No bookings are assigned to this schedule yet.
          </Text>
        ) : (
          <>
            <Text size="sm" c="dimmed">
              What boards and alights at each stop. Confirm loading at a booking's
              origin and unloading at its destination while the train is at that
              yard — unloading stamps the booking's own arrival, even before the
              train's final stop.
            </Text>
            {yards.map((yard, index) => {
              const trainHere = trainAtYardId === yard.yardId;
              return (
                <Stack key={yard.yardId} gap="sm">
                  {index > 0 && <Divider />}
                  <Group gap="xs">
                    <Text fw={600}>{yard.yard}</Text>
                    {trainHere && (
                      <Badge
                        size="sm"
                        variant="light"
                        color="edr-green"
                        leftSection={<TrainFront size={12} />}
                      >
                        Train here
                      </Badge>
                    )}
                  </Group>
                  <Stack gap={6}>
                    <Text size="sm" fw={600} c="dimmed">
                      Board here
                    </Text>
                    <WorkTable
                      rows={yard.toLoad}
                      side="load"
                      trainHere={trainHere}
                      onLoad={(bookingId) => load.mutate({ scheduleId, bookingId })}
                      onUnload={(bookingId) => unload.mutate({ scheduleId, bookingId })}
                      pendingBookingId={pendingLoadId}
                    />
                  </Stack>
                  <Stack gap={6}>
                    <Text size="sm" fw={600} c="dimmed">
                      Alight here
                    </Text>
                    <WorkTable
                      rows={yard.toUnload}
                      side="unload"
                      trainHere={trainHere}
                      onLoad={(bookingId) => load.mutate({ scheduleId, bookingId })}
                      onUnload={(bookingId) => unload.mutate({ scheduleId, bookingId })}
                      pendingBookingId={pendingUnloadId}
                    />
                  </Stack>
                </Stack>
              );
            })}
          </>
        )}
      </Stack>
    </Paper>
  );
}
