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

import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import type {
  IntercityBookingRow,
  IntercityCapacity,
} 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;
};

// A capacity axis can be null when the schedule's locomotive has no limit
// configured for it — render "—" instead of crashing on toFixed.
function fmt(n: number | null | undefined): string {
  if (n == null) return "—";
  return Number.isInteger(n) ? String(n) : n.toFixed(1);
}

function CapacityBadges({ capacity }: { capacity: IntercityCapacity | null }) {
  if (!capacity) {
    return (
      <Text size="sm" c="dimmed">
        Capacity unknown — schedule has no locomotive/train set yet.
      </Text>
    );
  }
  return (
    <Group gap="xs">
      <Badge
        variant="light"
        color={capacity.wagons == null ? "gray" : capacity.wagons > 0 ? "teal" : "red"}
      >
        {fmt(capacity.wagons)} wagons free
      </Badge>
      <Badge
        variant="light"
        color={capacity.weightTons == null ? "gray" : capacity.weightTons > 0 ? "teal" : "red"}
      >
        {fmt(capacity.weightTons)} t free
      </Badge>
      <Badge
        variant="light"
        color={capacity.lengthMeters == null ? "gray" : capacity.lengthMeters > 0 ? "teal" : "red"}
      >
        {fmt(capacity.lengthMeters)} m free
      </Badge>
    </Group>
  );
}

function NeedCells({ need }: { need: IntercityCapacity | null }) {
  if (!need) return <Table.Td colSpan={3}>—</Table.Td>;
  return (
    <>
      <Table.Td>{fmt(need.wagons)}</Table.Td>
      <Table.Td>{fmt(need.weightTons)} t</Table.Td>
      <Table.Td>{fmt(need.lengthMeters)} m</Table.Td>
    </>
  );
}

function CorridorCell({ row }: { row: IntercityBookingRow }) {
  return (
    <Group gap={6} wrap="nowrap">
      <Text size="sm">{row.origin}</Text>
      <ArrowRight size={13} />
      <Text size="sm">{row.destination}</Text>
    </Group>
  );
}

/**
 * Intercity ride-along desk for one import/export schedule: waiting intercity
 * bookings whose corridor lies on this train's route, checked against the
 * remaining wagon/weight/length budget. Accepting opens the customer's pay
 * window; after payment the booking is allocated. Loading/unloading is
 * confirmed manually when the train is physically at the booking's origin /
 * destination yard (the server validates against recorded checkpoints).
 */
/** Plain-language journey states for the accepted ride-along table. */
const INTERCITY_STATUS_META: Record<string, { label: string; color: string }> = {
  SELECTED_FOR_BATCH: { label: "Awaiting payment", color: "yellow" },
  APPROVED: { label: "Ready to load (gov)", color: "edr-green" },
  PAID: { label: "Paid — ready to load", color: "edr-green" },
  IN_TRANSIT: { label: "Loaded — in transit", color: "indigo" },
  COMPLETED: { label: "Delivered", color: "teal" },
};

