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

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

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;
};

/**
 * Step one of the Train Builder: pick the yard it is being assembled in and
 * couple at least one locomotive from that yard. The train code is assigned by
 * the system. Wagons are attached afterwards on the composition page.
 */
export default function BuildTrainModal({ opened, onClose, onBuilt }: BuildTrainModalProps) {
  const { toast } = useToast();
  const [exportTrainNumber, setExportTrainNumber] = useState("");
  const [importTrainNumber, setImportTrainNumber] = useState("");
  const [trainName, setTrainName] = useState("");
  const [yardId, setYardId] = useState("");
  const [locomotiveIds, setLocomotiveIds] = useState<string[]>([]);
  const [notes, setNotes] = useState("");

  const yardsQuery = useQuery(api.routes.yards.queryOptions({ staleTime: 5 * 60_000 }));
  // Admin-managed run list (dropdown settings); numbers already on a train
  // come back disabled so they cannot be picked twice.
  const importNumbers = useImportTrainNumberOptions();
  // Only serviceable locomotives standing in the selected yard, and not already
  // coupled to another built train, can be picked. A new train owns none yet, so
  // no train to keep-exclude.
  const locomotivesQuery = useQuery(
    api.locomotives.listFiltered.queryOptions({
      input: {
        filters: {
          status: "AVAILABLE",
          currentYardId: yardId,
          excludeCoupled: true,
        },
      },
      enabled: Boolean(yardId),
    }),
  );
  const build = useMutation(api.trainBuilder.build.mutationOptions());

  // A locomotive belongs to one yard — switching yards invalidates the pick.
  useEffect(() => {
    setLocomotiveIds([]);
  }, [yardId]);

  // The export run is fixed by the import run, so it tracks it rather than
  // being entered by hand (and clears back to empty when the import is cleared).
  useEffect(() => {
    setExportTrainNumber(exportRunFor(importTrainNumber));
  }, [importTrainNumber]);

  useEffect(() => {
    if (!opened) {
      setExportTrainNumber("");
      setImportTrainNumber("");
      setTrainName("");
      setYardId("");
      setLocomotiveIds([]);
      setNotes("");
    }
  }, [opened]);

  const handleBuild = async () => {
    if (!trainName.trim()) {
      toast({
        title: "Enter the voyage number",
        variant: "destructive",
      });
      return;
    }
    if (!yardId || locomotiveIds.length < 1) {
      toast({
        title: "Pick a yard and couple at least one locomotive",
        variant: "destructive",
      });
      return;
    }
    // Both numbers come from the fixed run pairs, so parity cannot be wrong —
    // only "nothing picked" is reachable here.
    if (!exportTrainNumber || !importTrainNumber) {
      toast({
        title: "Pick an import train number (e.g. 8002) — the export run follows it",
        variant: "destructive",
      });
      return;
    }
    try {
      const composition = await build.mutateAsync({
        exportTrainNumber: exportTrainNumber.trim(),
        importTrainNumber: importTrainNumber.trim(),
        currentYardId: yardId,
        locomotiveIds,
        trainName: trainName.trim(),
        ...(notes.trim() ? { notes: notes.trim() } : {}),
      });
      toast({ title: `Train ${composition.code} built` });
      onClose();
      onBuilt(composition);
    } catch (err) {
      toast({
        title: "Build failed",
        description: parseError(err, "Could not build the train"),
        variant: "destructive",
      });
    }
  };

  const locomotiveOptions = (locomotivesQuery.data ?? []).map((loco) => ({
    value: loco.id,
    label: `${loco.code}${loco.name ? ` — ${loco.name}` : ""} · pulls ${loco.maxPullWeightTons}T`,
  }));

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      title={<Text fw={600}>Build a train</Text>}
      radius="lg"
      centered
    >
      <Stack gap="md">
        <Text size="sm" c="dimmed">
          A train is assembled in one yard: two or more locomotives plus wagons
          standing in that same yard. The train code is assigned automatically;
          wagons are attached on the next screen.
        </Text>
        <TextInput
          label="Voyage number"
          placeholder="Enter voyage number"
          value={trainName}
          onChange={(e) => setTrainName(e.currentTarget.value)}
          maxLength={100}
          required
        />
        <Group grow>
          {/* Fixed by the import run — derived, never typed. */}
          <TextInput
            label="Export train number"
            description="Odd — Ethiopia → Djibouti runs"
            placeholder="e.g. 8001"
            value={exportTrainNumber}
            readOnly
            variant="filled"
          />
          <Select
            label="Import train number"
            description="Even — Djibouti → Ethiopia runs"
            placeholder={importNumbers.isLoading ? "Loading…" : "e.g. 8002"}
            data={importNumbers.options}
            value={importTrainNumber || null}
            onChange={(value) => setImportTrainNumber(value ?? "")}
            searchable
            clearable
            nothingFoundMessage={importNumbers.emptyMessage}
            error={importNumbers.settingMissing ? importNumbers.emptyMessage : undefined}
          />
        </Group>
        <Select
          label="Build yard"
          placeholder="Select the yard the train is assembled in"
          data={(yardsQuery.data ?? []).map((y) => ({
            value: y.id,
            label: y.label ?? y.code,
          }))}
          value={yardId || null}
          onChange={(v) => setYardId(v ?? "")}
          searchable
        />
        <MultiSelect
          label="Locomotives"
          description="A train must be pulled by at least one locomotive. First pick becomes the lead."
          placeholder={yardId ? "Select at least one locomotive" : "Select a yard first"}
          data={locomotiveOptions}
          value={locomotiveIds}
          onChange={setLocomotiveIds}
          searchable
          disabled={!yardId}
          error={
            locomotiveIds.length < 1 ? "Select at least one locomotive" : undefined
          }
          nothingFoundMessage={
            yardId ? "No available locomotives in this yard" : "Select a yard first"
          }
        />
        <Textarea
          label="Notes (optional)"
          value={notes}
          onChange={(e) => setNotes(e.currentTarget.value)}
          autosize
          minRows={2}
        />
        <Group justify="flex-end">
          <Button variant="default" onClick={onClose}>
            Cancel
          </Button>
          <Button loading={build.isPending} onClick={handleBuild}>
            Build train
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}

export interface BuildTrainModalProps {
  opened: boolean;
  onClose: () => void;
  onBuilt: (composition: TrainComposition) => void;
}
