import { useAuth } from "@/auth/useAuth";
import {
    canAccessRuleEngineResource,
    canApproveRuleEngineChange,
} from "@/lib/permissions";
import type { ColumnDef } from "@edr/ui-common";
import {
    Box,
    Button,
    Card,
    Group,
    List,
    Loader,
    Modal,
    Stack,
    Tabs,
    Text,
    Tooltip,
} from "@mantine/core";
import { Clock, Plus } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Navigate, useLocation, useParams } from "react-router-dom";

import { PageContainer, PageHeader } from "@/components/page";
import ManageRuleEngineOrderDialog from "@/components/ruleEngine/ManageRuleEngineOrderDialog";
import PriorityRuleApprovalsSection from "@/pages/ruleEngine/PriorityRuleApprovalsSection";
import RateApprovalsSection from "@/pages/ruleEngine/RateApprovalsSection";
import { nextPriorityRangeStart } from "@/pages/ruleEngine/priorityRuleRange";
import RuleEngineCardGrid from "@/components/ruleEngine/RuleEngineCardGrid";
import RuleEngineFormDialog from "@/components/ruleEngine/RuleEngineFormDialog";
import RuleEngineOrderControls from "@/components/ruleEngine/RuleEngineOrderControls";
import RuleEngineRecordActions from "@/components/ruleEngine/RuleEngineRecordActions";
import RuleEngineToolbar from "@/components/ruleEngine/RuleEngineToolbar";
import { formatCell } from "@/components/ruleEngine/ruleEngineFormat";
import { getOrderItemLabel } from "@/components/ruleEngine/ruleEngineOrder.utils";
import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
import { useRuleEngineViewMode } from "@/components/ruleEngine/useRuleEngineViewMode";
import {
    useApprovalChain,
    useApprovalRoleOptions,
    useCargoLeafOptions,
    useCargoTypeParentOptions,
    useContainerTypeOptions,
    useLiveRateOptions,
    useWagonTypeOptions,
    useYardOptions,
    type YardOption,
    usePriorityRuleWorkflow,
    useRateChangeWorkflow,
    useRateWorkflow,
    useRuleEngineList,
    useRuleEngineMutations,
    useRuleEngineOrderList,
    useRuleEngineOrderMutations,
} from "@/hooks/rule-engine/useRuleEngine";
import {
    DEFAULT_CONFIGURATION_SLUG,
    DEFAULT_RULES_SLUG,
    RULE_ENGINE_CATEGORY_BASE_PATH,
    RULE_ENGINE_SELECT_NONE,
    getRuleEngineResource,
    rateUnitOptions,
    type RuleEngineNavCategory,
} from "@/pages/ruleEngine/config/resources";
import type { RateChangeRequest } from "@/services/ruleEngine/ruleEngine.service";
import type { RuleEngineRecord } from "@/types/rule-engine";
import {
    DataTable,
    DataTableFooter,
    usePagination,
} from "@edr/ui-common";

const pathCategory = (pathname: string): RuleEngineNavCategory | undefined => {
  const normalized = pathname.toLowerCase();
  if (normalized.startsWith("/dashboard/configuration")) return "configuration";
  if (normalized.startsWith("/dashboard/rules")) return "rules";
  return undefined;
};

/**
 * Which yards may sit at one end of the leg a base-freight rate prices.
 *
 * The railway sells three shapes and each pins the countries: an import lands
 * at a Djibouti port and rails inland, an export is the reverse, and intercity
 * stays inside Ethiopia. Narrowing the dropdown is what stops an import rate
 * from being configured Ethiopia → Ethiopia — the API rejects that too, but
 * the admin should never be offered it. Non-base-freight rates carry no leg,
 * so they get nothing.
 */
