import { Button, Group, Modal, MultiSelect, Stack, Text } from "@mantine/core";
import { useMutation, useQuery } from "@tanstack/react-query";
import { isAxiosError } from "axios";
import { useEffect, useMemo, useState } from "react";

import { api } from "@/services/api";
import type { TrainComposition } from "@/services/trainBuilder.service";
import { useToast } from "@/hooks/use-toast";

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

/** Swap the locomotive set of a built train (minimum 2, same-yard rule). */
export default function ChangeLocomotivesModal({
  composition,
  opened,
  onClose,
}: ChangeLocomotivesModalProps) {
  const { toast } = useToast();
  const [locomotiveIds, setLocomotiveIds] = useState<string[]>([]);

  const yardId = composition?.currentYard?.id ?? "";
  // A locomotive already coupled to ANOTHER built train is not a valid pick —
  // the API rejects it on save. Exclude those here (keeping this train's own
  // ones, which are re-listed below as "(coupled)").
  const availableQuery = useQuery(
    api.locomotives.listFiltered.queryOptions({
      input: {
        filters: {
          status: "AVAILABLE",
          currentYardId: yardId,
          excludeCoupled: true,
          excludeTrainId: composition?.id,
        },
      },
      enabled: opened && Boolean(yardId),
    }),
  );
  const setLocomotives = useMutation(api.trainBuilder.setLocomotives.mutationOptions());

  useEffect(() => {
    if (opened && composition) {
      setLocomotiveIds(composition.locomotives.map((l) => l.id));
    }
  }, [opened, composition]);

  // Pickable = available locomotives in the yard + the ones already coupled
  // to this train (valid to keep even though they are not "loose" anymore).
  const options = useMemo(() => {
    const seen = new Set<string>();
    const rows: Array<{ value: string; label: string }> = [];
    for (const loco of composition?.locomotives ?? []) {
      seen.add(loco.id);
      rows.push({
        value: loco.id,
        label: `${loco.code}${loco.name ? ` — ${loco.name}` : ""} · pulls ${loco.maxPullWeightTons}T (coupled)`,
      });
    }
    for (const loco of availableQuery.data ?? []) {
      if (seen.has(loco.id)) continue;
      rows.push({
        value: loco.id,
        label: `${loco.code}${loco.name ? ` — ${loco.name}` : ""} · pulls ${loco.maxPullWeightTons}T`,
      });
    }
    return rows;
  }, [composition, availableQuery.data]);

  const handleSave = async () => {
    if (!composition) return;
    if (locomotiveIds.length < 2) {
      toast({ title: "A train needs at least two locomotives", variant: "destructive" });
      return;
    }
    try {
      await setLocomotives.mutateAsync({ id: composition.id, locomotiveIds });
      toast({ title: "Locomotives updated" });
      onClose();
    } catch (err) {
      toast({
        title: "Update failed",
        description: parseError(err, "Could not update locomotives"),
        variant: "destructive",
      });
    }
  };

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      title={<Text fw={600}>Change locomotives</Text>}
      radius="lg"
      centered
    >
      <Stack gap="md">
        <Text size="sm" c="dimmed">
          Only available locomotives standing in{" "}
          {composition?.currentYard?.label ?? "the train's yard"} can be coupled.
          The first pick is the lead locomotive.
        </Text>
        <MultiSelect
          label="Locomotives"
          data={options}
          value={locomotiveIds}
          onChange={setLocomotiveIds}
          searchable
          error={
            locomotiveIds.length > 0 && locomotiveIds.length < 2
              ? "Select at least two locomotives"
              : undefined
          }
          nothingFoundMessage="No available locomotives in this yard"
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button loading={setLocomotives.isPending} onClick={handleSave}>
            Save
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

export interface ChangeLocomotivesModalProps {
  composition: TrainComposition | null;
  opened: boolean;
  onClose: () => void;
}
