import { Freight } from "@edr/types";
import {
  Badge,
  Box,
  Button,
  Card,
  Divider,
  Grid,
  Group,
  Loader,
  Modal,
  NumberInput,
  Progress,
  Select,
  Slider,
  Stack,
  Text,
  Textarea,
  ThemeIcon,
} from "@mantine/core";
import { useMutation, useQuery } from "@tanstack/react-query";
import { ArrowRight, ArrowRightLeft, CheckCircle2, CircleSlash, Layers, Warehouse } from "lucide-react";
import { useEffect, useMemo, useState } from "react";

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

export interface WagonYardWorkspaceModalProps {
  opened: boolean;
  onClose: () => void;
}

const AVAILABLE = Freight.WagonStatus.Available;
const ASSIGNED = Freight.WagonStatus.Assigned;

const clampInt = (v: number | string, max: number): number => {
  const n = typeof v === "number" ? v : Number(v);
  if (!Number.isFinite(n) || n < 0) return 0;
  return Math.min(Math.floor(n), max);
};

/**
 * NumberInput + Slider + All/Half presets, kept in sync. `max` bounds the field
 * for actions that move real wagons; omit it for a transfer REQUEST, which may
 * legitimately ask for more than the yard holds today (OCC fulfils it in
 * instalments) — the slider then just tracks the current value.
 */
const QuantityField = ({
  value,
  onChange,
  max,
  disabled,
}: {
  value: number;
  onChange: (n: number) => void;
  max?: number;
  disabled?: boolean;
}) => {
  const capped = max ?? Number.MAX_SAFE_INTEGER;
  const set = (v: number | string) => onChange(clampInt(v, capped));
  const off = disabled || max === 0;
  return (
    <Stack gap={8}>
      <Group gap="sm" align="center" wrap="nowrap">
        <NumberInput
          value={value}
          onChange={set}
          min={0}
          max={max}
          allowNegative={false}
          clampBehavior="strict"
          disabled={off}
          radius="md"
          w={92}
        />
        <Slider
          style={{ flex: 1 }}
          value={value}
          onChange={set}
          min={0}
          max={Math.max(max ?? Math.max(value, 10), 1)}
          disabled={off}
          label={(v) => `${v}`}
          color="edr-green"
        />
      </Group>
      <Group gap={6}>
        {/* Presets only make sense against a real ceiling — an uncapped request
            field (transfer ask) shows the manual input alone. */}
        {max != null ? (
          <>
            <Button size="compact-xs" variant="light" color="gray" disabled={off} onClick={() => set(Math.ceil(max / 2))}>
              Half
            </Button>
            <Button size="compact-xs" variant="light" color="gray" disabled={off} onClick={() => set(max)}>
              All ({max})
            </Button>
          </>
        ) : null}
        {value > 0 ? (
          <Button size="compact-xs" variant="subtle" color="gray" onClick={() => set(0)}>
            Clear
          </Button>
        ) : null}
      </Group>
    </Stack>
  );
};

const LegendDot = ({ color, label, value }: { color: string; label: string; value: number }) => (
  <Group gap={6} wrap="nowrap">
    <Box w={10} h={10} style={{ borderRadius: 3, background: `var(--mantine-color-${color}-6)` }} />
    <Text size="sm" c="dimmed">
      {label}
    </Text>
    <Text size="sm" fw={700}>
      {value}
    </Text>
  </Group>
);

/**
 * Bulk yard operations. Pick a yard + wagon type (the two selects filter each
 * other to combinations that actually hold stock), read the live Available /
 * Assigned split, then move a quantity to another yard or flip a quantity
 * between Available and Assigned — replacing one-wagon-at-a-time edits.
 */