const yardOptionsForLegEnd = (
  yards: YardOption[],
  values: Record<string, unknown>,
  end: "origin" | "destination",
): { label: string; value: string }[] => {
  const appliesTo = String(values.appliesTo ?? "");
  let country: string | undefined;
  if (appliesTo === "INTERCITY") {
    country = "Ethiopia";
  } else if (
    appliesTo === "CONTAINER" ||
    appliesTo === "BULK" ||
    // Customs clearance + empty-container return are sold per direction +
    // route, so their yard dropdowns narrow exactly like base freight.
    (appliesTo === "OTHER" &&
      ["CUSTOMS_CLEARANCE", "WITH_RETURN"].includes(String(values.trigger ?? "")))
  ) {
    const direction = String(values.tradeDirection ?? "");
    // Direction is what decides the countries, so offer nothing until it is set
    // rather than defaulting to one and letting it read as a real choice.
    if (direction !== "IMPORT" && direction !== "EXPORT") return [];
    const startsInEthiopia = direction === "EXPORT";
    country = (end === "origin" ? startsInEthiopia : !startsInEthiopia)
      ? "Ethiopia"
      : "Djibouti";
  }
  if (!country) return [];
  return yards
    .filter((yard) => yard.country === country)
    .map(({ label, value }) => ({ label, value }));
};

