import { memo, useMemo, useState } from "react";
import {
  ActionIcon,
  Badge,
  Box,
  Button,
  Checkbox,
  Group,
  Menu,
  Modal,
  Paper,
  Select,
  Stack,
  Text,
  TextInput,
  Tooltip,
} from "@mantine/core";
import {
  AlertTriangle,
  MoreVertical,
  PackagePlus,
  Search,
  Trash2,
  X,
} from "lucide-react";
import { useMutation } from "@tanstack/react-query";

import { DataTable, type ColumnDef } from "@edr/ui-common";

import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
import type {
  BatchBoardBookingDetail,
  BatchBoardBookingState,
  BookingAllocationStatus,
} from "@/types/trainScheduling";

const cellMeta = {
  headerClassName: ruleEngineTable.headerCell,
  cellClassName: ruleEngineTable.bodyCell,
};

const fmtTons = (n: number) =>
  `${n.toLocaleString(undefined, { maximumFractionDigits: 1 })} t`;

const fmtDateTime = (iso: string | null) =>
  iso
    ? new Intl.DateTimeFormat("en-GB", {
        day: "2-digit",
        month: "short",
        hour: "2-digit",
        minute: "2-digit",
        hour12: false,
        timeZone: "Africa/Addis_Ababa",
      }).format(new Date(iso))
    : "—";

const initials = (name: string) =>
  name
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((w) => w[0])
    .join("")
    .toUpperCase() || "?";

const STATE_META: Record<
  BatchBoardBookingState,
  { label: string; color: string }
> = {
  ALLOCATED: { label: "Allocated", color: "edr-green" },
  SELECTED_FOR_BATCH: { label: "Selected for batch", color: "orange" },
  READY: { label: "Ready for batch", color: "teal" },
  WAITING: { label: "Paid · waiting", color: "blue" },
  PENDING_CONTRACT: { label: "Pending contract", color: "gray" },
  EXPIRED: { label: "Expired", color: "red" },
};

const ALLOC_META: Record<
  BookingAllocationStatus,
  { label: string; color: string }
> = {
  ASSIGNED: { label: "Wagons assigned", color: "edr-green" },
  NOT_ATTEMPTED: { label: "Not allocated", color: "gray" },
  DEFERRED: { label: "Deferred", color: "orange" },
  FAILED: { label: "Allocation failed", color: "red" },
};

const STATE_FILTERS = [
  { value: "ALL", label: "All states" },
  ...Object.entries(STATE_META).map(([value, m]) => ({
    value,
    label: m.label,
  })),
];

const ALLOC_FILTERS = [
  { value: "ALL", label: "All allocations" },
  ...Object.entries(ALLOC_META).map(([value, m]) => ({
    value,
    label: m.label,
  })),
];

export interface BookingsManagerProps {
  scheduleId: string;
  bookings: BatchBoardBookingDetail[];
  /** Re-pull the batch-board detail after a remove / re-assign mutation. */
  onChanged: () => void;
  /** Read-only when the schedule can no longer be edited (dispatched / arrived). */
  readOnly?: boolean;
}

/**
 * Searchable, filterable, bulk-manageable booking table for the batch board.
 * Staff can search by reference / customer, filter by batch state and wagon
 * allocation status, and remove or re-assign bookings individually or in bulk.
 * Wraps the shared DataTable; selection + actions are handled locally so the
 * surrounding accordion / tab layout stays untouched.
 *
 * Memoized: the detail page passes stable props (memoized bookings array +
 * useCallback onChanged), so its unrelated re-renders skip this subtree.
 */
