import { useEffect, useMemo, useRef, useState } from 'react';
import { useForm, useHttp } from '@inertiajs/react';
import { AlertTriangle, ArrowRightLeft, Check, Info, Loader2, Pencil, Plus, Trash2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from '@/Components/ui/button';
import { DecisionBar } from '@/Components/MenuSampleOrders/DecisionBar';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/Components/ui/tooltip';
import {
    Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle,
} from '@/Components/ui/dialog';
import { FloatingField } from '@/Components/Proto/UI/FloatingField';
import { SearchableSelect } from '@/Components/Form/SearchableSelect';
import { useToast } from '@/Components/Toast';
import { useNumberFormat } from '@/Hooks/useNumberFormat';
import { formatGrouped } from '@/lib/currencyFormat';
import { RebateHistoryTable } from '@/Components/MenuCompanies/CompanyRebate/RebateHistoryTable';
import { NetSuitePanels } from '@/Components/MenuCompanies/CompanyRebate/NetSuitePanels';
import { DemoDataBadge } from '@/Components/NetSuite/DemoDataBadge';

/**
 * Create Company Rebate — shared body for the own / others / sm scopes; faithful port of
 * the legacy createcompanyrebate.php / createcompanyrebatesalesadmin.php /
 * createcompanyrebatesm.php forms (header cascades, CP bank rule, USD↔IDR cross-calc,
 * 1.25× actual-rebate gross-up, per-itemKey cumulative nett price, forbidden-item guard,
 * request history + NetSuite panels).
 *
 * Legacy client state ported 1:1:
 *  - arrayvaluerow  → accRef  (itemKey = principalCode+productCode+parseFloat(sellingUSD);
 *    accumulates the 2dp-rounded ActualRebateUSD; every row sharing the key shows
 *    NettPrice = SellingUSD − accumulated, in USD and ×rate in IDR);
 *  - iscpvoucher    → noBankRowsRef (count of rows whose CP has no complete bank account;
 *    blocks submit when Voucher is off);
 *  - recreate mode restores rows from `prefill` and (deviation, legacy left these NaN)
 *    seeds the accumulator/counter from the restored rows.
 */

const SECTION_CARD = 'flex flex-col overflow-visible rounded-xl border border-border bg-card shadow-sm';
const SECTION_HEADER = 'grid grid-cols-[auto_1fr] items-center gap-3 border-b border-border min-h-[60px] p-[16px_24px]';
const STEP_BADGE = 'inline-grid size-8 shrink-0 place-items-center rounded-md bg-accent text-[13px] font-bold tracking-[-0.01em] text-primary';
const SECTION_TITLE = 'm-0 text-base font-bold leading-[1.3] tracking-[-0.005em] text-card-foreground';
// List Rebate table — clean design-system shell: hairline rows, right-aligned money
// (USD bold + Rp small beneath), %Rebate pill; product/recipient/history merge into one cell.
const LTH = 'whitespace-nowrap border-b border-border px-3 py-2.5 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground';
const LTD = 'border-b border-border/60 px-3 py-3.5 text-right align-top text-[13px] text-foreground';

const FieldError = ({ msg }) => (msg ? <p className="mt-1 text-[11px] font-semibold text-danger">{msg}</p> : null);

// Space-free helper: a tiny (i) icon that reveals its note on hover/focus.
const InfoHint = ({ text }) => (
    <TooltipProvider delayDuration={150}>
        <Tooltip>
            <TooltipTrigger asChild>
                <button type="button" aria-label={text} className="inline-grid size-4 place-items-center rounded-full text-muted-foreground/70 transition-colors hover:text-primary">
                    <Info className="size-3.5" aria-hidden="true" />
                </button>
            </TooltipTrigger>
            <TooltipContent className="max-w-[280px] text-[11.5px] leading-snug">{text}</TooltipContent>
        </Tooltip>
    </TooltipProvider>
);

const toNum = (v) => parseFloat(String(v ?? '').replace(/,/g, '')) || 0;
const fixed2 = (n) => (Math.round((n + Number.EPSILON) * 100) / 100);
const fmt2 = (n) => formatGrouped(n, { decimals: 2 });
const fmt0 = (n) => formatGrouped(n, { decimals: 0 });

function monthEdges() {
    // Legacy computed these server-side in Asia/Jakarta — mirror that regardless of the
    // browser's local clock (en-CA locale formats as YYYY-MM-DD).
    const today = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Jakarta' }).format(new Date());
    const [y, m] = today.split('-').map(Number);
    const pad = (n) => String(n).padStart(2, '0');
    return {
        first: `${y}-${pad(m)}-01`,
        last: `${y}-${pad(m)}-${pad(new Date(y, m, 0).getDate())}`,
        today,
    };
}

/** Legacy itemlist key: idPrincipal + idBarang + parseFloat(sellingUSD). */
const itemKeyOf = (principalCode, productCode, sellingUsdRaw) =>
    `${principalCode}${productCode}${toNum(sellingUsdRaw)}`;

let rowSeq = 0;
const nextRowId = () => `rebate-row-${Date.now()}-${rowSeq++}`;

const SCOPE_LABELS = {
    own: 'My Companies',
    others: 'For Others',
    sm: 'SM (Head Division)',
};

export default function CreateRebatePage({ scopes, defaultScope, industries, companies: ownCompanies, salesOptions, prefill, reviseMode, reviseFromId }) {
    const { show: showToast } = useToast();
    const edges = monthEdges();
    const initialScope = prefill?.scope ?? defaultScope;

    const form = useForm({
        Scope: initialScope,
        UserIDSales: prefill?.salesId ?? null,
        CompanyID: prefill?.companyId ?? null,
        DivisionID: null,
        IndustryID: null,
        PaymentTermID: null,
        SpecialCondition: '',
        ValidityDateStart: edges.first,
        ValidityDateEnd: edges.last,
        USDRate: prefill != null ? String(prefill.usdRate) : '',
        IsVoucher: prefill?.isVoucher ?? false,
        Pencairan: prefill?.pencairan ?? '',
        Comment: prefill?.comment ?? '',
        items: (prefill?.items ?? []).map((it) => ({
            ...it,
            _id: nextRowId(),
            _itemKey: itemKeyOf(it.ASTPrincipalCode, it.ASTProductCode, it.SellingPriceUSD),
            _hasBank: !!it.hasBank,
        })),
    });
    const { data, setData, errors, processing } = form;

    // ---- legacy client-state ports ------------------------------------------------
    // arrayvaluerow: cumulative 2dp ActualRebateUSD per itemKey. Deviation from legacy:
    // recreate rows seed the accumulator (legacy left it NaN and Edit/Delete then corrupted
    // the nett fix-ups).
    const accRef = useRef({});
    // iscpvoucher: number of rows whose CP has no complete bank account.
    const noBankRowsRef = useRef(0);
    useEffect(() => {
        const acc = {};
        let noBank = 0;
        (data.items ?? []).forEach((row) => {
            acc[row._itemKey] = fixed2((acc[row._itemKey] ?? 0) + fixed2(toNum(row.ActualRebateUSD)));
            if (!row._hasBank) noBank += 1;
        });
        accRef.current = acc;
        noBankRowsRef.current = noBank;
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []); // initial (recreate) rows only — afterwards mutations maintain the refs

    // ---- companies (own: server prop; others/sm: fetched per selected sales) -------
    const [companies, setCompanies] = useState(
        prefill?.companies ?? (initialScope === 'own' ? ownCompanies ?? [] : [])
    );
    const companiesHttp = useHttp({});
    const salesRef = useRef(data.UserIDSales);
    salesRef.current = data.UserIDSales;

    const onSalesChange = (salesId) => {
        // Legacy onchange: showUser(0)+showCompanyDivision(0)+showHistoryRebate(0) reset
        // everything company-dependent, then the company list reloads for the sales.
        accRef.current = {};
        noBankRowsRef.current = 0;
        setData((d) => ({
            ...d, UserIDSales: salesId, CompanyID: null, DivisionID: null, IndustryID: null,
            PaymentTermID: null, SpecialCondition: '', items: [],
        }));
        setContext(null);
        setCompanies([]);
        clearEntry();
        companiesHttp.cancel();
        if (!salesId) return;
        companiesHttp.get(route('company-rebates.companies.by-sales', { user: salesId, scope: data.Scope }), {
            onSuccess: (resp) => {
                if (salesRef.current === salesId) setCompanies(resp?.companies ?? []);
            },
        });
    };

    // Switching the legacy-page scope (own / for-others / sm) resets the whole form
    // context — each legacy page was a separate blank form.
    const onScopeChange = (next) => {
        if (next === data.Scope) return;
        accRef.current = {};
        noBankRowsRef.current = 0;
        setContext(null);
        setCompanies(next === 'own' ? ownCompanies ?? [] : []);
        setData((d) => ({
            ...d, Scope: next, UserIDSales: null, CompanyID: null, DivisionID: null,
            IndustryID: null, PaymentTermID: null, SpecialCondition: '', items: [],
        }));
        clearEntry();
        companiesHttp.cancel();
    };

    // Switching company invalidates every company-scoped piece: header cascades, CP list,
    // history, forbidden map — and the drafted rows (their recipients belong to the old
    // company; legacy kept them, a data bug the FormRequest now rejects — deviation).
    const onCompanyChange = (id) => {
        accRef.current = {};
        noBankRowsRef.current = 0;
        setData((d) => ({
            ...d, CompanyID: id, DivisionID: null, IndustryID: null,
            PaymentTermID: null, SpecialCondition: '', items: [],
        }));
        clearEntry();
    };

    // ---- company context (info line, division/industry autoset, CC term, contacts) --
    const [context, setContext] = useState(null);
    const contextHttp = useHttp({});
    const companyRef = useRef(data.CompanyID);
    companyRef.current = data.CompanyID;

    useEffect(() => {
        contextHttp.cancel();
        setContext(null);
        if (!data.CompanyID) {
            setData((d) => ({ ...d, DivisionID: null, IndustryID: null, PaymentTermID: null, SpecialCondition: '' }));
            return;
        }
        const requested = data.CompanyID;
        // Legacy company change repopulates the CP dropdown (selection lost, details cleared)
        // and re-pulls the last price for a still-selected product (price is per customer).
        setEntry((e) => ({ ...e, CompanyCPID: null }));
        contextHttp.get(route('company-rebates.companies.context', { company: requested }), {
            onSuccess: (resp) => {
                if (companyRef.current !== requested) return;
                setContext(resp);
                // Legacy: getcompanydivision/getcompanyindustry auto-set the readonly selects;
                // getcompanyterm/getcompanycondition fill the hidden POST fields.
                setData((d) => ({
                    ...d,
                    DivisionID: resp?.divisionId ?? null,
                    IndustryID: resp?.industryId ?? null,
                    PaymentTermID: resp?.paymentTermId ?? null,
                    SpecialCondition: resp?.specialCondition ?? '',
                }));
            },
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data.CompanyID]);

    // Legacy showCompanyDivision: switching company re-pulls the NetSuite last price for the
    // product still selected in the entry block (nsCustomerId differs per company).
    useEffect(() => {
        if (entry.productCode) fetchLastPrice(entry.productCode);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data.CompanyID]);

    // ---- rebate request history ------------------------------------------------------
    const [history, setHistory] = useState([]);
    const [historyLoading, setHistoryLoading] = useState(false);
    const historyHttp = useHttp({});
    useEffect(() => {
        historyHttp.cancel();
        setHistory([]);
        if (!data.CompanyID) { setHistoryLoading(false); return; }
        const requested = data.CompanyID;
        setHistoryLoading(true);
        historyHttp.get(route('company-rebates.companies.history', { company: requested }), {
            onSuccess: (resp) => { if (companyRef.current === requested) setHistory(resp?.rows ?? []); },
            onFinish: () => { if (companyRef.current === requested) setHistoryLoading(false); },
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data.CompanyID]);

    // ---- forbidden items (already rebated in the validity window) ---------------------
    const [rebatedMap, setRebatedMap] = useState({});
    const rebatedHttp = useHttp({});
    useEffect(() => {
        rebatedHttp.cancel();
        setRebatedMap({});
        if (!data.CompanyID || !data.ValidityDateStart || !data.ValidityDateEnd) return;
        const requested = `${data.CompanyID}|${data.ValidityDateStart}|${data.ValidityDateEnd}`;
        rebatedHttp.get(route('company-rebates.companies.rebated-items', {
            company: data.CompanyID,
            start: data.ValidityDateStart,
            end: data.ValidityDateEnd,
            ...(prefill?.fromId ? { except: prefill.fromId } : {}),
        }), {
            onSuccess: (resp) => {
                const still = `${companyRef.current}|${data.ValidityDateStart}|${data.ValidityDateEnd}`;
                if (still === requested) setRebatedMap(resp?.items ?? {});
            },
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data.CompanyID, data.ValidityDateStart, data.ValidityDateEnd]);

    // ---- NetSuite catalog (nsproduct mirror of legacy getprincipal()/getbarang()) -----
    const [catalog, setCatalog] = useState({ principals: [], products: [] });
    const catalogHttp = useHttp({});
    useEffect(() => {
        catalogHttp.get(route('company-rebates.catalog'), {
            onSuccess: (resp) => setCatalog({ principals: resp?.principals ?? [], products: resp?.products ?? [] }),
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    // ---- detail entry (the legacy "Details" input block) -------------------------------
    const EMPTY_ENTRY = {
        CompanyCPID: null, principalCode: null, productCode: null,
        SellingPriceUSD: '', SellingPriceIDR: '', RebatePriceUSD: '', RebatePriceIDR: '', Remark: '',
    };
    const [entry, setEntry] = useState(EMPTY_ENTRY);
    const [addErrors, setAddErrors] = useState([]);
    const clearEntry = () => { setEntry(EMPTY_ENTRY); setLastPrice(null); setAddErrors([]); };

    const contacts = context?.contacts ?? [];
    const selectedCp = contacts.find((c) => c.id === entry.CompanyCPID) ?? null;

    const cpOptions = contacts.map((c) => ({
        id: c.id,
        name: c.name,
        // Legacy paints no-bank CPs red when Voucher is off (CPNoBank options).
        warn: !c.hasBank && !data.IsVoucher,
        subtext: c.hasBank ? undefined : 'tanpa rekening',
    }));

    const principalOptions = catalog.principals.map((p) => ({ id: p.code, name: p.name }));
    const productOptions = useMemo(() => {
        const source = entry.principalCode
            ? catalog.products.filter((p) => p.principalCode === entry.principalCode)
            : catalog.products;
        return source.map((p) => ({
            id: p.code,
            name: p.name,
            warn: rebatedMap[p.code] != null, // legacy "Forbidden" red marking
            subtext: rebatedMap[p.code] != null ? 'sudah di-rebate' : undefined,
        }));
    }, [catalog.products, entry.principalCode, rebatedMap]);
    const selectedProduct = catalog.products.find((p) => p.code === entry.productCode) ?? null;
    const forbiddenIds = entry.productCode != null ? rebatedMap[entry.productCode] ?? null : null;

    // ---- latest NetSuite sales price (seam-backed) --------------------------------------
    const [lastPrice, setLastPrice] = useState(null); // {rows,last,avg}
    const [lastPriceLoading, setLastPriceLoading] = useState(false);
    const lastPriceHttp = useHttp({});
    const nsCustomerId = context?.company?.nsCustomerId
        ?? companies.find((c) => c.id === data.CompanyID)?.nsCustomerId ?? '';
    const priceReqRef = useRef('');

    const fetchLastPrice = (productCode) => {
        lastPriceHttp.cancel();
        setLastPrice(null); // legacy clears InsertLastPriceAvg before the fetch
        if (!productCode) { setLastPriceLoading(false); return; }
        const requested = `${productCode}|${nsCustomerId}`;
        priceReqRef.current = requested;
        setLastPriceLoading(true);
        lastPriceHttp.get(route('company-rebates.last-price', { item: productCode, nsCustomerId }), {
            onSuccess: (resp) => { if (priceReqRef.current === requested) setLastPrice(resp); },
            onFinish: () => { if (priceReqRef.current === requested) setLastPriceLoading(false); },
        });
    };

    const onPrincipalChange = (code) => {
        // Legacy: choosing a principal narrows the product list and clears the selection.
        setEntry((e) => ({ ...e, principalCode: code, productCode: null }));
        setLastPrice(null);
    };

    const onProductChange = (code) => {
        // Legacy: choosing a product back-fills the principal and pulls the last prices.
        const product = catalog.products.find((p) => p.code === code) ?? null;
        setEntry((e) => ({ ...e, productCode: code, principalCode: product?.principalCode ?? e.principalCode }));
        fetchLastPrice(code);
    };

    // ---- USD↔IDR cross-calculation (legacy calculate* + recalculateIDR) ----------------
    const rate = toNum(data.USDRate);

    const onRateChange = (raw) => {
        const r = toNum(raw);
        // Entry: recompute both IDR fields from USD (legacy oninput on the rate).
        setEntry((e) => ({
            ...e,
            SellingPriceIDR: e.SellingPriceUSD !== '' ? (toNum(e.SellingPriceUSD) * r).toFixed(2) : e.SellingPriceIDR,
            RebatePriceIDR: e.RebatePriceUSD !== '' ? (toNum(e.RebatePriceUSD) * r).toFixed(2) : e.RebatePriceIDR,
        }));
        // Rows: legacy recalculateIDR rewrites Selling/Rebate/Actual/Nett IDR = rate × USD.
        setData((d) => ({
            ...d,
            USDRate: raw,
            items: d.items.map((it) => ({
                ...it,
                SellingPriceIDR: (toNum(it.SellingPriceUSD) * r).toFixed(2),
                RebatePriceIDR: (toNum(it.RebatePriceUSD) * r).toFixed(2),
                ActualRebateIDR: (toNum(it.ActualRebateUSD) * r).toFixed(2),
                NettPriceIDR: (toNum(it.NettPriceUSD) * r).toFixed(2),
            })),
        }));
    };
    const usdRateField = useNumberFormat({ value: data.USDRate, onChange: onRateChange, decimals: 2 });

    // Entry price fields accept up to 5 decimals — legacy FormatCurrency never capped the
    // decimal tail and the decimal(20,5) columns keep 5 places (real legacy rows carry
    // 3-decimal rebates). Derived counterparts stay 2dp like the legacy calculate* fns.
    const sellUsdField = useNumberFormat({
        value: entry.SellingPriceUSD, decimals: 5,
        onChange: (raw) => setEntry((e) => ({ ...e, SellingPriceUSD: raw, SellingPriceIDR: rate > 0 && raw !== '' ? (toNum(raw) * rate).toFixed(2) : e.SellingPriceIDR })),
    });
    const sellIdrField = useNumberFormat({
        value: entry.SellingPriceIDR, decimals: 5,
        onChange: (raw) => setEntry((e) => ({ ...e, SellingPriceIDR: raw, SellingPriceUSD: rate > 0 && raw !== '' ? (toNum(raw) / rate).toFixed(2) : e.SellingPriceUSD })),
    });
    const rebUsdField = useNumberFormat({
        value: entry.RebatePriceUSD, decimals: 5,
        onChange: (raw) => setEntry((e) => ({ ...e, RebatePriceUSD: raw, RebatePriceIDR: rate > 0 && raw !== '' ? (toNum(raw) * rate).toFixed(2) : e.RebatePriceIDR })),
    });
    const rebIdrField = useNumberFormat({
        value: entry.RebatePriceIDR, decimals: 5,
        onChange: (raw) => setEntry((e) => ({ ...e, RebatePriceIDR: raw, RebatePriceUSD: rate > 0 && raw !== '' ? (toNum(raw) / rate).toFixed(2) : e.RebatePriceUSD })),
    });

    // ---- per-itemKey nett fix-up (legacy fixingnettpriceamount) --------------------------
    const applyNettFix = (items, key, accumulated) => items.map((it) => {
        if (it._itemKey !== key) return it;
        const nettUsd = toNum(it.SellingPriceUSD) - accumulated;
        return { ...it, NettPriceUSD: nettUsd.toFixed(2), NettPriceIDR: (nettUsd * rate).toFixed(2) };
    });

    // ---- Add Item (legacy addRowWithValue1) ----------------------------------------------
    const addItem = () => {
        const msgs = [];
        const sellUsd = entry.SellingPriceUSD;
        const sellIdr = entry.SellingPriceIDR;
        const rebUsd = entry.RebatePriceUSD;
        const rebIdr = entry.RebatePriceIDR;
        const remark = entry.Remark;
        const isVoucher = !!data.IsVoucher;
        const forbidden = forbiddenIds != null;

        if (data.USDRate === '' || rate === 0) msgs.push('USD Rate Kosong.');
        if (!entry.productCode) msgs.push('Silahkan pilih barang.');
        if (forbidden) msgs.push('Barang sudah pernah di Rebate.');
        if (sellUsd === '' || toNum(sellUsd) === 0) msgs.push('Unit Price USD Kosong.');
        if (sellIdr === '' || toNum(sellIdr) === 0) msgs.push('Unit Price IDR Kosong.');
        if (rebUsd === '' || toNum(rebUsd) === 0) msgs.push('Rebate USD Kosong.');
        if (rebIdr === '' || toNum(rebIdr) === 0) msgs.push('Rebate IDR Kosong.');
        // selectedCp guard also catches a stale id after any company-scoped reset slipped by.
        if (!entry.CompanyCPID || selectedCp == null) msgs.push('Silahkan pilih Company CP.');
        if (!entry.principalCode) msgs.push('Silahkan pilih Principal.');
        if (remark === '') msgs.push('Remark Kosong.');
        if (remark.length > 0 && remark.length < 20) msgs.push('Remark Kurang dari 20 huruf.');
        if (!isVoucher && selectedCp != null && !selectedCp.hasBank) msgs.push('CompanyCP Tidak Memiliki Bank.');
        // Legacy blocked while the Avg Price field was still empty — i.e. also when the
        // NetSuite fetch failed, not only while it is in flight.
        if (msgs.length === 0 && (lastPriceLoading || lastPrice == null)) msgs.push('Mohon tunggu sebentar.');

        if (msgs.length > 0) {
            setAddErrors(msgs);
            showToast('Item belum bisa ditambahkan — periksa input.', 'error');
            return;
        }
        setAddErrors([]);

        // Legacy computations (1.25× gross-up; nett = selling − actual; % on USD).
        const actualUsd = toNum(rebUsd) * 1.25;
        const actualIdr = toNum(rebIdr) * 1.25;
        const nettUsd = toNum(sellUsd) - actualUsd;
        const nettIdr = toNum(sellIdr) - actualIdr;
        const pct = (actualUsd / toNum(sellUsd)) * 100;
        const key = itemKeyOf(entry.principalCode, entry.productCode, sellUsd);
        const principal = catalog.principals.find((p) => p.code === entry.principalCode);
        const last = lastPrice?.last ?? { price: 0, date: '' };
        const cp = selectedCp;

        const row = {
            _id: nextRowId(),
            _itemKey: key,
            _hasBank: !!cp?.hasBank,
            CompanyCPID: cp.id,
            Recipient: `${cp.name} / ${cp.position} / ${cp.bankName} - ${cp.accountName} (${cp.accountNo})`,
            CPName: cp.name,
            CPPosition: cp.position,
            CPBankName: cp.bankName,
            CPAccountName: cp.accountName,
            CPAccountNo: cp.accountNo,
            ASTPrincipalCode: entry.principalCode,
            PrincipalName: principal?.name ?? selectedProduct?.principalName ?? '',
            ASTProductCode: entry.productCode,
            ProductName: selectedProduct?.name ?? '',
            LastPriceUSD: String(last.price ?? 0),
            LastPriceDate: last.date ?? '',
            AveragePrice: String(lastPrice?.avg ?? 0),
            // Selling/Rebate keep the typed precision (legacy POSTs the raw inputs into
            // decimal(20,5)); only the derived Actual/Nett cells are 2dp like legacy.
            SellingPriceUSD: String(toNum(sellUsd)),
            SellingPriceIDR: String(toNum(sellIdr)),
            RebatePriceUSD: String(toNum(rebUsd)),
            RebatePriceIDR: String(toNum(rebIdr)),
            ActualRebateUSD: actualUsd.toFixed(2),
            ActualRebateIDR: actualIdr.toFixed(2),
            NettPriceUSD: nettUsd.toFixed(2),
            NettPriceIDR: nettIdr.toFixed(2),
            RebatePercentage: String(pct),
            Remark: remark,
        };

        // Legacy accumulator flow: append row, add this row's 2dp actual to the key's
        // total, then rewrite every matching row's nett to selling − accumulated. The add
        // uses the row's STORED value so delete/edit subtraction can never drift from it.
        // NB the rewrite fires whenever the key EXISTED (legacy `itemlist in arrayvaluerow`)
        // — including at value 0 after Edit/Delete removed the key's last row, where legacy
        // still rewrites nett to (selling − round2 actual) with USD-derived IDR.
        const keyExisted = Object.prototype.hasOwnProperty.call(accRef.current, key);
        const accBefore = accRef.current[key] ?? 0;
        const accAfter = fixed2(accBefore + fixed2(toNum(row.ActualRebateUSD)));
        accRef.current = { ...accRef.current, [key]: accAfter };
        if (isVoucher && !cp.hasBank) noBankRowsRef.current += 1;

        setData((d) => {
            let items = [...d.items, row];
            if (keyExisted) items = applyNettFix(items, key, accAfter);
            return { ...d, items };
        });
        clearEntry();
    };

    // Shared row-removal accounting for Delete and Edit (legacy delRow1/editRow1 halves).
    const removeRowAccounting = (row) => {
        const key = row._itemKey;
        const next = fixed2((accRef.current[key] ?? 0) - toNum(row.ActualRebateUSD));
        accRef.current = { ...accRef.current, [key]: next };
        if (!row._hasBank) noBankRowsRef.current = Math.max(0, noBankRowsRef.current - 1);
        return next;
    };

    const deleteRow = (row) => {
        const accAfter = removeRowAccounting(row);
        setData((d) => ({
            ...d,
            items: applyNettFix(d.items.filter((it) => it._id !== row._id), row._itemKey, accAfter),
        }));
    };

    const editRow = (row) => {
        // Legacy editRow1: push the row's values back into the entry fields, then drop the row.
        setEntry({
            CompanyCPID: row.CompanyCPID,
            principalCode: row.ASTPrincipalCode,
            productCode: row.ASTProductCode,
            SellingPriceUSD: String(toNum(row.SellingPriceUSD)),
            SellingPriceIDR: String(toNum(row.SellingPriceIDR)),
            RebatePriceUSD: String(toNum(row.RebatePriceUSD)),
            RebatePriceIDR: String(toNum(row.RebatePriceIDR)),
            Remark: row.Remark,
        });
        fetchLastPrice(row.ASTProductCode);
        const accAfter = removeRowAccounting(row);
        setData((d) => ({
            ...d,
            items: applyNettFix(d.items.filter((it) => it._id !== row._id), row._itemKey, accAfter),
        }));
    };

    // ---- submit (legacy validateForm + confirm) -------------------------------------------
    const [confirmOpen, setConfirmOpen] = useState(false);
    const [submitErrors, setSubmitErrors] = useState([]);

    const validateForm = () => {
        const msgs = [];
        if ((data.items ?? []).length <= 0) msgs.push('List Rebate Kosong.');
        if (!data.DivisionID) msgs.push('Division Kosong.');
        if (!data.IndustryID) msgs.push('Industry Kosong.');
        if (!data.CompanyID) msgs.push('Company Kosong.');
        if (!data.ValidityDateStart) msgs.push('Validity Date (Start) Kosong.');
        if (!data.ValidityDateEnd) msgs.push('Validity Date (End) Kosong.');
        if (data.USDRate === '' || rate === 0) msgs.push('USD Rate Kosong.');
        if (!data.IsVoucher && noBankRowsRef.current !== 0) msgs.push('CompanyCP pada Detail Tidak Memiliki Bank.');
        if (data.ValidityDateStart && data.ValidityDateEnd
            && Date.parse(data.ValidityDateStart) >= Date.parse(data.ValidityDateEnd)) {
            msgs.push('Validity Start Date Melebihi Validity End Date.');
        }
        if (data.Scope !== 'own' && !data.UserIDSales) msgs.push('Sales Kosong.');
        return msgs;
    };

    const onSubmitClick = () => {
        const msgs = validateForm();
        if (msgs.length > 0) {
            setSubmitErrors(msgs);
            showToast('Form belum lengkap — periksa kembali.', 'error');
            return;
        }
        setSubmitErrors([]);
        setConfirmOpen(true); // legacy confirm('Create Rebate?')
    };

    const submit = () => {
        setConfirmOpen(false);
        // In revise mode, POST to the revise route with the original rebate ID.
        const submitRoute = reviseMode && reviseFromId
            ? route('company-rebates.revise.store', { rebate: reviseFromId })
            : route('company-rebates.store');
        const successMessage = reviseMode
            ? 'Berhasil melakukan Revise Company Rebate.'
            : 'Berhasil melakukan Create Company Rebate.';

        form.post(submitRoute, {
            // Success redirects back to the create page (remount for pristine form).
            preserveState: 'errors',
            onError: () => showToast('Please check the form and try again.', 'error'),
        });
    };

    // First store validation error (overlap / server-side rules) shown near the table.
    const itemsError = errors.items
        ?? Object.entries(errors).find(([k]) => k.startsWith('items.'))?.[1]
        ?? null;

    const infoLine = context != null ? [
        `Address : ${context.company?.address || '-'}`,
        `Telephone : ${context.company?.telephone || '-'}`,
        `Payment Term : ${context.paymentTermName || '-'}`,
        ...(context.specialCondition ? [`Special Condition : ${context.specialCondition}`] : []),
    ].join(' // ') : null;

    const industryOptions = (industries ?? []).map((i) => ({ id: i.id, name: i.name }));

    // Live "Nett Price" preview for the entry block — mirrors the exact math addItem will
    // store (1.25× actual-rebate gross-up). Display only; nothing is posted from here.
    const pvSellUsd = toNum(entry.SellingPriceUSD);
    const pvActualUsd = fixed2(toNum(entry.RebatePriceUSD) * 1.25);
    const pvNettUsd = pvSellUsd - pvActualUsd;
    const pvNettIdr = toNum(entry.SellingPriceIDR) - toNum(entry.RebatePriceIDR) * 1.25;
    const pvPct = pvSellUsd > 0 ? (pvActualUsd / pvSellUsd) * 100 : 0;
    const remarkLen = entry.Remark.trim().length;

    return (
        <div className="flex w-full min-w-0 flex-col gap-5">
            <header className="flex flex-wrap items-end justify-between gap-4">
                <div>
                    <p className="m-0 flex items-center gap-1.5 text-xs font-semibold text-muted-foreground">
                        <span>Company</span>
                        <span aria-hidden="true">›</span>
                        <span>Company Rebate</span>
                        <span aria-hidden="true">›</span>
                        <span className="text-foreground">{reviseMode ? 'Revise Request' : 'Create Request'}</span>
                    </p>
                    <h1 className="m-0 text-xl font-bold leading-tight text-card-foreground">
                        {reviseMode ? 'Revise Company Rebate' : 'Create Company Rebate'}
                    </h1>
                </div>
                {(scopes ?? []).length > 1 && (
                    <div className="flex gap-1 rounded-xl border border-border bg-card p-1" role="tablist" aria-label="Rebate scope">
                        {scopes.map((sc) => (
                            <button key={sc} type="button" role="tab" aria-selected={data.Scope === sc} onClick={() => onScopeChange(sc)}
                                className={cn('rounded-lg px-3.5 py-1.5 text-[12px] font-semibold transition-colors',
                                    data.Scope === sc ? 'bg-accent text-primary' : 'text-muted-foreground hover:bg-secondary hover:text-foreground')}>
                                {SCOPE_LABELS[sc] ?? sc}
                            </button>
                        ))}
                    </div>
                )}
            </header>

            {submitErrors.length > 0 && (
                <div className="rounded-lg border border-danger/40 bg-danger/5 px-4 py-3">
                    <ul className="m-0 list-disc pl-4 text-[12px] font-semibold text-danger">
                        {submitErrors.map((m) => <li key={m}>{m}</li>)}
                    </ul>
                </div>
            )}

            {/* ── 1 Data Rebate | 2 Tambah Item — side by side, stretched level ───── */}
            <div className="grid grid-cols-1 items-stretch gap-5 min-[1200px]:grid-cols-2">

            <section className={`${SECTION_CARD} p-6`} aria-labelledby="s1-title">
                <div className="mb-4 flex items-start gap-3">
                    <span className={STEP_BADGE}>1</span>
                    <h2 id="s1-title" className={SECTION_TITLE}>Data Rebate</h2>
                </div>

                <div className="mb-4 flex items-start gap-2.5 rounded-lg border border-warning-border bg-warning-bg px-3.5 py-2.5 text-[12.5px] leading-snug text-warning-text" role="note">
                    <AlertTriangle className="mt-0.5 size-4 shrink-0" aria-hidden="true" />
                    <span>Company berwarna merah belum tersinkronisasi dengan NetSuite — silakan hubungi IT.</span>
                </div>

                {/* uniform 16px rhythm; any height delta vs card 2 rests quietly below the last row */}
                <div className="flex flex-1 flex-col gap-4">
                    {data.Scope !== 'own' && (
                        <div>
                            <SearchableSelect
                                label="Sales *"
                                placeholder="Select Sales"
                                options={(salesOptions?.[data.Scope] ?? []).map((u) => ({ id: u.id, name: u.name }))}
                                value={data.UserIDSales}
                                onChange={onSalesChange}
                            />
                            <FieldError msg={errors.UserIDSales} />
                        </div>
                    )}
                    <div>
                        <SearchableSelect
                            label="Company *"
                            placeholder="Select Company"
                            options={companies}
                            value={data.CompanyID}
                            onChange={onCompanyChange}
                            disabled={data.Scope !== 'own' && !data.UserIDSales}
                        />
                        {infoLine != null && <p className="mt-1 text-[11px] italic text-muted-foreground">{infoLine}</p>}
                        <FieldError msg={errors.CompanyID} />
                    </div>
                    <div className="grid grid-cols-2 gap-4">
                        <div>
                            <FloatingField
                                as="select" label="Division *" value={data.DivisionID ?? ''} disabled onChange={() => {}}
                                options={[
                                    { value: '', label: 'Select Division' },
                                    ...(context?.divisionId != null
                                        ? [{ value: context.divisionId, label: context.divisionName ?? '' }] : []),
                                ]}
                            />
                            <FieldError msg={errors.DivisionID} />
                        </div>
                        <div>
                            <FloatingField
                                as="select" label="Industry *" value={data.IndustryID ?? ''} disabled onChange={() => {}}
                                options={[{ value: '', label: 'Select Industry' },
                                    ...industryOptions.map((i) => ({ value: i.id, label: i.name }))]}
                            />
                            <FieldError msg={errors.IndustryID} />
                        </div>
                    </div>
                    {data.Scope === 'sm' && (
                        <FloatingField label="Date *" type="date" value={edges.today} readOnly onChange={() => {}} />
                    )}
                    <div className="grid grid-cols-2 gap-4">
                        <div>
                            <FloatingField
                                label="Validity Date (Start) *" type="date" value={data.ValidityDateStart}
                                onChange={(e) => setData('ValidityDateStart', e.target.value)}
                            />
                            <FieldError msg={errors.ValidityDateStart} />
                        </div>
                        <div>
                            <FloatingField
                                label="Validity Date (End) *" type="date" value={data.ValidityDateEnd}
                                onChange={(e) => setData('ValidityDateEnd', e.target.value)}
                            />
                            <FieldError msg={errors.ValidityDateEnd} />
                        </div>
                    </div>
                    <div className="grid grid-cols-2 gap-4">
                        <div>
                            <div className="relative">
                                <FloatingField label="USD Rate *" alwaysFloat {...usdRateField} style={{ textAlign: 'right' }} autoComplete="off" className="[&_input]:pl-9" />
                                <ArrowRightLeft aria-hidden="true" className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
                            </div>
                            <p className="mt-1 text-[10px] text-muted-foreground">Ex: 15,100.99</p>
                            <FieldError msg={errors.USDRate} />
                        </div>
                        {/* A bordered box, not naked text. It shares a grid row with USD Rate, and a
                            bare label beside a bordered input made the row read as one control plus
                            a stray caption. Same treatment the Multinational / Order-with-PO toggles
                            carry: outlined when off, primary-tinted when on. */}
                        <label className={cn(
                            'flex h-11 cursor-pointer select-none items-center gap-2.5 rounded-md border px-3.5 text-[12px] font-semibold transition-colors',
                            data.IsVoucher
                                ? 'border-primary/30 bg-accent text-primary'
                                : 'border-input bg-card text-foreground hover:border-primary/60',
                        )}>
                            <input
                                type="checkbox"
                                className="sr-only"
                                checked={!!data.IsVoucher}
                                onChange={(e) => setData('IsVoucher', e.target.checked)}
                            />
                            <span aria-hidden="true" className={`grid size-[18px] shrink-0 place-items-center rounded-[5px] border transition-colors ${data.IsVoucher ? 'border-primary bg-primary text-primary-foreground' : 'border-input bg-card hover:border-primary/60'}`}>
                                {data.IsVoucher && <Check className="size-3" strokeWidth={3.5} />}
                            </span>
                            Voucher
                        </label>
                    </div>
                    <FloatingField
                        as="textarea" rows={2} label="Remark Pencairan" value={data.Pencairan} autoComplete="off"
                        onChange={(e) => setData('Pencairan', e.target.value)}
                    />
                    <FloatingField
                        as="textarea" rows={2} label="Comment" value={data.Comment} autoComplete="off"
                        onChange={(e) => setData('Comment', e.target.value)}
                    />
                </div>
            </section>

            {/* ── 2 · Tambah Item ─────────────────────────────────────────────────── */}
            <section className={`${SECTION_CARD} p-6`} aria-labelledby="s2-title">
                <div className="mb-4 flex items-start gap-3">
                    <span className={STEP_BADGE}>2</span>
                    <h2 id="s2-title" className={SECTION_TITLE}>Tambah Item</h2>
                </div>

                <div className="flex flex-col gap-4">
                    <div className="grid grid-cols-2 gap-4 max-[560px]:grid-cols-1">
                        <div>
                            <SearchableSelect
                                label="Company CP *" placeholder="Select CompanyCP"
                                options={cpOptions} value={entry.CompanyCPID}
                                onChange={(id) => setEntry((e) => ({ ...e, CompanyCPID: id }))}
                                disabled={!data.CompanyID}
                            />
                            {selectedCp != null && (
                                <p className={cn('mt-1 text-[11px] font-semibold',
                                    !selectedCp.hasBank && !data.IsVoucher ? 'text-danger' : 'text-muted-foreground')}>
                                    {!selectedCp.hasBank && 'CP Tidak Memiliki Rekening | '}
                                    Address : {selectedCp.address || '-'} | Telephone : {selectedCp.telephone || '-'}
                                </p>
                            )}
                        </div>
                        <SearchableSelect
                            label="Principal *" placeholder="Select Principal"
                            options={principalOptions} value={entry.principalCode}
                            onChange={onPrincipalChange}
                        />
                    </div>
                    <div>
                        <div className="relative">
                            <SearchableSelect
                                label="Product Name *" placeholder="Select Product Name"
                                options={productOptions} value={entry.productCode}
                                onChange={onProductChange}
                            />
                            <span className="absolute right-9 top-1/2 -translate-y-1/2">
                                <InfoHint text="Product Name yang dipilih adalah nama asli sebelum proses re-label." />
                            </span>
                        </div>
                        {forbiddenIds != null && (
                            <p className="mt-1 text-[11px] font-semibold text-danger">
                                Item Has Been Made In ID : {forbiddenIds.join(',')}
                            </p>
                        )}
                    </div>

                    <div>
                        <h3 className="m-0 mb-2 flex items-center gap-1.5 text-[13px] font-semibold text-muted-foreground">
                            Histori harga
                            <InfoHint text="Terisi otomatis dari produk terpilih." />
                        </h3>
                        {/* issue #187 — NETSUITE_LASTPRICE_FIXTURE prices are fabricated; say so. */}
                        <DemoDataBadge show={Boolean(lastPrice?.isDemoData)} what="The prices below" />
                        <div className="grid grid-cols-3 gap-3 max-[560px]:grid-cols-1">
                            {[
                                ['Last Price', lastPriceLoading ? '…' : lastPrice != null ? `$${fmt2(lastPrice.last.price)}` : '—'],
                                ['Last Date', lastPriceLoading ? '…' : lastPrice?.last.date || '—'],
                                ['Avg Price', lastPriceLoading ? '…' : lastPrice != null ? `$${fmt2(lastPrice.avg)}` : '—'],
                            ].map(([k, v]) => (
                                <div key={k} className="rounded-lg bg-secondary/40 px-3.5 py-2.5">
                                    <span className="block text-[11px] text-muted-foreground">{k}</span>
                                    <strong className="mt-0.5 block text-[15px] font-semibold tabular-nums text-foreground">{v}</strong>
                                </div>
                            ))}
                        </div>
                        {lastPrice != null && lastPrice.rows.length > 0 && (
                            <p className="m-0 mt-1.5 text-[11px] text-muted-foreground">
                                Latest Prices : $ {lastPrice.rows.map((r) => `${fmt2(r.price)}//${r.date}`).join(' || ')}
                            </p>
                        )}
                    </div>

                    {/* Harga per unit — inset panel + live nett preview (same math as Add Item) */}
                    <div className="rounded-xl bg-secondary/30 p-4">
                        <h3 className="m-0 mb-3 flex items-center gap-1.5 text-[13px] font-semibold text-muted-foreground">
                            Harga per unit
                            <InfoHint text="Kolom IDR terhitung otomatis dari USD Rate — bisa dioverride manual." />
                        </h3>
                        <div className="flex flex-col gap-3.5">
                            <div className="grid grid-cols-2 gap-3.5 max-[560px]:grid-cols-1">
                                <FloatingField pageBg label="Selling Price $ *" {...sellUsdField} onBlur={undefined} style={{ textAlign: 'right' }} autoComplete="off" />
                                <FloatingField pageBg label="Selling Price IDR *" {...sellIdrField} onBlur={undefined} style={{ textAlign: 'right' }} autoComplete="off" />
                            </div>
                            <div className="grid grid-cols-2 gap-3.5 max-[560px]:grid-cols-1">
                                <FloatingField pageBg label="Rebate $ *" {...rebUsdField} onBlur={undefined} style={{ textAlign: 'right' }} autoComplete="off" />
                                <FloatingField pageBg label="Rebate IDR *" {...rebIdrField} onBlur={undefined} style={{ textAlign: 'right' }} autoComplete="off" />
                            </div>
                            <div className="flex flex-wrap items-center justify-between gap-2 border-t border-border/60 pt-3 text-[12.5px] text-muted-foreground">
                                <span>Nett Price <span className="text-[10.5px]">(setelah gross-up 1.25×)</span></span>
                                <span className="flex items-center gap-2">
                                    <b className="text-[13.5px] font-semibold tabular-nums text-foreground">
                                        {pvSellUsd > 0 ? `$${fmt2(pvNettUsd)} · Rp ${fmt0(pvNettIdr)}` : '—'}
                                    </b>
                                    {pvSellUsd > 0 && (
                                        <span className="inline-flex rounded-full bg-accent px-2.5 py-0.5 text-[11px] font-bold tabular-nums text-primary">
                                            rebate {pvPct.toFixed(1)}%
                                        </span>
                                    )}
                                </span>
                            </div>
                        </div>
                    </div>

                    <div>
                        <FloatingField
                            label="Remark *" value={entry.Remark} autoComplete="off"
                            onChange={(e) => setEntry((en) => ({ ...en, Remark: e.target.value }))}
                        />
                        <div className="mt-1 flex items-center justify-between gap-2 text-[10.5px]">
                            <p className="m-0 text-muted-foreground">Minimal 20 huruf</p>
                            <p className={cn('m-0 tabular-nums', remarkLen > 0 && remarkLen < 20 ? 'font-semibold text-danger' : 'text-muted-foreground')}>{remarkLen} huruf</p>
                        </div>
                    </div>
                    {addErrors.length > 0 && (
                        <ul className="m-0 list-disc pl-4 text-[12px] font-semibold text-danger">
                            {addErrors.map((m) => <li key={m}>{m}</li>)}
                        </ul>
                    )}
                    {/* justify-START, and gradient. Both were violations: "NO ACTION-BUTTON ROW MAY
                        BE RIGHT-ALIGNED", and the gradient rule lists "Add Barang" by name among the
                        primaries that must not be flattened — this is that button. */}
                    <div className="flex justify-start">
                        <Button type="button" onClick={addItem} className="h-9 rounded-lg px-4 text-[13px] font-bold">
                            <Plus className="mr-1 size-4" />Tambah Item
                        </Button>
                    </div>
                </div>
            </section>
            </div>

            {/* ── 3 · List Rebate ─────────────────────────────────────────────────── */}
            <section className={`${SECTION_CARD} p-6`} aria-labelledby="s3-title">
                <div className="mb-2 flex items-center gap-3">
                    <span className={STEP_BADGE}>3</span>
                    <h2 id="s3-title" className={SECTION_TITLE}>List Rebate</h2>
                    <span className="inline-flex rounded-full bg-accent px-2.5 py-0.5 text-[11px] font-bold tabular-nums text-primary">
                        {data.items.length} item
                    </span>
                </div>
                <div className="overflow-x-auto">
                    {itemsError != null && <FieldError msg={itemsError} />}
                    <table className="w-full min-w-[960px] border-collapse [&_tbody_tr:last-child_td]:border-b-0 [&_tbody_tr:hover]:bg-secondary/60">
                        <thead>
                            <tr>
                                <th className={`${LTH} text-left`}>Produk</th>
                                <th className={LTH}>Selling Price</th>
                                <th className={LTH}>Rebate Price</th>
                                <th className={LTH}>Actual Rebate</th>
                                <th className={LTH}>Nett Price</th>
                                <th className={LTH}>%Rebate</th>
                                <th className={`${LTH} text-left`}>Remark</th>
                                <th className={LTH}><span className="sr-only">Aksi</span></th>
                            </tr>
                        </thead>
                        <tbody>
                            {data.items.length === 0 && (
                                <tr>
                                    <td colSpan={8} className="px-3 py-8 text-center text-[13px] italic text-muted-foreground">
                                        Belum ada item — tambahkan lewat form Tambah Item di atas.
                                    </td>
                                </tr>
                            )}
                            {data.items.map((row) => (
                                <tr key={row._id}>
                                    <td className={`${LTD} min-w-[220px] text-left`} title={row.Recipient}>
                                        <p className="m-0 text-[13.5px] font-semibold text-foreground">{row.PrincipalName} — {row.ProductName}</p>
                                        <p className="m-0 mt-0.5 text-[11.5px] text-muted-foreground">{row.CPName} · {row.CPPosition}</p>
                                        <p className="m-0 mt-0.5 text-[11px] text-muted-foreground/80 tabular-nums">
                                            Last ${fmt2(toNum(row.LastPriceUSD))}{row.LastPriceDate ? ` (${row.LastPriceDate})` : ''} · Avg ${fmt2(toNum(row.AveragePrice))}
                                        </p>
                                    </td>
                                    <td className={LTD}>
                                        <div className="font-semibold tabular-nums">${fmt2(toNum(row.SellingPriceUSD))}</div>
                                        <div className="mt-0.5 text-[11px] tabular-nums text-muted-foreground">Rp {fmt0(toNum(row.SellingPriceIDR))}</div>
                                    </td>
                                    <td className={LTD}>
                                        <div className="font-semibold tabular-nums">${fmt2(toNum(row.RebatePriceUSD))}</div>
                                        <div className="mt-0.5 text-[11px] tabular-nums text-muted-foreground">Rp {fmt0(toNum(row.RebatePriceIDR))}</div>
                                    </td>
                                    <td className={LTD}>
                                        <div className="font-semibold tabular-nums">${fmt2(toNum(row.ActualRebateUSD))}</div>
                                        <div className="mt-0.5 text-[11px] tabular-nums text-muted-foreground">Rp {fmt0(toNum(row.ActualRebateIDR))}</div>
                                    </td>
                                    <td className={LTD}>
                                        <div className="font-semibold tabular-nums">${fmt2(toNum(row.NettPriceUSD))}</div>
                                        <div className="mt-0.5 text-[11px] tabular-nums text-muted-foreground">Rp {fmt0(toNum(row.NettPriceIDR))}</div>
                                    </td>
                                    <td className={LTD}>
                                        <span className="inline-flex rounded-full bg-accent px-2.5 py-0.5 text-[11px] font-bold tabular-nums text-primary">
                                            {toNum(row.RebatePercentage).toFixed(1)}%
                                        </span>
                                    </td>
                                    <td className={`${LTD} max-w-[220px] text-left`}>
                                        <span className="line-clamp-2 whitespace-normal text-[12px] leading-snug text-muted-foreground" title={row.Remark}>{row.Remark}</span>
                                    </td>
                                    <td className={`${LTD} whitespace-nowrap`}>
                                        <Button type="button" variant="ghost" size="icon-sm" onClick={() => editRow(row)} aria-label="Edit item">
                                            <Pencil className="size-3.5" />
                                        </Button>
                                        <Button type="button" variant="ghost" size="icon-sm" onClick={() => deleteRow(row)}
                                            aria-label="Delete item" className="text-danger hover:text-danger">
                                            <Trash2 className="size-3.5" />
                                        </Button>
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            </section>

            {/* ── Rebate Request History (info panel — full width; can run long) ── */}
            <article className={SECTION_CARD}>
                <div className={SECTION_HEADER}>
                    <span className={STEP_BADGE}>4</span>
                    <h2 className={SECTION_TITLE}>Rebate Request History</h2>
                </div>
                <div className="p-6 pt-4">
                    <RebateHistoryTable rows={history} loading={historyLoading} hasCompany={!!data.CompanyID} />
                </div>
            </article>

            {/* ── NetSuite Integration (info panel) ───────────────────────────────── */}
            <article className={SECTION_CARD}>
                <div className={SECTION_HEADER}>
                    <span className={STEP_BADGE}>5</span>
                    <h2 className={SECTION_TITLE}>Netsuite Integration</h2>
                </div>
                <div className="p-6 pt-4">
                    <NetSuitePanels companyId={data.CompanyID} nsCustomerId={nsCustomerId} />
                </div>
            </article>

            {/* ── Floating decision pill — the app-wide shape for a long record page ──
                This used to be a full-width sticky DOCK, and it broke three locked rules at once
                (`.claude/rules/ui-conventions.md`, reported 2026-08-24 as "beda sama yang lain"):
                  · a dock, not the centred pill — the pill exists precisely so it never runs
                    under the sidebar, and every other long page here uses it;
                  · `justify-between` pushed the buttons to the RIGHT, which the file calls out
                    by name as a regression to fix, not to copy;
                  · Batal came BEFORE the primary. Primary-first is the rule; the Batal-first
                    carve-out is only for DESTRUCTIVE confirmations, and creating a rebate is
                    not one — the destructive-order dialog is the confirm step below.
                DecisionBar also carries its own spacer, so the -mb-14 / -mx-* bleed hacks that
                were fighting the page padding are gone. */}
            <DecisionBar>
                <span className="text-[12.5px] font-medium tabular-nums text-muted-foreground">{data.items.length} item siap disubmit</span>
                <div className="flex items-center gap-2.5">
                    <Button type="button" onClick={onSubmitClick} disabled={processing} className="h-9 px-4 text-xs">
                        {processing
                            ? (<><Loader2 className="mr-1 size-4 animate-spin" />Menyimpan...</>)
                            : 'Create Rebate'}
                    </Button>
                    <Button type="button" variant="outline" onClick={() => window.history.back()} className="h-9 px-4 text-xs">
                        Batal
                    </Button>
                </div>
            </DecisionBar>

            <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
                <DialogContent>
                    <DialogHeader>
                        <DialogTitle>Create Rebate?</DialogTitle>
                        <DialogDescription>
                            {data.items.length} item akan diajukan untuk company terpilih.
                        </DialogDescription>
                    </DialogHeader>
                    <DialogFooter>
                        <Button type="button" onClick={submit} disabled={processing}>
                            {processing
                                ? (<><Loader2 className="mr-1 size-4 animate-spin" />Menyimpan...</>)
                                : 'Create Rebate'}
                        </Button>
                        <Button type="button" variant="outline" onClick={() => setConfirmOpen(false)}>Batal</Button>
                    </DialogFooter>
                </DialogContent>
            </Dialog>
        </div>
    );
}
