import { FormEvent, useMemo, useState } from "react";
import {
  ArrowRight,
  Ban,
  CircleCheck,
  Edit,
  Eye,
  Plus,
  Route as RouteIcon,
  Trash2,
} from "lucide-react";
import type { ColumnDef } from "@edr/ui-common";
import {
  ActionIcon,
  Badge,
  Box,
  Button,
  Card,
  Divider,
  Group,
  Modal,
  Select,
  SimpleGrid,
  Stack,
  Text,
  ThemeIcon,
  Tooltip,
} from "@mantine/core";

import { useMutation, useQuery } from "@tanstack/react-query";

import { KpiStrip, PageContainer, PageHeader } from "@/components/page";
import FleetToolbar from "@/components/fleet/FleetToolbar";
import { useFleetViewMode } from "@/components/fleet/useFleetViewMode";
import RuleEngineListFooter from "@/components/ruleEngine/RuleEngineListFooter";
import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
import { api } from "@/services/api";
import { ruleEngineService } from "@/services/ruleEngine/ruleEngine.service";
import { useAuth } from "@/auth/useAuth";
import { canFleetAction } from "@/lib/permissions";
import { useToast } from "@/hooks/use-toast";
import {
  formatRouteLabel,
  ROUTE_STATUS_OPTIONS,
  totalRouteDistanceKm,
  type RouteRecord,
  type RouteStatus,
  type YardRef,
} from "@/services/routes.service";
import { DataTable, DataTableFooter, usePagination } from "@edr/ui-common";

type MilestoneFormRow = { yardId: string };

type RouteFormState = {
  status: RouteStatus;
  milestones: MilestoneFormRow[];
};

const emptyForm = (): RouteFormState => ({
  status: "AVAILABLE",
  milestones: [{ yardId: "" }, { yardId: "" }],
});

/** Order-insensitive pair key — yard distances are symmetric. */
const pairKey = (a: string, b: string) => (a < b ? `${a}|${b}` : `${b}|${a}`);

const yardLabel = (yard?: YardRef | null) =>
  yard ? `${yard.label} (${yard.code})` : "—";

const statusColor = (status: RouteStatus) => {
  switch (status) {
    case "AVAILABLE":
      return "edr-green";
    case "MAINTENANCE":
      return "yellow";
    case "DAMAGED":
      return "red";
    case "STOP_WORKING":
      return "gray";
    default:
      return "gray";
  }
};

const statusLabel = (status: RouteStatus) =>
  ROUTE_STATUS_OPTIONS.find((o) => o.value === status)?.label ?? status;

const normalizeRouteError = (error: unknown) => {
  const responseData = (error as { response?: { data?: unknown } })?.response?.data;
  const data =
    responseData && typeof responseData === "object"
      ? (responseData as Record<string, unknown>)
      : undefined;
  const rawMessage = data?.message ?? data?.error ?? (error as Error)?.message;
  return Array.isArray(rawMessage)
    ? rawMessage.join(", ")
    : rawMessage
      ? String(rawMessage)
      : "Save failed";
};

function RouteTimeline({ route }: { route: RouteRecord }) {
  const stops = [...(route.milestones ?? [])].sort(
    (a, b) => a.sequenceNo - b.sequenceNo,
  );
  const total = totalRouteDistanceKm(route);

  return (
    <Stack gap="sm">
      {stops.map((milestone, index) => {
        const label =
          milestone.yard?.label ?? milestone.yard?.code ?? milestone.yardId;
        const role =
          index === 0
            ? "Origin"
            : index === stops.length - 1
              ? "Destination"
              : `Milestone ${index}`;
        const km = Number(milestone.distanceKm ?? 0);
        return (
          <Box key={milestone.id ?? `${milestone.yardId}-${index}`}>
            {index > 0 && (
              <Group gap={8} pl={18} py={6}>
                <ThemeIcon size={22} radius="xl" variant="light" color="gray">
                  <ArrowRight size={12} />
                </ThemeIcon>
                <Text size="xs" c="dimmed" fw={600}>
                  {km} km
                </Text>
              </Group>
            )}
            <Group gap="sm" wrap="nowrap">
              <Badge size="sm" variant="light" color={index === 0 ? "teal" : "gray"}>
                {role}
              </Badge>
              <Text size="sm" fw={500}>
                {label}
              </Text>
            </Group>
          </Box>
        );
      })}
      <Divider />
      <Group justify="space-between">
        <Text size="sm" fw={600}>
          Total distance
        </Text>
        <Text size="sm" fw={700}>
          {total} km
        </Text>
      </Group>
    </Stack>
  );
}