const RuleEngineResourcePage = () => {
  const { user } = useAuth();
  const { resource: resourceSlug } = useParams<{ resource: string }>();
  const location = useLocation();
  const category = pathCategory(location.pathname);
  const config = resourceSlug ? getRuleEngineResource(resourceSlug) : undefined;

  const defaultPath = category
    ? `${RULE_ENGINE_CATEGORY_BASE_PATH[category]}/${category === "rules" ? DEFAULT_RULES_SLUG : DEFAULT_CONFIGURATION_SLUG
    }`
    : `${RULE_ENGINE_CATEGORY_BASE_PATH.configuration}/${DEFAULT_CONFIGURATION_SLUG}`;

  const { pagination, setPagination } = usePagination({ pageSize: 10 });
  const [search, setSearch] = useState("");
  // Category tabs (rates page): the active tab's filters go to the backend.
  const [activeTab, setActiveTab] = useState<string>(
    config?.listTabs?.[0]?.key ?? "",
  );
  const [formOpen, setFormOpen] = useState(false);
  const [editing, setEditing] = useState<RuleEngineRecord | null>(null);
  const [deleteTarget, setDeleteTarget] = useState<RuleEngineRecord | null>(
    null,
  );
  const [chainOpen, setChainOpen] = useState(false);
  const [orderDialogOpen, setOrderDialogOpen] = useState(false);
  const { viewMode, setViewMode } = useRuleEngineViewMode(
    config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
  );

  const canView = Boolean(
    config && canAccessRuleEngineResource(user, config.slug, "view"),
  );
  // Per-action gates replace the retired coarse "manage": Add shows only with
  // create, row Edit with update, row Delete with delete.
  const canCreate = Boolean(
    config && canAccessRuleEngineResource(user, config.slug, "create"),
  );
  const canUpdate = Boolean(
    config && canAccessRuleEngineResource(user, config.slug, "update"),
  );
  const canDelete = Boolean(
    config && canAccessRuleEngineResource(user, config.slug, "delete"),
  );
  // Update-class controls (reorder, rate submit/approve, approval-rule decide)
  // all map to the update permission — the matching endpoints now require it.
  const canUpdateControls = canUpdate;

  const listParams = useMemo(
    () => ({
      search: config?.supportsSearch ? search.trim() || undefined : undefined,
      page: pagination.pageIndex + 1,
      pageSize: pagination.pageSize,
      ...(config?.orderConfig
        ? {
          sortBy: config.orderConfig.field,
          sortOrder: "ASC" as const,
        }
        : {}),
      ...(config?.listTabs?.find((t) => t.key === activeTab)?.filters ?? {}),
    }),
    [
      config?.orderConfig,
      config?.supportsSearch,
      config?.listTabs,
      activeTab,
      search,
      pagination.pageIndex,
      pagination.pageSize,
    ],
  );

  useEffect(() => {
    setPagination((prev) => ({ pageIndex: 0, pageSize: prev.pageSize }));
    setSearch("");
    setActiveTab(config?.listTabs?.[0]?.key ?? "");
  }, [config?.slug, config?.listTabs, setPagination]);

  const { data, isLoading, isError, error } = useRuleEngineList(
    config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
    listParams,
  );

  const { create, update, remove } = useRuleEngineMutations(
    config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
  );
  const { reorder, moveOrder } = useRuleEngineOrderMutations(
    config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
  );
  const { data: orderListData, isLoading: orderListLoading } =
    useRuleEngineOrderList(
      config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
      Boolean(orderDialogOpen && config?.orderConfig),
      config?.orderConfig?.field,
    );
  const { submit, approve } = useRateWorkflow();
  const { data: chainData, isLoading: chainLoading } = useApprovalChain(
    chainOpen && config?.slug === "approval-rules",
  );

  // A LIVE rate is what pricing charges, so editing one files a change request
  // instead of mutating: the rate keeps its current value until an approver
  // applies the change. DRAFT rates still edit directly.
  const isRates = config?.slug === "rates";
  const [rateError, setRateError] = useState<string | null>(null);
  const rateChangeWorkflow = useRateChangeWorkflow(
    Boolean(isRates && canView),
    setRateError,
  );
  const canApproveRates = Boolean(isRates && canApproveRuleEngineChange(user, "rates"));
  /** rateId → its pending change, for the row badge. */
  const pendingByRateId = useMemo(() => {
    const map = new Map<string, RateChangeRequest>();
    for (const r of rateChangeWorkflow.pending.data ?? []) map.set(r.rateId, r);
    return map;
  }, [rateChangeWorkflow.pending.data]);

  // Priority rules never mutate directly: changes are filed for approval and a
  // pending queue renders above the table. Validation errors (range collision,
  // gap, ceiling) surface in a modal so the text is impossible to miss.
  const isPriorityRules = config?.slug === "priority-configs";
  const [priorityError, setPriorityError] = useState<string | null>(null);
  const priorityWorkflow = usePriorityRuleWorkflow(
    Boolean(isPriorityRules && canView),
    setPriorityError,
  );

  const editingId = editing?.id ? String(editing.id) : undefined;
  const usesContainerTypeField = Boolean(
    config?.formFields.some((f) => f.name === "containerTypeId"),
  );
  const usesCargoTypeField = Boolean(
    config?.formFields.some((f) => f.name === "cargoTypeId"),
  );
  const usesLiveRateField = Boolean(
    config?.formFields.some((f) => f.name === "rateId"),
  );
  const usesWagonTypeField = Boolean(
    config?.formFields.some(
      (f) => f.name === "wagonTypeId" || f.name === "wagonTypeIds",
    ),
  );

  const { data: cargoParentOptions, isLoading: cargoParentOptionsLoading } =
    useCargoTypeParentOptions(editingId, config?.slug === "cargo-types");
  const { data: cargoLeafOptions, isLoading: cargoLeafOptionsLoading } =
    useCargoLeafOptions(usesCargoTypeField);
  const { data: containerTypeOptions, isLoading: containerTypeOptionsLoading } =
    useContainerTypeOptions(config?.slug === "rates", usesContainerTypeField);
  const { data: liveRateOptions, isLoading: liveRateOptionsLoading } =
    useLiveRateOptions(usesLiveRateField);
  const { data: wagonTypeOptions, isLoading: wagonTypeOptionsLoading } =
    useWagonTypeOptions(usesWagonTypeField);
  const usesYardField = Boolean(
    config?.formFields.some(
      (f) =>
        f.name === "originYardId" ||
        f.name === "fromYardId" ||
        f.name === "toYardId",
    ),
  );
  const { data: yardOptions, isLoading: yardOptionsLoading } =
    useYardOptions(usesYardField);
  const yardLabelById = useMemo(
    () => Object.fromEntries((yardOptions ?? []).map((y) => [y.value, y.label])),
    [yardOptions],
  );
  const usesApprovalRoleField = Boolean(
    config?.formFields.some(
      (f) => f.name === "requiredRole" || f.name === "blocksRole",
    ),
  );
  const { data: approvalRoleOptions, isLoading: approvalRoleOptionsLoading } =
    useApprovalRoleOptions(usesApprovalRoleField);

  // Full rule list backing the auto-filled "min wagon count": the next range
  // always continues the chain for the selected type (per currency), so the
  // form needs every existing rule, not the current page.
  const { data: allPriorityRules } = useRuleEngineOrderList(
    config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
    Boolean(isPriorityRules && formOpen),
    config?.orderConfig?.field,
  );

  const formFields = useMemo(() => {
    if (!config) return [];
    return config.formFields.map((field) => {
      if (isPriorityRules && field.name === "minWagonCount") {
        return {
          ...field,
          // Editing keeps the rule's own start (a lower gap never forces it to
          // move); creating always continues the chain / fills the lowest gap.
          computeValue: (values: Record<string, unknown>) =>
            editing?.minWagonCount != null
              ? Number(editing.minWagonCount)
              : nextPriorityRangeStart(
                  allPriorityRules ?? [],
                  String(values.type ?? ""),
                  !values.currency || values.currency === RULE_ENGINE_SELECT_NONE
                    ? null
                    : String(values.currency),
                  editingId,
                ),
        };
      }
      if (config.slug === "cargo-types" && field.name === "parentGroupId") {
        return {
          ...field,
          options: cargoParentOptions ?? [
            { label: "None", value: RULE_ENGINE_SELECT_NONE },
          ],
        };
      }
      if (field.name === "containerTypeId") {
        return {
          ...field,
          type: "select" as const,
          options: containerTypeOptions ?? [],
        };
      }
      if (field.name === "cargoTypeId") {
        return {
          ...field,
          type: "select" as const,
          options: cargoLeafOptions ?? [],
        };
      }
      // Rate units follow the picked commodity: a per-item (break-bulk) cargo
      // is priced per item where a weighed one is priced per ton.
      if (field.name === "rateUnit" && field.optionsFromValues) {
        return {
          ...field,
          optionsFromValues: (values: Record<string, unknown>) =>
            rateUnitOptions(
              values,
              (cargoLeafOptions ?? []).find(
                (o) => o.value === String(values.cargoTypeId ?? ""),
              )?.unitOfMeasure ?? "",
            ),
        };
      }
      if (field.name === "rateId") {
        return {
          ...field,
          type: "select" as const,
          options: liveRateOptions ?? [],
        };
      }
      if (field.name === "wagonTypeId") {
        return {
          ...field,
          type: "select" as const,
          options: wagonTypeOptions ?? [],
        };
      }
      if (field.name === "wagonTypeIds") {
        return {
          ...field,
          type: "multiselect" as const,
          options: wagonTypeOptions ?? [],
        };
      }
      // Approval steps are configured against live IAM position types; until
      // they load, the static legacy list on the field config stands in so an
      // existing row's role still shows a label.
      if (field.name === "requiredRole" || field.name === "blocksRole") {
        if (!approvalRoleOptions) return field;
        const includeNone = field.name === "blocksRole";
        return {
          ...field,
          type: "select" as const,
          options: includeNone
            ? [{ label: "None", value: RULE_ENGINE_SELECT_NONE }, ...approvalRoleOptions]
            : approvalRoleOptions,
        };
      }
      // Each end of the leg only offers yards in the country that end of the
      // trade actually sits in, so an import can't be configured as if it
      // started inland. Resolved per keystroke because the legal set changes
      // with the direction the admin picks.
      // Yard-distance endpoints have no country restriction — any yard can pair
      // with any other; the other end is just excluded so A↔A can't be entered.
      if (field.name === "fromYardId" || field.name === "toYardId") {
        const otherEnd = field.name === "fromYardId" ? "toYardId" : "fromYardId";
        return {
          ...field,
          type: "select" as const,
          optionsFromValues: (values: Record<string, unknown>) =>
            (yardOptions ?? [])
              .filter(({ value }) => value !== String(values[otherEnd] ?? ""))
              .map(({ label, value }) => ({ label, value })),
        };
      }
      if (field.name === "originYardId" || field.name === "destinationYardId") {
        const end = field.name === "originYardId" ? "origin" : "destination";
        return {
          ...field,
          type: "select" as const,
          optionsFromValues: (values: Record<string, unknown>) =>
            yardOptionsForLegEnd(yardOptions ?? [], values, end),
        };
      }
      return field;
    });
  }, [config, cargoParentOptions, cargoLeafOptions, containerTypeOptions, liveRateOptions, wagonTypeOptions, yardOptions, approvalRoleOptions, isPriorityRules, allPriorityRules, editing, editingId]);

  const rows = data?.items ?? [];
  const meta = data?.meta;
  const pageCount = meta?.totalPages ?? 1;
  const totalCount = meta?.total ?? rows.length;

  const { data: createPositionList, isLoading: createPositionLoading } =
    useRuleEngineOrderList(
      config?.slug ?? DEFAULT_CONFIGURATION_SLUG,
      Boolean(formOpen && !editing && config?.orderConfig),
      config?.orderConfig?.field,
    );

  const createPositionOptions = useMemo(() => {
    if (!config?.orderConfig || !createPositionList?.length)
      return undefined;
    return createPositionList
      .filter((row) => row.id)
      .map((row) => ({
        label: getOrderItemLabel(row, config.slug),
        value: String(row.id),
      }));
  }, [config?.orderConfig, config?.slug, createPositionList]);

  const handleApproveRate = useCallback(
    (record: RuleEngineRecord) => {
      approve.mutate(String(record.id));
    },
    [approve],
  );

  const handleMoveOrder = useCallback(
    (id: string, direction: "up" | "down") => {
      moveOrder.mutate({ id, direction });
    },
    [moveOrder],
  );

  const columns = useMemo((): ColumnDef<RuleEngineRecord>[] => {
    if (!config) return [];

    const headerClassName = ruleEngineTable.headerCell;
    const cellClassName = ruleEngineTable.bodyCell;

    const base: ColumnDef<RuleEngineRecord>[] = config.columns.map((col) => ({
      id: col.id,
      header: col.header,
      meta: { headerClassName, cellClassName },
      cell: ({ row }) => {
        const cell = formatCell(row.original[col.accessorKey], col.format);
        // On the rate column, show the proposed value under the live one — the
        // live value stays the headline because it is what still gets charged.
        if (!isRates || col.accessorKey !== "rateValue") return cell;
        const change = pendingByRateId.get(String(row.original.id));
        if (!change || change.payload.rateValue === undefined) return cell;
        return (
          <Stack gap={0}>
            {cell}
            <Tooltip label="Awaiting approval — this rate still charges its current value">
              <Group gap={4} wrap="nowrap">
                <Clock size={11} color="var(--mantine-color-orange-6)" />
                <Text size="xs" c="orange.7" fw={600}>
                  {Number(change.payload.rateValue).toLocaleString()} pending
                </Text>
              </Group>
            </Tooltip>
          </Stack>
        );
      },
    }));

    base.push({
      id: "actions",
      header: "Actions",
      size: config.orderConfig ? 200 : 140,
      minSize: config.orderConfig ? 180 : 120,
      meta: {
        headerClassName,
        cellClassName: `${cellClassName} whitespace-nowrap`,
      },
      cell: ({ row }) => (
        <div onClick={(e) => e.stopPropagation()} data-stop-row-click>
          <Group gap="xs" wrap="nowrap" justify="flex-end">
            {config.orderConfig && canUpdateControls ? (
              <RuleEngineOrderControls
                record={row.original}
                orderConfig={config.orderConfig}
                totalCount={totalCount}
                disabled={moveOrder.isPending}
                onMove={handleMoveOrder}
              />
            ) : null}
            <RuleEngineRecordActions
              record={row.original}
              config={config}
              layout="row"
              readOnly={!canUpdateControls}
              onEdit={(record) => {
                setEditing(record);
                setFormOpen(true);
              }}
              onDelete={setDeleteTarget}
              onViewChain={
                config.slug === "approval-rules"
                  ? () => setChainOpen(true)
                  : undefined
              }
              onSubmitRate={canUpdateControls ? (id) => submit.mutate(id) : undefined}
              onApproveRate={canUpdateControls ? handleApproveRate : undefined}
            />
          </Group>
        </div>
      ),
    });

    return base;
  }, [
    canUpdateControls,
    config,
    isRates,
    pendingByRateId,
    submit,
    handleApproveRate,
    handleMoveOrder,
    moveOrder.isPending,
    totalCount,
  ]);

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

  if (!resourceSlug || !category) {
    return <Navigate to={defaultPath} replace />;
  }

  if (!config || config.category !== category) {
    return <Navigate to={defaultPath} replace />;
  }

  if (!canView) {
    return <Navigate to="/dashboard/overview" replace />;
  }

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

  const openEdit = (record: RuleEngineRecord) => {
    setEditing(record);
    setFormOpen(true);
  };

  const handleFormSubmit = (values: Record<string, unknown>) => {
    let payload = values;
    if (config.slug === "rates") {
      // Base-freight categories have no surcharge trigger field — the engine
      // treats them as ALWAYS. Surcharges (Applies to = Other) keep their
      // chosen trigger.
      const isSurcharge = values.appliesTo === "OTHER";
      payload = {
        ...values,
        currency: "USD",
        trigger: isSurcharge ? values.trigger : "ALWAYS",
      };
      // Editing a LIVE rate files a change request — the rate keeps charging
      // its current value until an approver applies it. DRAFT rates fall
      // through to the normal update below.
      if (editing?.id && editing.status === "LIVE") {
        rateChangeWorkflow.submit.mutate(
          { rateId: String(editing.id), update: payload },
          {
            onSuccess: () => {
              setFormOpen(false);
              setEditing(null);
            },
          },
        );
        return;
      }
    } else if (isPriorityRules) {
      // Label is required by the backend but hidden in the UI for now.
      payload = { ...values, label: String(Date.now()) };
      // Approval workflow: file a change request instead of mutating directly.
      // On update, keep the target's existing label rather than a fresh stamp.
      if (editing?.id) {
        priorityWorkflow.submit.mutate(
          {
            action: "UPDATE",
            priorityConfigId: String(editing.id),
            update: { ...values, label: String(editing.label ?? Date.now()) },
          },
          {
            onSuccess: () => {
              setFormOpen(false);
              setEditing(null);
            },
          },
        );
      } else {
        priorityWorkflow.submit.mutate(
          { action: "CREATE", create: payload },
          {
            onSuccess: () => {
              setFormOpen(false);
              setEditing(null);
            },
          },
        );
      }
      return;
    } else if (config.slug === "weight-limit-rules") {
      // Empty max capacity means "no ceiling" — send null explicitly so an
      // edit can clear a previously-set ceiling (omitting the key keeps it).
      payload = { ...values, maxCapacityTons: values.maxCapacityTons ?? null };
    }

    if (editing?.id) {
      update.mutate(
        { id: editing.id, payload },
        {
          onSuccess: () => {
            setFormOpen(false);
            setEditing(null);
          },
        },
      );
    } else {
      create.mutate(payload, {
        onSuccess: () => {
          setFormOpen(false);
          setEditing(null);
        },
      });
    }
  };

  const itemLabel = config.label.toLowerCase();
  const addLabel = `Add ${config.label.replace(/s$/, "")}`;

  return (
    <PageContainer>
      <PageHeader
        title={config.label}
        subtitle={config.subtitle}
        action={
          canCreate && config.slug !== "container-types" ? (
            <Button leftSection={<Plus size={18} />} onClick={openCreate}>
              {addLabel}
            </Button>
          ) : undefined
        }
      />

      {isPriorityRules ? (
        <PriorityRuleApprovalsSection
          requests={priorityWorkflow.pending.data ?? []}
          canDecide={canUpdateControls}
          approve={priorityWorkflow.approve}
          reject={priorityWorkflow.reject}
        />
      ) : null}

      {isRates ? (
        <RateApprovalsSection
          requests={rateChangeWorkflow.pending.data ?? []}
          canDecide={canApproveRates}
          approve={rateChangeWorkflow.approve}
          reject={rateChangeWorkflow.reject}
          yardLabels={yardLabelById}
        />
      ) : null}

      <Modal
        opened={rateError != null}
        onClose={() => setRateError(null)}
        title="Cannot save rate change"
        centered
      >
        <Text size="sm" c="red">
          {rateError}
        </Text>
        <Group justify="flex-end" mt="md">
          <Button variant="light" onClick={() => setRateError(null)}>
            Close
          </Button>
        </Group>
      </Modal>

      <Modal
        opened={priorityError != null}
        onClose={() => setPriorityError(null)}
        title="Cannot save priority rule"
        centered
        size="md"
      >
        <Stack gap="md">
          <Text size="sm" c="red.8">
            {priorityError}
          </Text>
          <Group justify="flex-end">
            <Button variant="default" onClick={() => setPriorityError(null)}>
              OK
            </Button>
          </Group>
        </Stack>
      </Modal>

      <Card p={0}>
        <Stack gap={0}>
          {config.listTabs && (
            <Tabs
              value={activeTab}
              onChange={(v) => {
                setActiveTab(v ?? config.listTabs![0].key);
                setPagination({ pageIndex: 0, pageSize: pagination.pageSize });
              }}
              px="md"
              pt="sm"
            >
              <Tabs.List>
                {config.listTabs.map((tab) => (
                  <Tabs.Tab key={tab.key} value={tab.key}>
                    {tab.label}
                  </Tabs.Tab>
                ))}
              </Tabs.List>
            </Tabs>
          )}
          <Box px="md" pt="md" pb="sm" w="100%">
            <RuleEngineToolbar
              search={search}
              onSearchChange={
                config.supportsSearch
                  ? (v) => {
                    setSearch(v);
                    setPagination({
                      pageIndex: 0,
                      pageSize: pagination.pageSize,
                    });
                  }
                  : undefined
              }
              showSearch={Boolean(config.supportsSearch)}
              searchPlaceholder={config.searchPlaceholder}
              onManageOrder={
                canUpdateControls && config.orderConfig
                  ? () => setOrderDialogOpen(true)
                  : undefined
              }
              viewMode={viewMode}
              onViewModeChange={setViewMode}
            />
          </Box>

          {viewMode === "table" ? (
            <DataTable
              columns={columns}
              data={rows}
              status={tableStatus}
              error={
                isError
                  ? {
                    message: "Failed to load data",
                    description:
                      error instanceof Error
                        ? error.message
                        : "Unknown error",
                  }
                  : undefined
              }
              emptyMessage={`No ${itemLabel} found.`}
              pagination={{
                pageIndex: pagination.pageIndex,
                pageSize: pagination.pageSize,
                pageCount,
                totalCount,
              }}
              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: {
                      showing: "Showing",
                      ofLabel: "of",
                      items: itemLabel,
                    },
                  }}
                />
              )}
            />
          ) : (
            <RuleEngineCardGrid
              config={config}
              rows={rows}
              status={tableStatus}
              emptyMessage={`No ${itemLabel} found.`}
              itemLabel={itemLabel}
              pagination={pagination}
              pageCount={pageCount}
              totalCount={totalCount}
              onPaginationChange={setPagination}
              readOnly={!canUpdate && !canDelete}
              onEdit={canUpdate ? openEdit : undefined}
              onDelete={canDelete ? setDeleteTarget : undefined}
              onViewChain={
                config.slug === "approval-rules"
                  ? () => setChainOpen(true)
                  : undefined
              }
              onSubmitRate={canUpdateControls ? (id) => submit.mutate(id) : undefined}
              onApproveRate={canUpdateControls ? handleApproveRate : undefined}
            />
          )}
        </Stack>
      </Card>

      <RuleEngineFormDialog
        open={formOpen}
        onOpenChange={setFormOpen}
        title={
          editing
            ? `Edit ${config.label.replace(/s$/, "")}`
            : `Add ${config.label.replace(/s$/, "")}`
        }
        description={
          editing
            ? `Update this ${config.label.toLowerCase()} record.`
            : `Create a new ${config.label.toLowerCase()} record.`
        }
        fields={formFields}
        initialRecord={editing}
        isSubmitting={
          create.isPending || update.isPending || priorityWorkflow.submit.isPending
        }
        selectOptionsLoading={
          (config.slug === "cargo-types" && cargoParentOptionsLoading) ||
          (usesContainerTypeField && containerTypeOptionsLoading) ||
          (usesCargoTypeField && cargoLeafOptionsLoading) ||
          (usesLiveRateField && liveRateOptionsLoading) ||
          (usesWagonTypeField && wagonTypeOptionsLoading) ||
          (usesYardField && yardOptionsLoading) ||
          (usesApprovalRoleField && approvalRoleOptionsLoading)
        }
        positionOptions={!editing ? createPositionOptions : undefined}
        positionLoading={createPositionLoading}
        onSubmit={handleFormSubmit}
      />

      {config.orderConfig ? (
        <ManageRuleEngineOrderDialog
          open={orderDialogOpen}
          onOpenChange={setOrderDialogOpen}
          config={config}
          items={orderListData ?? []}
          isLoading={orderListLoading}
          isSaving={reorder.isPending}
          onSave={(payload) => {
            reorder.mutate(payload, {
              onSuccess: () => setOrderDialogOpen(false),
            });
          }}
        />
      ) : null}

      <Modal
        opened={Boolean(deleteTarget)}
        onClose={() => setDeleteTarget(null)}
        title="Delete record?"
        centered
        size="sm"
      >
        <Stack gap="md">
          <Text size="sm">
            {isPriorityRules
              ? "This files a delete request for approval — the rule is removed once an approver confirms."
              : `This will soft-delete the selected ${config.label.toLowerCase()} record.`}
          </Text>
          <Group justify="flex-end" gap="sm">
            <Button variant="default" onClick={() => setDeleteTarget(null)}>
              Cancel
            </Button>
            <Button
              color="red"
              loading={remove.isPending || priorityWorkflow.submit.isPending}
              onClick={() => {
                if (!deleteTarget) return;
                if (isPriorityRules) {
                  priorityWorkflow.submit.mutate(
                    {
                      action: "DELETE",
                      priorityConfigId: String(deleteTarget.id),
                    },
                    { onSuccess: () => setDeleteTarget(null) },
                  );
                  return;
                }
                remove.mutate(deleteTarget.id, {
                  onSuccess: () => setDeleteTarget(null),
                });
              }}
            >
              {isPriorityRules ? "Request delete" : "Delete"}
            </Button>
          </Group>
        </Stack>
      </Modal>

      <Modal
        opened={chainOpen}
        onClose={() => setChainOpen(false)}
        title="Approval chain"
        centered
        size="md"
      >
        <Stack gap="md">
          {chainLoading ? (
            <Group justify="center" p="xl">
              <Loader />
            </Group>
          ) : (
            <>
              {(chainData ?? []).length === 0 ? (
                <Text size="sm" c="dimmed">
                  No approval rules configured.
                </Text>
              ) : (
                <List spacing="md">
                  {(chainData ?? []).map((step, index) => (
                    <List.Item key={String(step.id ?? index)}>
                      <Stack gap="xs">
                        <Text size="sm" fw={500}>
                          Step {String(step.stepOrder ?? index + 1)}:{" "}
                          {String(step.actionLabel ?? "")}
                        </Text>
                        <Text size="sm" c="dimmed">
                          Role: {String(step.requiredRole ?? "—")}
                        </Text>
                      </Stack>
                    </List.Item>
                  ))}
                </List>
              )}
            </>
          )}
        </Stack>
      </Modal>
    </PageContainer>
  );
};

export default RuleEngineResourcePage;