export function IntercityRideAlongPanel({
  scheduleId,
  direction,
}: {
  scheduleId: string;
  direction: string | null | undefined;
}) {
  const { toast } = useToast();
  const queryClient = useQueryClient();
  const [selected, setSelected] = useState<string[]>([]);

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

  // Accepting/loading/unloading a ride-along changes the schedule's booking
  // list, the yard worklists AND this panel — refresh all three so the
  // workspace board and yard-work tables never show a stale picture.
  const invalidate = () => {
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.intercityCandidates.queryKey({ scheduleId }),
    });
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.yardWork.queryKey({ scheduleId }),
    });
    void queryClient.invalidateQueries({
      queryKey: api.trainScheduling.scheduleDetail.queryKey({ id: scheduleId }),
    });
  };

  const accept = useMutation(
    api.trainScheduling.acceptIntercityBookings.mutationOptions({
      onSuccess: (result) => {
        setSelected([]);
        void invalidate();
        if (result.accepted.length > 0) {
          toast({
            title: `${result.accepted.length} intercity booking(s) accepted`,
            description: "Customers have been asked to pay.",
          });
        }
        for (const r of result.rejected) {
          toast({
            title: "Booking skipped",
            description: r.reason,
            variant: "destructive",
          });
        }
      },
      onError: (err) =>
        toast({
          title: "Accept failed",
          description: parseError(err, "Could not accept intercity bookings"),
          variant: "destructive",
        }),
    }),
  );

  const load = useMutation(
    api.trainScheduling.loadIntercityBooking.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.unloadIntercityBooking.mutationOptions({
      onSuccess: () => {
        void invalidate();
        toast({ title: "Cargo unloaded — booking completed" });
      },
      onError: (err) =>
        toast({
          title: "Unload failed",
          description: parseError(err, "Could not confirm unloading"),
          variant: "destructive",
        }),
    }),
  );

  // Intercity bookings only ride import/export trains.
  if (direction !== "IMPORT" && direction !== "EXPORT") return null;

  const data = candidatesQuery.data;
  const candidates = data?.candidates ?? [];
  const accepted = data?.accepted ?? [];

  if (candidatesQuery.isLoading) {
    return (
      <Paper withBorder radius="lg" p="lg" mt="md">
        <Group gap="xs">
          <Loader size="xs" />
          <Text size="sm" c="dimmed">
            Loading intercity ride-along bookings…
          </Text>
        </Group>
      </Paper>
    );
  }

  if (candidates.length === 0 && accepted.length === 0) return null;

  return (
    <Paper withBorder radius="lg" p="lg" mt="md">
      <Stack gap="md">
        <Group justify="space-between" wrap="wrap">
          <Group gap="xs">
            <TrainFront size={18} />
            <Text fw={700}>Intercity ride-along</Text>
          </Group>
          <CapacityBadges capacity={data?.remaining ?? null} />
        </Group>

        {candidates.length > 0 && (
          <>
            <Text size="sm" c="dimmed">
              Waiting intercity bookings whose corridor lies on this train's
              route. Accepting opens the customer's payment window against the
              free capacity above.
            </Text>
            <Table.ScrollContainer minWidth={720}>
              <Table verticalSpacing="xs" highlightOnHover>
                <Table.Thead>
                  <Table.Tr>
                    <Table.Th w={36} />
                    <Table.Th>Booking</Table.Th>
                    <Table.Th>Customer</Table.Th>
                    <Table.Th>Corridor</Table.Th>
                    <Table.Th>Wagons</Table.Th>
                    <Table.Th>Weight</Table.Th>
                    <Table.Th>Length</Table.Th>
                    <Table.Th>Fits</Table.Th>
                  </Table.Tr>
                </Table.Thead>
                <Table.Tbody>
                  {candidates.map((row) => (
                    <Table.Tr key={row.id}>
                      <Table.Td>
                        <Checkbox
                          size="xs"
                          checked={selected.includes(row.id)}
                          onChange={(e) =>
                            setSelected((prev) =>
                              e.currentTarget.checked
                                ? [...prev, row.id]
                                : prev.filter((id) => id !== row.id),
                            )
                          }
                        />
                      </Table.Td>
                      <Table.Td>
                        <Group gap={6}>
                          <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>
                      </Table.Td>
                      <Table.Td>
                        <Text size="sm">{row.customer}</Text>
                      </Table.Td>
                      <Table.Td>
                        <CorridorCell row={row} />
                      </Table.Td>
                      <NeedCells need={row.need} />
                      <Table.Td>
                        {row.fits ? (
                          <Badge size="sm" variant="light" color="teal">
                            Fits
                          </Badge>
                        ) : (
                          <Tooltip label="Exceeds the remaining wagon/weight/length budget">
                            <Badge size="sm" variant="light" color="red">
                              No room
                            </Badge>
                          </Tooltip>
                        )}
                      </Table.Td>
                    </Table.Tr>
                  ))}
                </Table.Tbody>
              </Table>
            </Table.ScrollContainer>
            <Group justify="flex-end">
              <Button
                size="xs"
                color="edr-green"
                loading={accept.isPending}
                disabled={selected.length === 0}
                onClick={() => accept.mutate({ scheduleId, bookingIds: selected })}
              >
                Accept {selected.length > 0 ? `${selected.length} ` : ""}onto this train
              </Button>
            </Group>
          </>
        )}

        {accepted.length > 0 && (
          <>
            <Text size="sm" fw={600}>
              On this train
            </Text>
            <Table.ScrollContainer minWidth={680}>
              <Table verticalSpacing="xs">
                <Table.Thead>
                  <Table.Tr>
                    <Table.Th>Booking</Table.Th>
                    <Table.Th>Customer</Table.Th>
                    <Table.Th>Corridor</Table.Th>
                    <Table.Th>Status</Table.Th>
                    <Table.Th />
                  </Table.Tr>
                </Table.Thead>
                <Table.Tbody>
                  {accepted.map((row) => (
                    <Table.Tr key={row.id}>
                      <Table.Td>
                        <Text size="sm" fw={600}>
                          {row.reference ?? row.id.slice(0, 8)}
                        </Text>
                      </Table.Td>
                      <Table.Td>
                        <Text size="sm">{row.customer}</Text>
                      </Table.Td>
                      <Table.Td>
                        <CorridorCell row={row} />
                      </Table.Td>
                      <Table.Td>
                        <Badge
                          size="sm"
                          variant="light"
                          color={
                            INTERCITY_STATUS_META[row.status ?? ""]?.color ?? "gray"
                          }
                        >
                          {INTERCITY_STATUS_META[row.status ?? ""]?.label ?? row.status}
                        </Badge>
                      </Table.Td>
                      <Table.Td>
                        <Group gap="xs" justify="flex-end">
                          {row.status === "PAID" && (
                            <Tooltip label="Train must be at the booking's origin yard">
                              <Button
                                size="compact-xs"
                                variant="light"
                                leftSection={<PackageCheck size={13} />}
                                loading={load.isPending}
                                onClick={() =>
                                  load.mutate({ scheduleId, bookingId: row.id })
                                }
                              >
                                Load
                              </Button>
                            </Tooltip>
                          )}
                          {row.status === "IN_TRANSIT" && (
                            <Tooltip label="Train must be at the booking's destination yard">
                              <Button
                                size="compact-xs"
                                variant="light"
                                color="orange"
                                leftSection={<PackageOpen size={13} />}
                                loading={unload.isPending}
                                onClick={() =>
                                  unload.mutate({ scheduleId, bookingId: row.id })
                                }
                              >
                                Unload
                              </Button>
                            </Tooltip>
                          )}
                        </Group>
                      </Table.Td>
                    </Table.Tr>
                  ))}
                </Table.Tbody>
              </Table>
            </Table.ScrollContainer>
          </>
        )}

        {candidatesQuery.isError && (
          <Alert color="red" variant="light" icon={<AlertCircle size={16} />}>
            {parseError(candidatesQuery.error, "Could not load intercity candidates")}
          </Alert>
        )}
      </Stack>
    </Paper>
  );
}