const WagonYardWorkspaceModal = ({ opened, onClose }: WagonYardWorkspaceModalProps) => {
  const { toast } = useToast();

  const { data: wagons = [], isLoading } = useQuery(api.wagons.list.queryOptions({ input: {} }));
  const { data: yards = [] } = useQuery(api.routes.yards.queryOptions());
  const { data: wagonTypes = [] } = useQuery(api.wagonTypes.list.queryOptions());

  const [yardId, setYardId] = useState<string | null>(null);
  const [typeId, setTypeId] = useState<string | null>(null);

  const [transferYardId, setTransferYardId] = useState<string | null>(null);
  const [transferQty, setTransferQty] = useState(0);
  const [transferReason, setTransferReason] = useState("");
  const [toAssignedQty, setToAssignedQty] = useState(0);
  const [toAvailableQty, setToAvailableQty] = useState(0);

  const createRequest = useMutation(
    api.wagonTransferRequests.create.mutationOptions(),
  );
  const setStatus = useMutation(api.wagons.bulkSetStatus.mutationOptions());

  const yardName = useMemo(() => {
    const byId = new Map(yards.map((y) => [y.id, y.label || y.code || y.id]));
    return (id: string) => byId.get(id) ?? id;
  }, [yards]);

  const typeInfo = useMemo(() => {
    const byId = new Map(wagonTypes.map((t) => [t.id, t]));
    return {
      label: (id: string) => {
        const t = byId.get(id);
        return t ? `${t.code}${t.name ? ` - ${t.name}` : ""}` : id;
      },
      code: (id: string) => byId.get(id)?.code ?? id,
    };
  }, [wagonTypes]);

  const yardWagons = useMemo(
    () => wagons.filter((w): w is Wagon & { currentYardId: string } => Boolean(w.currentYardId)),
    [wagons],
  );

  const yardOptions = useMemo(() => {
    const ids = new Set<string>();
    for (const w of yardWagons) {
      if (typeId && w.wagonTypeId !== typeId) continue;
      ids.add(w.currentYardId);
    }
    return [...ids]
      .map((id) => ({ value: id, label: yardName(id) }))
      .sort((a, b) => a.label.localeCompare(b.label));
  }, [yardWagons, typeId, yardName]);

  const typeOptions = useMemo(() => {
    const ids = new Set<string>();
    for (const w of yardWagons) {
      if (yardId && w.currentYardId !== yardId) continue;
      ids.add(w.wagonTypeId);
    }
    return [...ids]
      .map((id) => ({ value: id, label: typeInfo.label(id) }))
      .sort((a, b) => a.label.localeCompare(b.label));
  }, [yardWagons, yardId, typeInfo]);

  const matching = useMemo(() => {
    if (!yardId || !typeId) return [] as Wagon[];
    return yardWagons.filter((w) => w.currentYardId === yardId && w.wagonTypeId === typeId);
  }, [yardWagons, yardId, typeId]);

  // Wagons coupled to a built train (trainId set) are managed through the
  // train-builder flow — they can't be bulk-flipped or transferred here, so
  // keep them out of the action pools and bucket them under "Other".
  const availableWagons = useMemo(
    () => matching.filter((w) => w.status === AVAILABLE && !w.trainId),
    [matching],
  );
  const assignedWagons = useMemo(
    () => matching.filter((w) => w.status === ASSIGNED && !w.trainId),
    [matching],
  );
  const otherWagons = useMemo(
    () =>
      matching.filter(
        (w) => w.trainId != null || (w.status !== AVAILABLE && w.status !== ASSIGNED),
      ),
    [matching],
  );
  const total = matching.length;
  const availableCount = availableWagons.length;
  const assignedCount = assignedWagons.length;
  const otherCount = otherWagons.length;
  // Split "Other" so a coupled wagon is visible as such. The Available/Assigned
  // buckets deliberately count only UNCOUPLED wagons (see above), so a yard
  // holding 54 assigned wagons of which 53 are on a train shows "Assigned 1" —
  // accurate for shunting, but unreadable unless the other 53 are named.
  const onTrainCount = useMemo(
    () => matching.filter((w) => w.trainId != null).length,
    [matching],
  );

  const destinationYardOptions = useMemo(
    () =>
      yards
        .filter((y) => y.id !== yardId)
        .map((y) => ({ value: y.id, label: y.label || y.code || y.id }))
        .sort((a, b) => a.label.localeCompare(b.label)),
    [yards, yardId],
  );

  const bothSelected = Boolean(yardId && typeId);

  // Reset action inputs when the selection changes.
  useEffect(() => {
    setTransferYardId(null);
    setTransferQty(0);
    setTransferReason("");
    setToAssignedQty(0);
    setToAvailableQty(0);
  }, [yardId, typeId]);

  // Reset the whole workspace when closed.
  useEffect(() => {
    if (!opened) {
      setYardId(null);
      setTypeId(null);
    }
  }, [opened]);

  // Keep quantities within bounds as counts shift after each action. A TRANSFER
  // REQUEST is deliberately uncapped: OCC delivers in instalments, so asking for
  // 50 where 20 sit today is normal — only the status flips below are bounded by
  // what is physically in the yard.
  useEffect(() => setToAssignedQty((q) => Math.min(q, availableCount)), [availableCount]);
  useEffect(() => setToAvailableQty((q) => Math.min(q, assignedCount)), [assignedCount]);

  const showError = (err: unknown, fallback: string) => {
    const message =
      (err as { response?: { data?: { message?: string } } })?.response?.data?.message ?? fallback;
    toast({ title: fallback, description: String(message), variant: "destructive" });
  };

  // Request-only: the requester specifies count + destination; OCC later picks
  // the physical wagons and executes the move. No wagons are moved here.
  const handleRequest = async () => {
    if (
      !yardId ||
      !typeId ||
      !transferYardId ||
      transferQty < 1 ||
      !transferReason.trim()
    )
      return;
    try {
      await createRequest.mutateAsync({
        fromYardId: yardId,
        toYardId: transferYardId,
        wagonTypeId: typeId,
        quantity: transferQty,
        reason: transferReason.trim(),
      });
      toast({
        title: `Requested ${transferQty} ${typeInfo.code(typeId)} wagon(s) · ${yardName(
          yardId,
        )} → ${yardName(transferYardId)}`,
        description: "OCC will pick the wagons and complete the move.",
      });
      setTransferQty(0);
      setTransferYardId(null);
      setTransferReason("");
    } catch (err) {
      showError(err, "Request failed");
    }
  };

  const handleFlip = async (
    pool: Wagon[],
    qty: number,
    status: Freight.WagonStatus,
    label: string,
    reset: () => void,
  ) => {
    if (qty < 1) return;
    const ids = pool.slice(0, qty).map((w) => w.id);
    if (!ids.length) return;
    try {
      const res = await setStatus.mutateAsync({ wagonIds: ids, status });
      toast({ title: `${res.updated} wagon(s) set to ${label}` });
      reset();
    } catch (err) {
      showError(err, "Status update failed");
    }
  };

  const busy = createRequest.isPending || setStatus.isPending;
  const pct = (n: number) => (total > 0 ? (n / total) * 100 : 0);

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      size="min(1080px, 96vw)"
      radius="lg"
      centered
      overlayProps={{ blur: 2 }}
      title={
        <Group gap="sm">
          <ThemeIcon variant="light" color="edr-green" radius="md" size="lg">
            <Warehouse size={18} />
          </ThemeIcon>
          <div>
            <Text fw={700}>Wagon Yard Operations</Text>
            <Text size="xs" c="dimmed">
              Move and re-status wagons in bulk — no one-by-one edits
            </Text>
          </div>
        </Group>
      }
    >
      <Stack gap="lg">
        {/* ---- Selection ---- */}
        <Card withBorder radius="md" padding="md" bg="var(--mantine-color-gray-0)">
          <Grid gap="md" align="flex-end">
            <Grid.Col span={{ base: 12, sm: 6 }}>
              <Select
                label="Yard"
                placeholder="Select a yard"
                data={yardOptions}
                value={yardId}
                onChange={setYardId}
                searchable
                clearable
                leftSection={<Warehouse size={16} />}
                nothingFoundMessage="No yards with stock"
                radius="md"
              />
            </Grid.Col>
            <Grid.Col span={{ base: 12, sm: 6 }}>
              <Select
                label="Wagon type"
                placeholder="Select a wagon type"
                data={typeOptions}
                value={typeId}
                onChange={setTypeId}
                searchable
                clearable
                leftSection={<Layers size={16} />}
                nothingFoundMessage="No wagon types here"
                radius="md"
              />
            </Grid.Col>
          </Grid>
        </Card>

        {isLoading ? (
          <Group justify="center" p="xl">
            <Loader />
          </Group>
        ) : !bothSelected ? (
          <Card withBorder radius="md" padding="xl">
            <Stack align="center" gap={6}>
              <ThemeIcon variant="light" color="gray" radius="xl" size={48}>
                <Layers size={22} />
              </ThemeIcon>
              <Text fw={600}>Pick a yard and a wagon type</Text>
              <Text size="sm" c="dimmed" ta="center" maw={420}>
                You&apos;ll see how many wagons of that type sit in that yard, how many are available
                vs assigned, and can move or re-status them all at once.
              </Text>
            </Stack>
          </Card>
        ) : (
          <>
            {/* ---- Overview hero ---- */}
            <Card withBorder radius="md" padding="lg">
              <Group justify="space-between" align="flex-start" wrap="wrap" gap="md">
                <Group gap="lg" align="center" wrap="nowrap">
                  <div>
                    <Text size="3rem" fw={800} lh={1}>
                      {total}
                    </Text>
                  </div>
                  <div>
                    <Text fw={700} size="lg">
                      {typeInfo.code(typeId!)} wagons
                    </Text>
                    <Group gap={6} c="dimmed">
                      <Warehouse size={14} />
                      <Text size="sm">{yardName(yardId!)}</Text>
                    </Group>
                  </div>
                </Group>
                <Group gap="lg" wrap="wrap">
                  <LegendDot color="teal" label="Available in yard" value={availableCount} />
                  <LegendDot color="blue" label="Assigned in yard" value={assignedCount} />
                  {onTrainCount > 0 ? (
                    <LegendDot color="gray" label="On train" value={onTrainCount} />
                  ) : null}
                  {otherCount - onTrainCount > 0 ? (
                    <LegendDot color="gray" label="Other" value={otherCount - onTrainCount} />
                  ) : null}
                </Group>
              </Group>

              <Progress.Root size={22} radius="md" mt="md">
                <Progress.Section value={pct(availableCount)} color="teal">
                  {availableCount > 0 ? <Progress.Label>{availableCount}</Progress.Label> : null}
                </Progress.Section>
                <Progress.Section value={pct(assignedCount)} color="blue">
                  {assignedCount > 0 ? <Progress.Label>{assignedCount}</Progress.Label> : null}
                </Progress.Section>
                <Progress.Section value={pct(otherCount)} color="gray">
                  {otherCount > 0 ? <Progress.Label>{otherCount}</Progress.Label> : null}
                </Progress.Section>
              </Progress.Root>
            </Card>

            {/* ---- Actions ---- */}
            <Grid gap="lg">
              {/* Transfer */}
              <Grid.Col span={{ base: 12, md: 6 }}>
                <Card withBorder radius="md" h="100%" padding="lg">
                  <Group gap="xs" mb={4}>
                    <ThemeIcon variant="light" color="grape" radius="md" size="md">
                      <ArrowRightLeft size={16} />
                    </ThemeIcon>
                    <Text fw={700}>Request transfer to another yard</Text>
                  </Group>
                  <Text size="xs" c="dimmed" mb="md">
                    Sends a request to OCC — they pick the wagons and complete the move.
                  </Text>
                  <Stack gap="md">
                    <div>
                      <Group justify="space-between" mb={4}>
                        <Text size="sm" fw={500}>
                          How many wagons
                        </Text>
                        <Badge color="teal" variant="light">
                          {availableCount} available
                        </Badge>
                      </Group>
                      {/* No max: the request may exceed what the yard holds
                          today — OCC fulfils it in instalments. */}
                      <QuantityField value={transferQty} onChange={setTransferQty} />
                    </div>
                    <Select
                      label="Destination yard"
                      placeholder="Select destination"
                      data={destinationYardOptions}
                      value={transferYardId}
                      onChange={setTransferYardId}
                      searchable
                      radius="md"
                    />
                    <Textarea
                      label="Reason"
                      placeholder="Why are these wagons needed?"
                      value={transferReason}
                      onChange={(e) => setTransferReason(e.currentTarget.value)}
                      required
                      autosize
                      minRows={2}
                      radius="md"
                    />
                    {transferYardId && transferQty > 0 ? (
                      <Card bg="var(--mantine-color-gray-0)" radius="md" padding="sm" withBorder>
                        <Group gap={8} wrap="nowrap">
                          <Text size="sm" fw={600}>
                            {yardName(yardId!)} {total}
                            <Text span c="red.6" fw={700}>
                              {" "}
                              −{transferQty}
                            </Text>
                          </Text>
                          <ArrowRight size={16} />
                          <Text size="sm" fw={600}>
                            {yardName(transferYardId)}
                            <Text span c="teal.7" fw={700}>
                              {" "}
                              +{transferQty}
                            </Text>
                          </Text>
                        </Group>
                      </Card>
                    ) : null}
                    <Button
                      leftSection={<ArrowRightLeft size={16} />}
                      onClick={handleRequest}
                      loading={createRequest.isPending}
                      disabled={
                        busy ||
                        !transferYardId ||
                        transferQty < 1 ||
                        !transferReason.trim()
                      }
                      color="edr-green"
                    >
                      Request {transferQty > 0 ? `${transferQty} ` : ""}wagon
                      {transferQty === 1 ? "" : "s"}
                    </Button>
                  </Stack>
                </Card>
              </Grid.Col>

              {/* Re-status */}
              <Grid.Col span={{ base: 12, md: 6 }}>
                <Card withBorder radius="md" h="100%" padding="lg">
                  <Group gap="xs" mb="md">
                    <ThemeIcon variant="light" color="orange" radius="md" size="md">
                      <ArrowRightLeft size={16} />
                    </ThemeIcon>
                    <Text fw={700}>Change availability</Text>
                  </Group>
                  <Stack gap="lg">
                    <Box>
                      <Group justify="space-between" mb={6}>
                        <Group gap={6}>
                          <ThemeIcon variant="light" color="blue" radius="sm" size="sm">
                            <CircleSlash size={12} />
                          </ThemeIcon>
                          <Text size="sm" fw={600}>
                            Available → Assigned
                          </Text>
                        </Group>
                        <Badge color="teal" variant="light">
                          {availableCount} free
                        </Badge>
                      </Group>
                      <QuantityField
                        value={toAssignedQty}
                        onChange={setToAssignedQty}
                        max={availableCount}
                      />
                      <Button
                        mt="sm"
                        fullWidth
                        variant="light"
                        color="blue"
                        disabled={busy || toAssignedQty < 1}
                        loading={setStatus.isPending}
                        onClick={() =>
                          handleFlip(availableWagons, toAssignedQty, ASSIGNED, "Assigned", () =>
                            setToAssignedQty(0),
                          )
                        }
                      >
                        Assign {toAssignedQty > 0 ? `${toAssignedQty} ` : ""}wagon
                        {toAssignedQty === 1 ? "" : "s"}
                      </Button>
                    </Box>

                    <Divider variant="dashed" />

                    <Box>
                      <Group justify="space-between" mb={6}>
                        <Group gap={6}>
                          <ThemeIcon variant="light" color="teal" radius="sm" size="sm">
                            <CheckCircle2 size={12} />
                          </ThemeIcon>
                          <Text size="sm" fw={600}>
                            Assigned → Available
                          </Text>
                        </Group>
                        <Badge color="blue" variant="light">
                          {assignedCount} assigned
                        </Badge>
                      </Group>
                      <QuantityField
                        value={toAvailableQty}
                        onChange={setToAvailableQty}
                        max={assignedCount}
                      />
                      <Button
                        mt="sm"
                        fullWidth
                        variant="light"
                        color="teal"
                        disabled={busy || toAvailableQty < 1}
                        loading={setStatus.isPending}
                        onClick={() =>
                          handleFlip(assignedWagons, toAvailableQty, AVAILABLE, "Available", () =>
                            setToAvailableQty(0),
                          )
                        }
                      >
                        Free up {toAvailableQty > 0 ? `${toAvailableQty} ` : ""}wagon
                        {toAvailableQty === 1 ? "" : "s"}
                      </Button>
                    </Box>
                  </Stack>
                </Card>
              </Grid.Col>
            </Grid>
          </>
        )}
      </Stack>
    </Modal>
  );
};

export default WagonYardWorkspaceModal;
