import { useState } from 'react';
import {
  ActionIcon,
  Alert,
  Badge,
  Button,
  Card,
  Group,
  Modal,
  NumberInput,
  Select,
  Stack,
  Tabs,
  Text,
  TextInput,
} from '@mantine/core';
import { Info, Pencil, Plus, Trash2 } from 'lucide-react';

import { useQuery } from '@tanstack/react-query';

import { DataTable, type ColumnDef } from '@edr/ui-common';
import ListControls from '@/components/common/ListControls';
import { useListControls } from '@/hooks/useListControls';

import { PageContainer, PageHeader } from '@/components/page';
import { useToast } from '@/hooks/use-toast';
import {
  useAllWarehouseYards,
  useAllocationRules,
  useCreateAllocationRule,
  useCreateFeeRule,
  useDeleteAllocationRule,
  useDeleteFeeRule,
  useFeeRules,
  useUpdateAllocationRule,
  useUpdateFeeRule,
} from '@/hooks/useWarehouses';
import { api } from '@/services/api';
import {
  FEE_RULE_BASES,
  FEE_RULE_BASIS_LABELS,
  FEE_RULE_TYPES,
  FEE_RULE_TYPE_LABELS,
  type AllocationRule,
  type FeeRule,
  type FeeRuleBasis,
  type FeeRuleType,
} from '@/types/warehouse';

const RULE_TYPE_COLOR: Record<FeeRuleType, string> = {
  STORAGE_FEE: 'teal',
  DEMURRAGE_FEE: 'orange',
  DOUBLE_HANDLING_FEE: 'grape',
  TRUCK_DETENTION_FEE: 'blue',
};
import { extractErrorMessage, lettersOnly } from '@/components/warehouses/options';

const FREIGHT = [
  { value: 'CONTAINER', label: 'Container' },
  { value: 'BULK', label: 'Bulk' },
];
const TRADE = [
  { value: 'IMPORT', label: 'Import' },
  { value: 'EXPORT', label: 'Export' },
  { value: 'BOTH', label: 'Import & Export' },
  { value: 'DOMESTIC', label: 'Domestic' },
];
const CURRENCIES = [
  { value: 'USD', label: 'USD - Dollar' },
  { value: 'ETB', label: 'ETB - Birr' },
];

const clean = (s: string) => s.trim() || undefined;
const selectValue = (value: string | null, fallback = '') => value ?? fallback;
const numberValue = (value: string | number, fallback = 0) => {
  const next = Number(value);
  return Number.isFinite(next) ? next : fallback;
};
const anyLabel = (value: string, label: string) => value.trim() || `Any ${label}`;
const dash = '-';
type CodeOptionSource = {
  id?: string;
  code?: string;
  cargoTypeName?: string;
  label?: string;
  name?: string;
};

const codeOptions = (rows: unknown[]) =>
  (rows as CodeOptionSource[])
    .filter((row) => row.code)
    .map((row) => ({
      value: row.code as string,
      label: `${row.cargoTypeName ?? row.label ?? row.name ?? row.code} (${row.code})`,
    }));

const isUnknownTiersError = (error: unknown) => extractErrorMessage(error).includes('property tiers should not exist');

export default function WarehouseRulesPage() {
  return (
    <PageContainer>
      <PageHeader
        title="Allocation & Fee Rules"
        subtitle="Configure yard allocation and storage or demurrage free time and rates."
      />
      <Card>
        <Tabs defaultValue="allocation">
          <Tabs.List>
            <Tabs.Tab value="allocation">Allocation Rules</Tabs.Tab>
            <Tabs.Tab value="fees">Storage / Demurrage Fees</Tabs.Tab>
          </Tabs.List>
          <Tabs.Panel value="allocation" pt="md">
            <AllocationRules />
          </Tabs.Panel>
          <Tabs.Panel value="fees" pt="md">
            <FeeRules />
          </Tabs.Panel>
        </Tabs>
      </Card>
    </PageContainer>
  );
}