export default function RoutesPage() {
  const [search, setSearch] = useState("");
  const [formOpen, setFormOpen] = useState(false);
  const [viewing, setViewing] = useState<RouteRecord | null>(null);
  const [editing, setEditing] = useState<RouteRecord | null>(null);
  const [form, setForm] = useState<RouteFormState>(emptyForm());
  const { viewMode, setViewMode } = useFleetViewMode("routes");
  const { pagination, setPagination } = usePagination({ pageSize: 10 });
  const { toast } = useToast();
  const { user } = useAuth();
  const canCreate = canFleetAction(user, "routes", "create");
  const canUpdate = canFleetAction(user, "routes", "update");
  const canDelete = canFleetAction(user, "routes", "delete");

  const routesQuery = useQuery(api.routes.list.queryOptions());
  const yardsQuery = useQuery(api.routes.yards.queryOptions());
  // Segment km are configured in Configuration → Yard Distances and resolved
  // by the API on save; this fetch is only to preview them in the form.
  const yardDistancesQuery = useQuery({
    queryKey: ["yard-distances", "all"],
    queryFn: () =>
      ruleEngineService.listAll<{ id: string; fromYardId: string; toYardId: string; distanceKm: string }>(
        "yard-distances",
      ),
  });
  const createMutation = useMutation(api.routes.create.mutationOptions());
  const updateMutation = useMutation(api.routes.update.mutationOptions());
  const deactivateMutation = useMutation(api.routes.deactivate.mutationOptions());

  const filteredRoutes = useMemo(() => {
    const query = search.trim().toLowerCase();
    if (!query) return routesQuery.data ?? [];
    return (routesQuery.data ?? []).filter((route) => {
      const searchable = [
        formatRouteLabel(route),
        route.originYard?.label,
        route.originYard?.code,
        route.destinationYard?.label,
        route.destinationYard?.code,
        ...(route.milestones ?? []).map(
          (m) => m.yard?.label ?? m.yard?.code ?? m.yardId,
        ),
      ]
        .filter(Boolean)
        .join(" ")
        .toLowerCase();
      return searchable.includes(query);
    });
  }, [routesQuery.data, search]);

  const pageCount = Math.max(1, Math.ceil(filteredRoutes.length / pagination.pageSize));
  const pagedRoutes = useMemo(() => {
    const start = pagination.pageIndex * pagination.pageSize;
    return filteredRoutes.slice(start, start + pagination.pageSize);
  }, [filteredRoutes, pagination.pageIndex, pagination.pageSize]);

  const allRoutes = routesQuery.data ?? [];
  const availableCount = allRoutes.filter((r) => r.status === "AVAILABLE").length;

  const yardOptions = useMemo(
    () =>
      (yardsQuery.data ?? []).map((yard) => ({
        value: yard.id,
        label: `${yard.label} (${yard.code})`,
      })),
    [yardsQuery.data],
  );

  const distanceByPair = useMemo(() => {
    const map = new Map<string, number>();
    for (const row of yardDistancesQuery.data ?? []) {
      map.set(pairKey(row.fromYardId, row.toYardId), Number(row.distanceKm));
    }
    return map;
  }, [yardDistancesQuery.data]);

  /** Configured km for the segment ending at `index` (undefined = pair not configured yet). */
  const segmentKm = (index: number): number | undefined => {
    if (index === 0) return 0;
    const from = form.milestones[index - 1]?.yardId;
    const to = form.milestones[index]?.yardId;
    if (!from || !to) return undefined;
    return distanceByPair.get(pairKey(from, to));
  };

  const formTotalKm = useMemo(() => {
    let total = 0;
    for (let i = 1; i < form.milestones.length; i++) {
      const from = form.milestones[i - 1]?.yardId;
      const to = form.milestones[i]?.yardId;
      if (!from || !to) continue;
      total += distanceByPair.get(pairKey(from, to)) ?? 0;
    }
    return total;
  }, [form.milestones, distanceByPair]);

  const resetForm = () => {
    setFormOpen(false);
    setEditing(null);
    setForm(emptyForm());
  };

  const openCreate = () => {
    setEditing(null);
    setForm(emptyForm());
    setFormOpen(true);
  };

  const openEdit = (route: RouteRecord) => {
    setEditing(route);
    setForm({
      status: route.status,
      milestones: [...(route.milestones ?? [])]
        .sort((a, b) => a.sequenceNo - b.sequenceNo)
        .map((m) => ({ yardId: m.yardId })),
    });
    setFormOpen(true);
  };

  const setMilestone = (index: number, patch: Partial<MilestoneFormRow>) => {
    setForm((current) => ({
      ...current,
      milestones: current.milestones.map((row, i) =>
        i === index ? { ...row, ...patch } : row,
      ),
    }));
  };

  const addMilestone = () => {
    setForm((current) => ({
      ...current,
      milestones: [...current.milestones, { yardId: "" }],
    }));
  };

  const removeMilestone = (index: number) => {
    setForm((current) => ({
      ...current,
      milestones: current.milestones.filter((_, i) => i !== index),
    }));
  };

  const buildPayload = () => ({
    status: form.status,
    milestones: form.milestones.map((row) => ({ yardId: row.yardId })),
  });

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();
    if (form.milestones.length < 2 || form.milestones.some((row) => !row.yardId)) {
      toast({
        title: "Save failed",
        description: "Select at least an origin and destination yard",
        variant: "destructive",
      });
      return;
    }
    // Pre-empt the API's missing-pair rejection with a readable message; if the
    // distance list failed to load, skip and let the API validate.
    if (yardDistancesQuery.data) {
      const missing: string[] = [];
      for (let i = 1; i < form.milestones.length; i++) {
        const from = form.milestones[i - 1].yardId;
        const to = form.milestones[i].yardId;
        if (!distanceByPair.has(pairKey(from, to))) {
          const label = (id: string) =>
            yardOptions.find((o) => o.value === id)?.label ?? id;
          missing.push(`${label(from)} ↔ ${label(to)}`);
        }
      }
      if (missing.length > 0) {
        toast({
          title: "Save failed",
          description: `No distance configured for: ${missing.join(", ")}. Add it under Configuration → Yard Distances first.`,
          variant: "destructive",
        });
        return;
      }
    }

    try {
      const payload = buildPayload();
      if (editing) {
        await updateMutation.mutateAsync({ id: editing.id, data: payload });
        toast({ title: "Route updated" });
      } else {
        await createMutation.mutateAsync(payload);
        toast({ title: "Route created" });
      }
      resetForm();
    } catch (error) {
      toast({ title: "Save failed", description: normalizeRouteError(error), variant: "destructive" });
    }
  };

  const handleDeactivate = async (route: RouteRecord) => {
    try {
      await deactivateMutation.mutateAsync(route.id);
      toast({ title: "Route marked stop working" });
    } catch {
      toast({ title: "Update failed", description: "Could not update route status", variant: "destructive" });
    }
  };

  const handleStatusChange = async (route: RouteRecord, status: RouteStatus) => {
    try {
      await updateMutation.mutateAsync({ id: route.id, data: { status } });
      setViewing((current) => (current?.id === route.id ? { ...current, status } : current));
      toast({ title: "Status updated" });
    } catch (error) {
      toast({ title: "Update failed", description: normalizeRouteError(error), variant: "destructive" });
    }
  };

  const isSaving = createMutation.isPending || updateMutation.isPending;

  const availableOptionsForIndex = (index: number) => {
    const selectedByOthers = new Set(
      form.milestones
        .filter((row, i) => i !== index && row.yardId)
        .map((row) => row.yardId),
    );
    return yardOptions.filter(
      (option) =>
        option.value === form.milestones[index]?.yardId ||
        !selectedByOthers.has(option.value),
    );
  };

  const tableStatus = routesQuery.isLoading
    ? "loading"
    : routesQuery.isError
      ? "error"
      : "success";

  const columns = useMemo((): ColumnDef<RouteRecord>[] => {
    const headerClassName = ruleEngineTable.headerCell;
    const cellClassName = ruleEngineTable.bodyCell;
    return [
      {
        id: "corridor",
        header: "Corridor",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => (
          <Text fw={600} size="sm">
            {formatRouteLabel(row.original)}
          </Text>
        ),
      },
      {
        id: "origin",
        header: "Origin",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => yardLabel(row.original.originYard),
      },
      {
        id: "destination",
        header: "Destination",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => yardLabel(row.original.destinationYard),
      },
      {
        id: "distance",
        header: "Total KM",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => `${totalRouteDistanceKm(row.original)} km`,
      },
      {
        id: "milestones",
        header: "Stops",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => row.original.milestones?.length ?? 0,
      },
      {
        id: "status",
        header: "Status",
        meta: { headerClassName, cellClassName },
        cell: ({ row }) => (
          <Badge color={statusColor(row.original.status)} variant="light" size="sm">
            {statusLabel(row.original.status)}
          </Badge>
        ),
      },
      {
        id: "actions",
        header: "Actions",
        meta: { headerClassName, cellClassName: `${cellClassName} whitespace-nowrap` },
        cell: ({ row }) => (
          <Group gap={4} justify="flex-end" wrap="nowrap">
            <Tooltip label="View">
              <ActionIcon variant="subtle" color="gray" onClick={() => setViewing(row.original)}>
                <Eye size={16} />
              </ActionIcon>
            </Tooltip>
            {canUpdate ? (
              <Tooltip label="Edit">
                <ActionIcon variant="subtle" color="gray" onClick={() => openEdit(row.original)}>
                  <Edit size={16} />
                </ActionIcon>
              </Tooltip>
            ) : null}
            {canDelete ? (
              <Tooltip label="Mark stop working">
                <ActionIcon
                  variant="subtle"
                  color="red"
                  disabled={row.original.status === "STOP_WORKING" || deactivateMutation.isPending}
                  onClick={() => handleDeactivate(row.original)}
                >
                  <Trash2 size={16} />
                </ActionIcon>
              </Tooltip>
            ) : null}
          </Group>
        ),
      },
    ];
  }, [deactivateMutation.isPending, canUpdate, canDelete]);

  return (
    <PageContainer>
      <PageHeader
        title="Routes"
        subtitle="Define rail corridors, segment distances, and operational status for train scheduling."
        action={
          canCreate ? (
            <Button leftSection={<Plus size={18} />} onClick={openCreate}>
              Add route
            </Button>
          ) : undefined
        }
      />

      <KpiStrip
        loading={routesQuery.isLoading}
        items={[
          { label: "Total routes", value: allRoutes.length, icon: RouteIcon },
          { label: "Available", value: availableCount, icon: CircleCheck, color: "edr-green" },
          {
            label: "Unavailable",
            value: allRoutes.length - availableCount,
            icon: Ban,
            color: "gray",
          },
        ]}
      />

      <Card radius="lg" padding={0} withBorder>
        <Stack gap={0}>
          <Box px="md" pt="md" pb="sm" w="100%">
            <FleetToolbar
              search={search}
              onSearchChange={setSearch}
              searchPlaceholder="Search corridors…"
              viewMode={viewMode}
              onViewModeChange={setViewMode}
            />
          </Box>

          {viewMode === "table" ? (
            <DataTable
              columns={columns}
              data={pagedRoutes}
              status={tableStatus}
              emptyMessage="No routes found"
              pagination={{
                pageIndex: pagination.pageIndex,
                pageSize: pagination.pageSize,
                pageCount,
                totalCount: filteredRoutes.length,
              }}
              tableOptions={{
                manualPagination: true,
                pageCount,
                state: { pagination },
                onPaginationChange: setPagination,
              }}
              containerClassName="border-0 shadow-none bg-transparent"
              footer={({ table, pagination: footerPagination }) => (
                <DataTableFooter
                  table={table}
                  pagination={footerPagination}
                  options={{ labels: { items: "routes" } }}
                />
              )}
            />
          ) : (
            <Stack gap={0}>
              {tableStatus === "loading" ? (
                <Text py="xl" ta="center" c="dimmed" size="sm">
                  Loading…
                </Text>
              ) : !pagedRoutes.length ? (
                <Text py="xl" ta="center" c="dimmed" size="sm">
                  No routes found
                </Text>
              ) : (
                <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" p="md">
                  {pagedRoutes.map((route) => (
                    <Card key={route.id} radius="lg" padding="lg" withBorder>
                      <Stack gap="sm">
                        <Group justify="space-between">
                          <Text fw={600}>{formatRouteLabel(route)}</Text>
                          <Badge color={statusColor(route.status)} variant="light" size="sm">
                            {statusLabel(route.status)}
                          </Badge>
                        </Group>
                        <Text size="sm" c="dimmed">
                          {totalRouteDistanceKm(route)} km · {route.milestones?.length ?? 0} stops
                        </Text>
                        <Group gap={6} justify="flex-end">
                          <Button variant="light" size="compact-sm" onClick={() => setViewing(route)}>
                            View
                          </Button>
                          {canUpdate ? (
                            <Button variant="light" size="compact-sm" onClick={() => openEdit(route)}>
                              Edit
                            </Button>
                          ) : null}
                        </Group>
                      </Stack>
                    </Card>
                  ))}
                </SimpleGrid>
              )}
              <RuleEngineListFooter
                pagination={pagination}
                pageCount={pageCount}
                totalCount={filteredRoutes.length}
                itemLabel="routes"
                onPaginationChange={setPagination}
              />
            </Stack>
          )}
        </Stack>
      </Card>

      <Modal
        opened={formOpen}
        onClose={resetForm}
        title={<Text fw={600}>{editing ? "Edit Route" : "Add Route"}</Text>}
        size="lg"
        radius="lg"
        centered
      >
        <form onSubmit={handleSubmit}>
          <Stack gap="md">
            {editing && (
              <Select
                label="Status"
                data={ROUTE_STATUS_OPTIONS}
                value={form.status}
                onChange={(value) =>
                  value && setForm((current) => ({ ...current, status: value as RouteStatus }))
                }
              />
            )}
            <Group justify="space-between">
              <Text size="sm" fw={500}>
                Stops & segment distances
              </Text>
              <Button type="button" variant="light" size="compact-sm" onClick={addMilestone}>
                Add milestone
              </Button>
            </Group>
            {form.milestones.map((row, index) => {
              const role =
                index === 0
                  ? "Origin"
                  : index === form.milestones.length - 1
                    ? "Destination"
                    : "Milestone";
              const km = segmentKm(index);
              const bothSelected =
                index > 0 && Boolean(row.yardId && form.milestones[index - 1]?.yardId);
              return (
                <Group key={`${role}-${index}`} align="center" wrap="nowrap" gap="sm">
                  <Text w={90} size="sm" fw={500}>
                    {role}
                  </Text>
                  <Select
                    style={{ flex: 1 }}
                    data={availableOptionsForIndex(index)}
                    value={row.yardId || null}
                    onChange={(value) => value && setMilestone(index, { yardId: value })}
                    placeholder="Select yard"
                    searchable
                  />
                  {index > 0 ? (
                    <Box w={120}>
                      {bothSelected ? (
                        km != null ? (
                          <Text size="sm" fw={600} ta="right">
                            {km} km
                          </Text>
                        ) : (
                          <Tooltip label="No distance configured for this yard pair — add it under Configuration → Yard Distances">
                            <Text size="xs" c="red.7" fw={600} ta="right">
                              Not configured
                            </Text>
                          </Tooltip>
                        )
                      ) : (
                        <Text size="xs" c="dimmed" ta="right">
                          — km
                        </Text>
                      )}
                    </Box>
                  ) : (
                    <Box w={120} />
                  )}
                  <ActionIcon
                    variant="subtle"
                    color="red"
                    disabled={form.milestones.length <= 2}
                    onClick={() => removeMilestone(index)}
                  >
                    <Trash2 size={16} />
                  </ActionIcon>
                </Group>
              );
            })}
            <Text size="sm" c="dimmed">
              Total route distance: <strong>{formTotalKm} km</strong> — segment
              distances come from Configuration → Yard Distances
            </Text>
            <Group justify="flex-end">
              <Button variant="default" type="button" onClick={resetForm}>
                Cancel
              </Button>
              <Button color="edr-green" type="submit" loading={isSaving}>
                Save
              </Button>
            </Group>
          </Stack>
        </form>
      </Modal>

      <Modal
        opened={Boolean(viewing)}
        onClose={() => setViewing(null)}
        title={<Text fw={600}>{viewing ? formatRouteLabel(viewing) : "Route details"}</Text>}
        radius="lg"
        centered
        size="md"
      >
        {viewing ? (
          <Stack gap="md">
            <Group justify="space-between" align="flex-end">
              <div>
                <Text size="sm" fw={500}>
                  Status
                </Text>
                <Badge mt={4} color={statusColor(viewing.status)} variant="light">
                  {statusLabel(viewing.status)}
                </Badge>
              </div>
              <Select
                w={200}
                label="Update status"
                data={ROUTE_STATUS_OPTIONS}
                value={viewing.status}
                onChange={(value) =>
                  value && handleStatusChange(viewing, value as RouteStatus)
                }
              />
            </Group>
            <div>
              <Text size="sm" fw={500} mb={8}>
                Road timeline
              </Text>
              <RouteTimeline route={viewing} />
            </div>
          </Stack>
        ) : null}
      </Modal>
    </PageContainer>
  );
}