export const BookingsManager = memo(function BookingsManager({
  scheduleId,
  bookings,
  onChanged,
  readOnly = false,
}: BookingsManagerProps) {
  const { toast } = useToast();
  const [query, setQuery] = useState("");
  const [stateFilter, setStateFilter] = useState<string>("ALL");
  const [allocFilter, setAllocFilter] = useState<string>("ALL");
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [confirm, setConfirm] = useState<
    | { kind: "remove"; ids: string[]; label: string }
    | { kind: "reassign"; ids: string[]; label: string }
    | null
  >(null);

  const unassign = useMutation(
    api.trainScheduling.unassignBooking.mutationOptions(),
  );
  const reassign = useMutation(
    api.trainScheduling.assignUnassignedBooking.mutationOptions(),
  );
  const busy = unassign.isPending || reassign.isPending;

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    return bookings.filter((b) => {
      if (stateFilter !== "ALL" && b.state !== stateFilter) return false;
      if (allocFilter !== "ALL" && b.allocationStatus !== allocFilter)
        return false;
      if (!q) return true;
      return (
        b.reference.toLowerCase().includes(q) ||
        b.company.toLowerCase().includes(q)
      );
    });
  }, [bookings, query, stateFilter, allocFilter]);

  // Selection is bounded to whatever is currently visible (filtered) to avoid
  // acting on rows the user can't see.
  const visibleIds = useMemo(() => filtered.map((b) => b.id), [filtered]);
  const selectedVisible = useMemo(
    () => visibleIds.filter((id) => selected.has(id)),
    [visibleIds, selected],
  );
  const allVisibleSelected =
    visibleIds.length > 0 && selectedVisible.length === visibleIds.length;
  const someVisibleSelected =
    selectedVisible.length > 0 && !allVisibleSelected;

  const toggleAll = () =>
    setSelected((prev) => {
      const next = new Set(prev);
      if (allVisibleSelected) {
        visibleIds.forEach((id) => next.delete(id));
      } else {
        visibleIds.forEach((id) => next.add(id));
      }
      return next;
    });

  const toggleOne = (id: string) =>
    setSelected((prev) => {
      const next = new Set(prev);
      next.has(id) ? next.delete(id) : next.add(id);
      return next;
    });

  const clearSelection = () => setSelected(new Set());

  const runRemove = async (ids: string[]) => {
    let ok = 0;
    let failed = 0;
    // Sequential — each unassign mutates the schedule graph; parallel would race.
    for (const id of ids) {
      try {
        await unassign.mutateAsync({ id: scheduleId, bookingId: id });
        ok += 1;
      } catch {
        failed += 1;
      }
    }
    toast({
      title: "Bookings removed",
      description: `${ok} removed${failed ? ` · ${failed} failed` : ""}`,
      variant: failed ? "destructive" : "default",
    });
    clearSelection();
    onChanged();
  };

  const runReassign = async (ids: string[]) => {
    let ok = 0;
    let failed = 0;
    for (const id of ids) {
      try {
        await reassign.mutateAsync({ id: scheduleId, bookingId: id });
        ok += 1;
      } catch {
        failed += 1;
      }
    }
    toast({
      title: "Re-assignment run",
      description: `${ok} re-assigned${failed ? ` · ${failed} failed` : ""}`,
      variant: failed ? "destructive" : "default",
    });
    clearSelection();
    onChanged();
  };

  const confirmAction = async () => {
    if (!confirm) return;
    const ids = confirm.ids;
    setConfirm(null);
    if (confirm.kind === "remove") await runRemove(ids);
    else await runReassign(ids);
  };

  const columns = useMemo<ColumnDef<BatchBoardBookingDetail>[]>(() => {
    const cols: ColumnDef<BatchBoardBookingDetail>[] = [];

    if (!readOnly) {
      cols.push({
        id: "select",
        meta: cellMeta,
        header: () => (
          <Checkbox
            size="xs"
            aria-label="Select all"
            checked={allVisibleSelected}
            indeterminate={someVisibleSelected}
            onChange={toggleAll}
          />
        ),
        cell: ({ row }) => (
          <Checkbox
            size="xs"
            aria-label={`Select ${row.original.reference}`}
            checked={selected.has(row.original.id)}
            onChange={() => toggleOne(row.original.id)}
          />
        ),
      });
    }

    cols.push(
      {
        id: "reference",
        header: "Reference",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          return (
            <Group gap={6} wrap="nowrap">
              <Text size="sm" fw={700} c="dark.5">
                {b.reference}
              </Text>
              {b.isGovernment ? (
                <Badge size="xs" variant="light" color="grape">
                  Gov
                </Badge>
              ) : null}
            </Group>
          );
        },
      },
      {
        id: "customer",
        header: "Customer",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          return (
            <Group gap={8} wrap="nowrap">
              <Box
                style={{
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  width: 26,
                  height: 26,
                  borderRadius: 8,
                  flexShrink: 0,
                  background: "#FEF1D5",
                  border: "1px solid #FBD171",
                }}
              >
                <Text size="10px" fw={800} style={{ color: "#B26C09" }}>
                  {initials(b.company)}
                </Text>
              </Box>
              <Text size="sm" c="gray.7" truncate>
                {b.company}
              </Text>
            </Group>
          );
        },
      },
      {
        id: "selectedForBatch",
        header: "Selected for batch",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          if (!b.selectedForBatchAt)
            return (
              <Text size="sm" c="dimmed">
                —
              </Text>
            );
          return (
            <>
              <Text size="sm" style={{ whiteSpace: "nowrap" }}>
                {fmtDateTime(b.selectedForBatchAt)} EAT
              </Text>
              {b.paymentDeadline ? (
                <Text size="xs" c="orange.7" fw={600}>
                  Pay by {fmtDateTime(b.paymentDeadline)} EAT
                </Text>
              ) : null}
            </>
          );
        },
      },
      {
        id: "capacity",
        header: "Capacity",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          return (
            <Group gap={4} wrap="nowrap">
              <Badge variant="default" radius="sm" size="sm">
                {b.wagons}w
              </Badge>
              <Badge variant="default" radius="sm" size="sm">
                {fmtTons(b.weightTons)}
              </Badge>
            </Group>
          );
        },
      },
      {
        id: "state",
        header: "Batch state",
        meta: cellMeta,
        cell: ({ row }) => {
          const m = STATE_META[row.original.state];
          return (
            <Badge variant="light" color={m.color} radius="sm">
              {m.label}
            </Badge>
          );
        },
      },
      {
        id: "allocation",
        header: "Wagon allocation",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          const m = ALLOC_META[b.allocationStatus];
          const badge = (
            <Badge variant="light" color={m.color} radius="sm">
              {m.label}
            </Badge>
          );
          if (!b.allocationIssue) return badge;
          return (
            <Tooltip label={b.allocationIssue} multiline maw={320} withArrow>
              <Group gap={4} wrap="nowrap">
                {badge}
                <AlertTriangle size={14} color="var(--mantine-color-red-6)" />
              </Group>
            </Tooltip>
          );
        },
      },
    );

    if (!readOnly) {
      cols.push({
        id: "actions",
        header: "",
        meta: cellMeta,
        cell: ({ row }) => {
          const b = row.original;
          const isAssigned = b.allocationStatus === "ASSIGNED";
          return (
            <Group justify="flex-end" gap={4} wrap="nowrap">
              <Menu position="bottom-end" withinPortal shadow="md">
                <Menu.Target>
                  <ActionIcon variant="subtle" color="gray" aria-label="Actions">
                    <MoreVertical size={16} />
                  </ActionIcon>
                </Menu.Target>
                <Menu.Dropdown>
                  <Menu.Item
                    leftSection={<PackagePlus size={14} />}
                    disabled={isAssigned || busy}
                    onClick={() =>
                      setConfirm({
                        kind: "reassign",
                        ids: [b.id],
                        label: b.reference,
                      })
                    }
                  >
                    Re-assign to wagons
                  </Menu.Item>
                  <Menu.Item
                    color="red"
                    leftSection={<Trash2 size={14} />}
                    disabled={!isAssigned || busy}
                    onClick={() =>
                      setConfirm({
                        kind: "remove",
                        ids: [b.id],
                        label: b.reference,
                      })
                    }
                  >
                    Remove from train
                  </Menu.Item>
                </Menu.Dropdown>
              </Menu>
            </Group>
          );
        },
      });
    }

    return cols;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    readOnly,
    selected,
    allVisibleSelected,
    someVisibleSelected,
    visibleIds,
    busy,
  ]);

  return (
    <Stack gap="sm">
      {/* Toolbar: search + filters */}
      <Group gap="sm" wrap="wrap">
        <TextInput
          flex={1}
          miw={220}
          radius="md"
          placeholder="Search reference or customer…"
          leftSection={<Search size={15} />}
          value={query}
          onChange={(e) => setQuery(e.currentTarget.value)}
          rightSection={
            query ? (
              <ActionIcon
                variant="subtle"
                color="gray"
                size="sm"
                onClick={() => setQuery("")}
                aria-label="Clear search"
              >
                <X size={14} />
              </ActionIcon>
            ) : null
          }
        />
        <Select
          radius="md"
          w={170}
          data={STATE_FILTERS}
          value={stateFilter}
          onChange={(v) => setStateFilter(v ?? "ALL")}
          aria-label="Filter by batch state"
        />
        <Select
          radius="md"
          w={180}
          data={ALLOC_FILTERS}
          value={allocFilter}
          onChange={(v) => setAllocFilter(v ?? "ALL")}
          aria-label="Filter by allocation"
        />
        <Text size="xs" c="dimmed">
          {filtered.length} of {bookings.length}
        </Text>
      </Group>

      {/* Bulk action bar */}
      {!readOnly && selectedVisible.length > 0 ? (
        <Paper
          withBorder
          radius="md"
          px="md"
          py="xs"
          style={{
            background: "var(--mantine-color-edr-green-0)",
            borderColor: "var(--mantine-color-edr-green-2)",
          }}
        >
          <Group justify="space-between" wrap="wrap" gap="sm">
            <Group gap="xs" wrap="nowrap">
              <Badge color="edr-green" radius="sm">
                {selectedVisible.length} selected
              </Badge>
              <Button
                size="compact-xs"
                variant="subtle"
                color="gray"
                onClick={clearSelection}
              >
                Clear
              </Button>
            </Group>
            <Group gap="xs" wrap="nowrap">
              <Button
                size="compact-sm"
                variant="light"
                color="edr-green"
                leftSection={<PackagePlus size={14} />}
                loading={reassign.isPending}
                disabled={busy}
                onClick={() =>
                  setConfirm({
                    kind: "reassign",
                    ids: selectedVisible,
                    label: `${selectedVisible.length} booking(s)`,
                  })
                }
              >
                Re-assign
              </Button>
              <Button
                size="compact-sm"
                variant="light"
                color="red"
                leftSection={<Trash2 size={14} />}
                loading={unassign.isPending}
                disabled={busy}
                onClick={() =>
                  setConfirm({
                    kind: "remove",
                    ids: selectedVisible,
                    label: `${selectedVisible.length} booking(s)`,
                  })
                }
              >
                Remove
              </Button>
            </Group>
          </Group>
        </Paper>
      ) : null}

      <DataTable
        columns={columns}
        data={filtered}
        status="success"
        emptyMessage={
          bookings.length
            ? "No bookings match the current search / filters."
            : "No bookings in this batch window."
        }
        containerClassName="overflow-x-auto rounded-lg border border-edr-border"
      />

      <Modal
        opened={Boolean(confirm)}
        onClose={() => setConfirm(null)}
        centered
        radius="md"
        title={
          confirm?.kind === "remove"
            ? "Remove from train"
            : "Re-assign to wagons"
        }
      >
        <Text size="sm" mb="lg">
          {confirm?.kind === "remove"
            ? `Remove ${confirm?.label} from this train? Their wagon allocation will be released.`
            : `Re-assign ${confirm?.label} to available wagons on this train?`}
        </Text>
        <Group justify="flex-end" gap="sm">
          <Button variant="default" onClick={() => setConfirm(null)}>
            Cancel
          </Button>
          <Button
            color={confirm?.kind === "remove" ? "red" : "edr-green"}
            loading={busy}
            onClick={confirmAction}
          >
            {confirm?.kind === "remove" ? "Remove" : "Re-assign"}
          </Button>
        </Group>
      </Modal>
    </Stack>
  );
});

export default BookingsManager;