function AllocationRules() {
  const { toast } = useToast();
  const { data, isLoading } = useAllocationRules();
  const { data: yards = [], isLoading: yardsLoading } = useAllWarehouseYards();
  const create = useCreateAllocationRule();
  const update = useUpdateAllocationRule();
  const remove = useDeleteAllocationRule();
  const [open, setOpen] = useState(false);
  const [editingId, setEditingId] = useState<string | null>(null);
  const [form, setForm] = useState({
    name: '',
    priority: 100,
    freightType: '',
    tradeDirection: '',
    cargoTypeCode: '',
    containerStatus: '',
    targetYardCode: '',
    storageType: '',
  });

  const rules = data ?? [];
  const controls = useListControls(rules, {
    searchKeys: ['name', 'targetYardCode', 'targetWarehouseCode', 'targetZoneCode', 'freightType', 'tradeDirection', 'cargoTypeCode', 'storageType'],
    dateKey: 'createdAt',
  });
  const yardOptions = yards
    .filter((yard) => yard.code)
    .map((yard) => ({
      value: yard.code,
      label: `${yard.code} - ${yard.name}${yard.warehouse?.code ? ` (${yard.warehouse.code})` : ''}`,
    }));

  const resetForm = () => {
    setEditingId(null);
    setForm({
      name: '',
      priority: 100,
      freightType: '',
      tradeDirection: '',
      cargoTypeCode: '',
      containerStatus: '',
      targetYardCode: '',
      storageType: '',
    });
  };

  const startEdit = (rule: AllocationRule) => {
    setForm({
      name: rule.name,
      priority: rule.priority ?? 100,
      freightType: rule.freightType ?? '',
      tradeDirection: rule.tradeDirection ?? '',
      cargoTypeCode: rule.cargoTypeCode ?? '',
      containerStatus: rule.containerStatus ?? '',
      targetYardCode: rule.targetYardCode ?? '',
      storageType: rule.storageType ?? '',
    });
    setEditingId(rule.id);
    setOpen(true);
  };

  const submit = async () => {
    if (!form.name.trim() || !form.targetYardCode.trim()) {
      toast({ variant: 'destructive', title: 'Name and target yard are required' });
      return;
    }

    const payload = {
      name: form.name.trim(),
      priority: form.priority,
      freightType: clean(form.freightType) ?? null,
      tradeDirection: clean(form.tradeDirection) ?? null,
      cargoTypeCode: clean(form.cargoTypeCode) ?? null,
      containerStatus: clean(form.containerStatus) ?? null,
      targetYardCode: form.targetYardCode.trim(),
      storageType: clean(form.storageType) ?? null,
      isActive: true,
    };
    try {
      if (editingId) {
        await update.mutateAsync({ id: editingId, payload: payload as never });
        toast({ title: 'Allocation rule updated' });
      } else {
        await create.mutateAsync(payload as never);
        toast({ title: 'Allocation rule created' });
      }
      setOpen(false);
      resetForm();
    } catch (error) {
      toast({ variant: 'destructive', title: editingId ? 'Update failed' : 'Create failed', description: extractErrorMessage(error) });
    }
  };

  const allocationColumns: ColumnDef<AllocationRule>[] = [
    { id: 'priority', header: 'Priority', cell: ({ row }) => row.original.priority },
    { id: 'name', header: 'Name', cell: ({ row }) => row.original.name },
    { id: 'freight', header: 'Freight', cell: ({ row }) => row.original.freightType ?? dash },
    { id: 'trade', header: 'Trade', cell: ({ row }) => row.original.tradeDirection ?? dash },
    { id: 'cargoCode', header: 'Cargo code', cell: ({ row }) => row.original.cargoTypeCode ?? dash },
    {
      id: 'targetYard',
      header: 'Target yard',
      cell: ({ row }) => <Badge variant="light">{row.original.targetYardCode}</Badge>,
    },
    {
      id: 'active',
      header: 'Active',
      cell: ({ row }) => (
        <Badge color={row.original.isActive ? 'green' : 'gray'} variant="light">
          {row.original.isActive ? 'Yes' : 'No'}
        </Badge>
      ),
    },
    {
      id: 'actions',
      header: '',
      meta: { headerClassName: 'text-right', cellClassName: 'text-right' },
      cell: ({ row }) => (
        <Group gap={4} justify="flex-end" wrap="nowrap">
          <ActionIcon variant="subtle" color="blue" onClick={() => startEdit(row.original)} title="Edit">
            <Pencil size={16} />
          </ActionIcon>
          <ActionIcon
            variant="subtle"
            color="red"
            onClick={() => remove.mutate(row.original.id)}
            title="Delete"
          >
            <Trash2 size={16} />
          </ActionIcon>
        </Group>
      ),
    },
  ];

  return (
    <>
      <Group justify="space-between" mb="sm">
        <Text c="dimmed" size="sm">
          {controls.totalCount} rule(s) matched by ascending priority
        </Text>
        <Button leftSection={<Plus size={16} />} onClick={() => { resetForm(); setOpen(true); }}>
          New allocation rule
        </Button>
      </Group>
      <Alert icon={<Info size={16} />} color="orange" variant="light" mb="md">
        <Text size="sm">
          Allocation rules tell the system where to place a booking when it enters the warehouse.
          Lower priority numbers are checked first.
        </Text>
      </Alert>

      <ListControls
        search={controls.search}
        onSearchChange={controls.setSearch}
        searchPlaceholder="Search allocation rules…"
        dateFrom={controls.dateFrom}
        onDateFromChange={controls.setDateFrom}
        dateTo={controls.dateTo}
        onDateToChange={controls.setDateTo}
        dateLabel="Created"
        hasFilters={controls.hasFilters}
        onReset={controls.reset}
      />

      <DataTable
        columns={allocationColumns}
        data={controls.pagedRows}
        status={isLoading ? 'loading' : 'success'}
        emptyMessage="No allocation rules yet. Create one to route inventory to a yard automatically."
        containerClassName="border-0 shadow-none"
        {...controls.tableProps}
      />

      <Modal opened={open} onClose={() => { setOpen(false); resetForm(); }} title={editingId ? 'Edit allocation rule' : 'New allocation rule'} centered size="lg">
        <Stack gap="md">
          <Card withBorder radius="md" padding="sm" bg="gray.0">
            <Stack gap={4}>
              <Text size="xs" c="dimmed" fw={700} tt="uppercase">
                Rule preview
              </Text>
              <Text size="sm">
                <b>When</b> {anyLabel(form.tradeDirection, 'trade direction').toLowerCase()} /{' '}
                {anyLabel(form.freightType, 'freight type').toLowerCase()} booking
                {form.cargoTypeCode.trim() ? ` with cargo code ${form.cargoTypeCode.trim()}` : ''}
                {form.containerStatus.trim() ? ` and container status ${form.containerStatus.trim()}` : ''}{' '}
                is received, <b>send it to</b> {form.targetYardCode || 'a selected target yard'}.
              </Text>
            </Stack>
          </Card>
          <Group grow>
            <TextInput
              label="Rule name"
              placeholder="e.g. Import containers to open yard"
              required
              value={form.name}
              onChange={(e) => {
                const value = lettersOnly(e.currentTarget.value);
                setForm((f) => ({ ...f, name: value }));
              }}
            />
            <NumberInput
              label="Priority"
              value={form.priority}
              onChange={(v) => setForm((f) => ({ ...f, priority: numberValue(v, 100) || 100 }))}
            />
          </Group>
          <Group grow>
            <Select
              label="Freight type"
              placeholder="Any freight"
              data={FREIGHT}
              value={form.freightType || null}
              onChange={(v) => setForm((f) => ({ ...f, freightType: selectValue(v) }))}
              clearable
            />
            <Select
              label="Trade direction"
              placeholder="Any direction"
              data={TRADE}
              value={form.tradeDirection || null}
              onChange={(v) => setForm((f) => ({ ...f, tradeDirection: selectValue(v) }))}
              clearable
            />
          </Group>
          <Group grow>
            <TextInput
              label="Cargo type code"
              placeholder="e.g. COFFEE"
              value={form.cargoTypeCode}
              onChange={(e) => {
                const value = e.currentTarget.value;
                setForm((f) => ({ ...f, cargoTypeCode: value }));
              }}
            />
            <TextInput
              label="Container status"
              placeholder="e.g. MAINTENANCE"
              value={form.containerStatus}
              onChange={(e) => {
                const value = e.currentTarget.value;
                setForm((f) => ({ ...f, containerStatus: value }));
              }}
            />
          </Group>
          <Group grow>
            <Select
              label="Target yard"
              required
              searchable
              clearable
              data={yardOptions}
              value={form.targetYardCode || null}
              placeholder={yardsLoading ? 'Loading yards...' : 'Select target yard'}
              nothingFoundMessage="No yards found"
              disabled={yardsLoading}
              onChange={(value) => setForm((f) => ({ ...f, targetYardCode: selectValue(value) }))}
            />
            <TextInput
              label="Storage type"
              placeholder="e.g. OPEN_STACK"
              value={form.storageType}
              onChange={(e) => {
                const value = e.currentTarget.value;
                setForm((f) => ({ ...f, storageType: value }));
              }}
            />
          </Group>
          <Group justify="flex-end" mt="sm">
            <Button variant="default" onClick={() => { setOpen(false); resetForm(); }}>
              Cancel
            </Button>
            <Button loading={create.isPending || update.isPending} onClick={submit}>
              {editingId ? 'Save changes' : 'Create'}
            </Button>
          </Group>
        </Stack>
      </Modal>
    </>
  );
}

function FeeRules() {
  const { toast } = useToast();
  const { data, isLoading } = useFeeRules();
  const { data: cargoTypes = [], isLoading: cargoTypesLoading } = useQuery(
    api.cargoTypes.list.queryOptions({ staleTime: Infinity }),
  );
  const { data: containerTypes = [], isLoading: containerTypesLoading } = useQuery(
    api.containerTypes.list.queryOptions({ staleTime: Infinity }),
  );
  // Detention rules match on the truck-type CODE denormalised onto
  // vehicles.vehicle_type, so the options come from the managed truck types
  // rather than a hardcoded list that drifts the moment a type is added.
  const { data: truckTypes = [], isLoading: truckTypesLoading } = useQuery(
    api.truckTypes.list.queryOptions({ staleTime: Infinity }),
  );
  const create = useCreateFeeRule();
  const update = useUpdateFeeRule();
  const remove = useDeleteFeeRule();
  const [open, setOpen] = useState(false);
  const [editingId, setEditingId] = useState<string | null>(null);
  const [form, setForm] = useState({
    name: '',
    ruleType: 'DEMURRAGE_FEE' as FeeRuleType,
    basis: 'PER_CONTAINER' as FeeRuleBasis,
    freightType: '',
    tradeDirection: '',
    cargoTypeCode: '',
    containerType: '',
    vehicleType: '',
    freeDays: 3,
    freeHours: 3,
    ratePerDay: 0,
    tiers: [] as Array<{ fromDay: number; toDay: number | null; ratePerDay: number }>,
    currency: 'USD',
  });
  const rules = data ?? [];
  const controls = useListControls(rules, {
    searchKeys: ['name', 'ruleType', 'freightType', 'tradeDirection', 'cargoTypeCode', 'containerType', 'vehicleType', 'currency'],
    dateKey: 'createdAt',
  });
  const cargoTypeOptions = codeOptions(cargoTypes);
  const containerTypeOptions = codeOptions(containerTypes);
  const isBulkRule = form.freightType === 'BULK';
  const isContainerRule = form.freightType === 'CONTAINER';
  // Double handling is a flat per-unit charge (basis × rate), not day-based:
  // no free days, no progressive tiers.
  const isDoubleHandling = form.ruleType === 'DOUBLE_HANDLING_FEE';
  // Truck detention: per truck per day after an HOURS-based grace (default 3h),
  // with day tiers. Uses "free hours" instead of "free days".
  const isTruckDetention = form.ruleType === 'TRUCK_DETENTION_FEE';
  // Double handling + truck detention apply to IMPORT only — trade direction is locked.
  const isImportOnly = isDoubleHandling || isTruckDetention;

  const resetForm = () => {
    setEditingId(null);
    setForm({
      name: '',
      ruleType: 'DEMURRAGE_FEE',
      basis: 'PER_CONTAINER',
      freightType: '',
      tradeDirection: '',
      cargoTypeCode: '',
      containerType: '',
      vehicleType: '',
      freeDays: 3,
      freeHours: 3,
      ratePerDay: 0,
      tiers: [],
      currency: 'USD',
    });
  };

  const startEdit = (rule: FeeRule) => {
    setForm({
      name: rule.name,
      ruleType: rule.ruleType,
      basis: (rule.basis as FeeRuleBasis) ?? 'PER_CONTAINER',
      freightType: rule.freightType ?? '',
      tradeDirection: rule.tradeDirection ?? '',
      cargoTypeCode: rule.cargoTypeCode ?? '',
      containerType: rule.containerType ?? '',
      vehicleType: rule.vehicleType ?? '',
      freeDays: rule.freeDays ?? 3,
      freeHours: rule.freeHours ?? 3,
      ratePerDay: rule.ratePerDay ?? 0,
      tiers: (rule.tiers ?? []).map((t) => ({ fromDay: t.fromDay, toDay: t.toDay, ratePerDay: t.ratePerDay })),
      currency: rule.currency ?? 'USD',
    });
    setEditingId(rule.id);
    setOpen(true);
  };

  const addTier = () =>
    setForm((f) => {
      const last = f.tiers[f.tiers.length - 1];
      const fromDay = last?.toDay ? last.toDay + 1 : f.tiers.length ? last.fromDay + 1 : f.freeDays + 1;
      return {
        ...f,
        tiers: [...f.tiers, { fromDay, toDay: fromDay, ratePerDay: f.ratePerDay || 0 }],
      };
    });

  const updateTier = (
    index: number,
    patch: Partial<{ fromDay: number; toDay: number | null; ratePerDay: number }>,
  ) =>
    setForm((f) => ({
      ...f,
      tiers: f.tiers.map((tier, i) => (i === index ? { ...tier, ...patch } : tier)),
    }));

  const removeTier = (index: number) =>
    setForm((f) => ({
      ...f,
      tiers: f.tiers.filter((_, i) => i !== index),
    }));

  const submit = async () => {
    if (!form.name.trim()) {
      toast({ variant: 'destructive', title: 'Name is required' });
      return;
    }

    const tiers = form.tiers.map((tier) => ({
      fromDay: tier.fromDay,
      toDay: tier.toDay || null,
      ratePerDay: tier.ratePerDay,
    }));
    for (const [index, tier] of tiers.entries()) {
      if (!Number.isInteger(tier.fromDay) || tier.fromDay < 1) {
        toast({ variant: 'destructive', title: `Tier ${index + 1}: from day must be at least 1` });
        return;
      }
      if (tier.toDay != null && tier.toDay < tier.fromDay) {
        toast({ variant: 'destructive', title: `Tier ${index + 1}: to day must be after from day` });
        return;
      }
      if (tier.ratePerDay < 0) {
        toast({ variant: 'destructive', title: `Tier ${index + 1}: amount must be zero or greater` });
        return;
      }
    }

    const payload = {
      name: form.name.trim(),
      ruleType: form.ruleType,
      freightType: clean(form.freightType) ?? null,
      tradeDirection: isImportOnly ? 'IMPORT' : clean(form.tradeDirection) ?? null,
      cargoTypeCode: isBulkRule ? (clean(form.cargoTypeCode) ?? null) : null,
      containerType: isContainerRule ? (clean(form.containerType) ?? null) : null,
      // Double handling: flat basis × rate. Truck detention: HOURS-based grace.
      freeDays: isDoubleHandling || isTruckDetention ? 0 : form.freeDays,
      ratePerDay: form.ratePerDay,
      currency: form.currency || 'USD',
      ...(isDoubleHandling ? { basis: form.basis } : {}),
      ...(isTruckDetention ? { freeHours: form.freeHours, vehicleType: clean(form.vehicleType) ?? null } : {}),
      ...(!isDoubleHandling && tiers.length ? { tiers } : {}),
    };

    try {
      if (editingId) {
        await update.mutateAsync({ id: editingId, payload: payload as never });
        toast({ title: 'Fee rule updated' });
      } else {
        await create.mutateAsync(payload as never);
        toast({ title: 'Fee rule created' });
      }
      setOpen(false);
      resetForm();
    } catch (error) {
      if (!editingId && tiers.length && isUnknownTiersError(error)) {
        const legacyPayload: Omit<typeof payload, 'tiers'> = {
          name: payload.name,
          ruleType: payload.ruleType,
          freightType: payload.freightType,
          tradeDirection: payload.tradeDirection,
          cargoTypeCode: payload.cargoTypeCode,
          containerType: payload.containerType,
          freeDays: payload.freeDays,
          ratePerDay: payload.ratePerDay,
          currency: payload.currency,
        };
        await create.mutateAsync(legacyPayload as never);
        toast({
          title: 'Fee rule created without tiers',
          description: 'The connected API does not support progressive tiers yet. Deploy the warehouse fee tier migration/API to save tier rows.',
        });
        setOpen(false);
        resetForm();
        return;
      }
      toast({ variant: 'destructive', title: editingId ? 'Update failed' : 'Create failed', description: extractErrorMessage(error) });
    }
  };

  const feeColumns: ColumnDef<FeeRule>[] = [
    {
      id: 'type',
      header: 'Type',
      cell: ({ row }) => (
        <Badge color={RULE_TYPE_COLOR[row.original.ruleType] ?? 'gray'} variant="light">
          {FEE_RULE_TYPE_LABELS[row.original.ruleType] ?? row.original.ruleType}
        </Badge>
      ),
    },
    { id: 'name', header: 'Name', cell: ({ row }) => row.original.name },
    { id: 'freight', header: 'Freight', cell: ({ row }) => row.original.freightType ?? dash },
    { id: 'trade', header: 'Trade', cell: ({ row }) => row.original.tradeDirection ?? dash },
    { id: 'cargo', header: 'Cargo', cell: ({ row }) => row.original.cargoTypeCode ?? dash },
    { id: 'container', header: 'Container', cell: ({ row }) => row.original.containerType ?? dash },
    {
      id: 'scope',
      header: 'Location scope',
      cell: ({ row }) => {
        const rule = row.original;
        const hasScope = [rule.facilityId, rule.warehouseId, rule.yardId, rule.zoneId].some(Boolean);
        if (!hasScope) return dash;
        return (
          <Stack gap={2}>
            {rule.facilityId && <Text size="xs">Facility: {rule.facilityId}</Text>}
            {rule.warehouseId && <Text size="xs">Warehouse: {rule.warehouseId}</Text>}
            {rule.yardId && <Text size="xs">Yard: {rule.yardId}</Text>}
            {rule.zoneId && <Text size="xs">Zone: {rule.zoneId}</Text>}
          </Stack>
        );
      },
    },
    { id: 'freeDays', header: 'Free days', cell: ({ row }) => row.original.freeDays },
    {
      id: 'rate',
      header: 'Rate / day',
      cell: ({ row }) => `${Number(row.original.ratePerDay).toLocaleString()} ${row.original.currency}`,
    },
    {
      id: 'active',
      header: 'Active',
      cell: ({ row }) => (
        <Badge color={row.original.isActive ? 'green' : 'gray'} variant="light">
          {row.original.isActive ? 'Yes' : 'No'}
        </Badge>
      ),
    },
    {
      id: 'actions',
      header: '',
      meta: { headerClassName: 'text-right', cellClassName: 'text-right' },
      cell: ({ row }) => (
        <Group gap={4} justify="flex-end" wrap="nowrap">
          <ActionIcon variant="subtle" color="blue" onClick={() => startEdit(row.original)} title="Edit">
            <Pencil size={16} />
          </ActionIcon>
          <ActionIcon
            variant="subtle"
            color="red"
            onClick={() => remove.mutate(row.original.id)}
            title="Delete"
          >
            <Trash2 size={16} />
          </ActionIcon>
        </Group>
      ),
    },
  ];

  return (
    <>
      <Group justify="space-between" mb="sm">
        <Text c="dimmed" size="sm">
          {controls.totalCount} rule(s) - most specific match applies
        </Text>
        <Button leftSection={<Plus size={16} />} onClick={() => { resetForm(); setOpen(true); }}>
          New fee rule
        </Button>
      </Group>

      <ListControls
        search={controls.search}
        onSearchChange={controls.setSearch}
        searchPlaceholder="Search fee rules…"
        dateFrom={controls.dateFrom}
        onDateFromChange={controls.setDateFrom}
        dateTo={controls.dateTo}
        onDateToChange={controls.setDateTo}
        dateLabel="Created"
        hasFilters={controls.hasFilters}
        onReset={controls.reset}
      />

      <DataTable
        columns={feeColumns}
        data={controls.pagedRows}
        status={isLoading ? 'loading' : 'success'}
        emptyMessage="No storage or demurrage fee rules yet."
        containerClassName="border-0 shadow-none"
        {...controls.tableProps}
      />

      <Modal opened={open} onClose={() => { setOpen(false); resetForm(); }} title={editingId ? 'Edit fee rule' : 'New fee rule'} centered size="lg">
        <Stack gap="sm">
          <Group grow>
            <TextInput
              label="Name"
              required
              value={form.name}
              onChange={(e) => {
                const value = lettersOnly(e.currentTarget.value);
                setForm((f) => ({ ...f, name: value }));
              }}
            />
            <Select
              label="Rule type"
              data={FEE_RULE_TYPES.map((type) => ({
                value: type,
                label: FEE_RULE_TYPE_LABELS[type],
              }))}
              value={form.ruleType}
              onChange={(value) =>
                setForm((f) => ({
                  ...f,
                  ruleType: selectValue(value, 'DEMURRAGE_FEE') as FeeRuleType,
                }))
              }
              allowDeselect={false}
            />
          </Group>
          <Group grow>
            <Select
              label="Freight type"
              data={FREIGHT}
              value={form.freightType || null}
              onChange={(value) =>
                setForm((f) => ({
                  ...f,
                  freightType: selectValue(value),
                  cargoTypeCode: value === 'BULK' ? f.cargoTypeCode : '',
                  containerType: value === 'CONTAINER' ? f.containerType : '',
                }))
              }
              clearable
            />
            <Select
              label="Trade direction"
              description={isImportOnly ? 'Import only for this fee type' : undefined}
              data={TRADE}
              value={isImportOnly ? 'IMPORT' : form.tradeDirection || null}
              onChange={(value) => setForm((f) => ({ ...f, tradeDirection: selectValue(value) }))}
              disabled={isImportOnly}
              clearable={!isImportOnly}
            />
            {isTruckDetention && (
              <Select
                label="Truck type"
                placeholder={truckTypesLoading ? 'Loading truck types...' : 'Any truck type'}
                data={truckTypes.map((t) => ({ value: t.code, label: `${t.name} (${t.code})` }))}
                disabled={truckTypesLoading}
                value={form.vehicleType || null}
                onChange={(value) => setForm((f) => ({ ...f, vehicleType: selectValue(value) }))}
                clearable
              />
            )}
            {isBulkRule && (
              <Select
                label="Cargo type"
                placeholder={cargoTypesLoading ? 'Loading cargo types...' : 'Any cargo'}
                data={cargoTypeOptions}
                value={form.cargoTypeCode || null}
                onChange={(value) => setForm((f) => ({ ...f, cargoTypeCode: selectValue(value) }))}
                searchable
                clearable
                disabled={cargoTypesLoading}
              />
            )}
          </Group>
          {isContainerRule && (
            <Select
              label="Container type"
              placeholder={containerTypesLoading ? 'Loading container types...' : 'Any container'}
              data={containerTypeOptions}
              value={form.containerType || null}
              onChange={(value) => setForm((f) => ({ ...f, containerType: selectValue(value) }))}
              searchable
              clearable
              disabled={containerTypesLoading}
            />
          )}
          <Group grow>
            {isDoubleHandling ? (
              <Select
                label="Basis"
                data={FEE_RULE_BASES.map((b) => ({ value: b, label: FEE_RULE_BASIS_LABELS[b] }))}
                value={form.basis}
                onChange={(value) =>
                  setForm((f) => ({ ...f, basis: selectValue(value, 'PER_CONTAINER') as FeeRuleBasis }))
                }
                allowDeselect={false}
              />
            ) : isTruckDetention ? (
              <NumberInput
                label="Free hours"
                description="Grace before detention accrues"
                min={0}
                value={form.freeHours}
                onChange={(value) => setForm((f) => ({ ...f, freeHours: numberValue(value) }))}
              />
            ) : (
              <NumberInput
                label="Free days"
                min={0}
                value={form.freeDays}
                onChange={(value) => setForm((f) => ({ ...f, freeDays: numberValue(value) }))}
              />
            )}
            <NumberInput
              label={isDoubleHandling ? 'Rate / unit' : 'Rate / day'}
              min={0}
              value={form.ratePerDay}
              onChange={(value) => setForm((f) => ({ ...f, ratePerDay: numberValue(value) }))}
            />
            <Select
              label="Currency"
              data={CURRENCIES}
              value={form.currency}
              onChange={(value) => setForm((f) => ({ ...f, currency: selectValue(value, 'USD') }))}
              allowDeselect={false}
            />
          </Group>
          {isDoubleHandling && (
            <Text size="xs" c="dimmed">
              Flat charge — the rate is multiplied by the selected basis (
              {FEE_RULE_BASIS_LABELS[form.basis]}). No free days or progressive tiers.
            </Text>
          )}
          {isBulkRule && !isDoubleHandling && (
            <Text size="xs" c="dimmed">
              Bulk rate is per day, scaled by quantity — tons for tonnage cargo, item count for
              countable cargo (Machinery, Truck, Automobile, Livestock…), set on each cargo type.
            </Text>
          )}
          {!isDoubleHandling && (
          <Stack gap="xs">
            <Group justify="space-between">
              <Text size="sm" fw={600}>
                Progressive tariff tiers
              </Text>
              <Button variant="light" size="xs" leftSection={<Plus size={14} />} onClick={addTier}>
                Add tier
              </Button>
            </Group>
            {form.tiers.map((tier, index) => (
              <Group key={index} grow align="end">
                <NumberInput
                  label="From day"
                  min={1}
                  value={tier.fromDay}
                  onChange={(value) => updateTier(index, { fromDay: numberValue(value, 1) || 1 })}
                />
                <NumberInput
                  label="To day"
                  min={tier.fromDay}
                  value={tier.toDay ?? ''}
                  placeholder="Open"
                  onChange={(value) =>
                    updateTier(index, {
                      toDay: value === '' ? null : numberValue(value, tier.fromDay),
                    })
                  }
                />
                <NumberInput
                  label="Amount / day"
                  min={0}
                  value={tier.ratePerDay}
                  onChange={(value) => updateTier(index, { ratePerDay: numberValue(value) })}
                />
                <ActionIcon variant="subtle" color="red" onClick={() => removeTier(index)} title="Remove tier">
                  <Trash2 size={16} />
                </ActionIcon>
              </Group>
            ))}
            {form.tiers.length === 0 && (
              <Text size="xs" c="dimmed">
                No stepped tiers. The flat rate per day is used after the free days.
              </Text>
            )}
          </Stack>
          )}
          <Group justify="flex-end" mt="sm">
            <Button variant="default" onClick={() => { setOpen(false); resetForm(); }}>
              Cancel
            </Button>
            <Button loading={create.isPending || update.isPending} onClick={submit}>
              {editingId ? 'Save changes' : 'Create'}
            </Button>
          </Group>
        </Stack>
      </Modal>
    </>
  );
}
