/* ============================================================
   IMTRACK — AC Log module + New AC / Pre-plan launchers
   ============================================================ */
(function () {
  const E = window.IMTRACK_ENGINE;
  const { useState, useMemo, useEffect } = React;

  function nextAcNumber(store, contractId) {
    const ex = store.acRecords.filter((a) => a.contractId === contractId);
    const mx = Math.max(0, ...ex.map((a) => parseInt(a.acNumber, 10) || 0));
    return String(mx + 1).padStart(3, '0');
  }
  function monthEnd(y, m) { return y + '-' + String(m).padStart(2, '0') + '-' + String(new Date(y, m, 0).getDate()).padStart(2, '0'); }
  function contractYearOptions(contract) {
    if (!contract) return [2025, 2026, 2027];
    const sy = new Date(contract.start + 'T00:00:00').getFullYear();
    const ey = new Date(contract.end + 'T00:00:00').getFullYear();
    const out = [];
    for (let y = sy; y <= ey; y++) out.push(y);
    return out.length ? out : [E.CY];
  }
  function periodBounds(contract, year) {
    if (!contract) return { startMonth: 1, endMonth: 12, startDate: year + '-01-01', endDate: year + '-12-31' };
    const start = new Date(contract.start + 'T00:00:00');
    const end = new Date(contract.end + 'T00:00:00');
    const startMonth = start.getFullYear() === year ? start.getMonth() + 1 : 1;
    const endMonth = end.getFullYear() === year ? end.getMonth() + 1 : 12;
    return {
      startMonth,
      endMonth,
      startDate: year + '-' + String(startMonth).padStart(2, '0') + '-01',
      endDate: monthEnd(year, endMonth),
    };
  }

  function buildAc(store, o) {
    const c = store.contracts.find((x) => x.id === o.contractId);
    const po = o.poLineId ? store.poLines.find((p) => p.id === o.poLineId) : null;
    const af = po ? store.afopLines.find((a) => a.id === po.afopLineId) : store.afopLines.find((a) => a.contractId === o.contractId) || {};
    const finalPo = po || store.poLines.find((p) => p.afopLineId === af.id && p.year === (o.year || E.CY));
    const resolvedAfId = po ? po.afopLineId : af.id;
    return {
      id: 'ac' + Math.floor(Math.random() * 1e9), contractId: o.contractId, contractNo: c.no, vendor: c.vendor,
      afopNo: c.afopNo, afopLineId: resolvedAfId, poLineId: finalPo ? finalPo.id : null, wId: af.wId, ioId: af.ioId,
      pic: o.pic || 'CBCC', scope: c.scope, acNumber: o.acNumber, erpAcNo: c.no + '-O-' + o.acNumber,
      billingType: o.billing || 'reimb', cur: o.cur || c.cur, periodStart: o.periodStart, periodEnd: o.periodEnd,
      cashCallPeriod: o.cp, valueSubmitted: Number(o.sub) || 0, erpAmount: o.status === 's05' ? (Number(o.sub) || 0) : 0,
      status: o.status || 's01', sharedFolder: o.status === 's05', remarks: o.remarks || '',
      notes: '', acStatusHistory: [{ from: null, to: o.status || 's01', changedBy: 'CBCC', changedAt: E.fmtDate(E.TODAY), source: 'manual' }],
      erpCreate: null, erpAchieve: null, erpPost: null, erpClear: null, erpPoNo: finalPo ? finalPo.erpPoNo : '', year: o.year || E.CY, poLinesCovered: finalPo ? [finalPo.id] : [],
      deletedAt: null,
    };
  }

  /* ---------------- Pre-plan ---------------- */
  function PreplanButton({ contractId, small }) {
    const ctx = window.useStore();
    const { store } = ctx;
    const toast = window.useToast();
    const [open, setOpen] = useState(false);
    const [cid, setCid] = useState(contractId || '');
    const [year, setYear] = useState(E.CY);
    const [freq, setFreq] = useState('monthly');
    const [milestones, setMilestones] = useState(4);
    const [f, setF] = useState({ poLineId: null });
    useEffect(() => { if (open) { setCid(contractId || ''); setYear(E.CY); setFreq('monthly'); } }, [open]);

    const selectedContract = store.contracts.find((c) => c.id === cid);
    const years = contractYearOptions(selectedContract);
    const activeYear = years.includes(year) ? year : years[0];
    const previewBounds = periodBounds(selectedContract, activeYear);
    const count = freq === 'monthly'
      ? Math.max(0, previewBounds.endMonth - previewBounds.startMonth + 1)
      : freq === 'annual' ? 1 : milestones;
    const generate = () => {
      if (!cid) { toast('Select a contract', 'warn'); return; }
      const contract = store.contracts.find((x) => x.id === cid);
      const genYear = contractYearOptions(contract).includes(year) ? year : contractYearOptions(contract)[0];
      const bounds = periodBounds(contract, genYear);
      const recs = [];
      let n = parseInt(nextAcNumber(store, cid), 10);
      const make = (cp, ps, pe) => { recs.push(buildAc(store, { contractId: cid, acNumber: String(n).padStart(3, '0'), cp, periodStart: ps, periodEnd: pe, status: 's01', year: genYear, sub: 0, poLineId: f.poLineId })); n++; };
      if (freq === 'monthly') { for (let m = bounds.startMonth; m <= bounds.endMonth; m++) make(genYear + '-' + String(m).padStart(2, '0'), genYear + '-' + String(m).padStart(2, '0') + '-01', monthEnd(genYear, m)); }
      else if (freq === 'annual') make(genYear + '-' + String(bounds.endMonth).padStart(2, '0'), bounds.startDate, bounds.endDate);
      else for (let m = 0; m < milestones; m++) { const mm = Math.min(bounds.endMonth, bounds.startMonth + m * Math.max(1, Math.floor((bounds.endMonth - bounds.startMonth + 1) / milestones))); make(genYear + '-' + String(mm).padStart(2, '0'), genYear + '-' + String(mm).padStart(2, '0') + '-01', monthEnd(genYear, mm)); }
      ctx.actions.addAcRecords(recs);
      toast(recs.length + ' AC placeholder(s) created for ' + (store.contracts.find((x) => x.id === cid) || {}).no, 'good');
      setOpen(false);
    };
    return (<>
      <button className={'btn btn-secondary' + (small ? ' btn-sm' : '')} onClick={() => setOpen(true)}><Icon name="copy" size={small ? 14 : 16} />Pre-plan ACs</button>
      {open && <Modal title="Pre-plan AC placeholders" subtitle="Generate recurring AC records to fill in as they arrive" onClose={() => setOpen(false)}
        footer={<><button className="btn btn-secondary" onClick={() => setOpen(false)}>Cancel</button><button className="btn btn-primary" onClick={generate}>Create {count} placeholder{count > 1 ? 's' : ''}</button></>}>
        <div className="form-grid">
          <Field label="Contract" req span><Select value={cid} onChange={setCid} placeholder="Select contract…" options={store.contracts.map((c) => ({ value: c.id, label: c.no + ' · ' + c.vendor }))} /></Field>
          <Field label="Year" req hint="Limited to the selected contract period"><Select value={String(activeYear)} onChange={(v) => setYear(Number(v))} options={years.map((y) => ({ value: String(y), label: String(y) }))} /></Field>
          <Field label="Billing Frequency" req><Select value={freq} onChange={setFreq} options={[{ value: 'monthly', label: 'Monthly (12 placeholders)' }, { value: 'annual', label: 'Annual (1 placeholder)' }, { value: 'milestone', label: 'Milestone (custom)' }]} /></Field>
          {freq === 'milestone' && <Field label="Number of milestones"><input className="input" type="number" min="1" max="12" value={milestones} onChange={(e) => setMilestones(Number(e.target.value))} /></Field>}
          {cid && <Field label="PO Line" hint="Assign all generated placeholders to this PO line" span>
            {(() => {
              const poOptions = store.poLines.filter((p) => {
                const af = store.afopLines.find((a) => a.id === p.afopLineId);
                return af && af.contractId === cid && !p.deletedAt && p.year === activeYear;
              }).map((p) => {
                const af = store.afopLines.find((a) => a.id === p.afopLineId);
                return { value: p.id, label: 'AFOP #' + (af ? af.no : '?') + ': PO ' + p.erpPoNo + ' · ' + p.desc };
              });
              return <Select value={f.poLineId || ''} onChange={(v) => setF({ ...f, poLineId: v })} placeholder={poOptions.length ? 'Select PO line (auto otherwise)…' : 'Auto — no PO lines for this year'} options={poOptions} />;
            })()}
          </Field>}
        </div>
        <div className="card card-pad mt16" style={{ background: 'var(--color-slate-50)' }}>
          <div className="field-hint">Each placeholder is created at status <strong>01 Follow Up</strong> with a sequential AC Number and pre-filled contract, contractor, AFOP, IO, WP&amp;B line and the PO line you select above. Edit each as the actual AC arrives.</div>
        </div>
      </Modal>}
    </>);
  }

  /* ---------------- New AC ---------------- */
  function NewAcButton({ contractId, small }) {
    const ctx = window.useStore();
    const { store } = ctx;
    const toast = window.useToast();
    const [open, setOpen] = useState(false);
    const blank = () => ({ contractId: contractId || '', acNumber: '', cp: E.CURRENT_MONTH, sub: '', status: 's01', billing: 'reimb', cur: 'USD', poLineId: null });
    const [f, setF] = useState(blank());
    useEffect(() => { if (open) { const b = blank(); if (b.contractId) b.acNumber = nextAcNumber(store, b.contractId); setF(b); } }, [open]);
    const onContract = (id) => setF({ ...f, contractId: id, acNumber: nextAcNumber(store, id), cur: (store.contracts.find((c) => c.id === id) || {}).cur || 'USD' });
    const save = () => {
      if (!f.contractId || !f.acNumber) { toast('Select a contract and AC number', 'warn'); return; }
      const [y, m] = f.cp.split('-');
      const rec = buildAc(store, { ...f, year: Number(y), periodStart: f.cp + '-01', periodEnd: monthEnd(Number(y), Number(m)) });
      ctx.actions.addAcRecords([rec]);
      toast('AC ' + f.acNumber + ' created', 'good');
      setOpen(false);
    };
    const c = store.contracts.find((x) => x.id === f.contractId);
    const cHasAfop = c ? store.afopLines.some((a) => a.contractId === c.id) : true;
    return (<>
      <button className={'btn btn-primary' + (small ? ' btn-sm' : '')} onClick={() => setOpen(true)}><Icon name="plus" size={small ? 14 : 16} />New AC</button>
      {open && <Modal title="New AC Record" onClose={() => setOpen(false)} size="lg"
        footer={<><button className="btn btn-secondary" onClick={() => setOpen(false)}>Cancel</button><button className="btn btn-primary" onClick={save}>Create AC</button></>}>
        {c && !cHasAfop && (
          <div style={{ background: 'var(--color-warning-50)', border: '1px solid var(--color-warning-200)', color: 'var(--color-warning-700)', padding: '8px 12px', borderRadius: 'var(--radius-md)', fontSize: 'var(--text-xs)', display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
            <Icon name="info" size={14} />This contract has no AFOP lines. The AC can still be created but will not appear in budget waterfall views until AFOP lines are linked.
          </div>
        )}
        <div className="form-grid">
          <Field label="Company Contract No" req span><Select value={f.contractId} onChange={onContract} placeholder="Select contract…" options={store.contracts.map((c) => ({ value: c.id, label: c.no + ' · ' + c.vendor }))} /></Field>
          {c && <Field label="Contractor Name"><div className="field-readonly">{c.vendor}</div></Field>}
          {c && <Field label="AFOP / ERP Contract No"><div className="field-readonly mono">{c.afopNo}</div></Field>}
          <Field label="AC Number" req hint="Suggested next number — editable to match ERP format"><input className="input mono" value={f.acNumber} onChange={(e) => setF({ ...f, acNumber: e.target.value })} /></Field>
          <Field label="Cash Call Period" req hint="Independent of all date fields"><input className="input mono" type="month" value={f.cp} onChange={(e) => setF({ ...f, cp: e.target.value })} /></Field>
          <Field label="Billing Type"><Select value={f.billing} onChange={(v) => setF({ ...f, billing: v })} options={store.lookups.billingType.map((s) => ({ value: s.id, label: s.label }))} /></Field>
          <Field label="Currency"><Select value={f.cur} onChange={(v) => setF({ ...f, cur: v })} options={store.lookups.currency.filter((s) => s.active).map((s) => ({ value: s.id, label: s.label }))} /></Field>
          <Field label="AC Value Submitted"><input className="input num" type="number" value={f.sub} onChange={(e) => setF({ ...f, sub: e.target.value })} placeholder="0.00" /></Field>
          <Field label="PO Line" span hint="Links AC to a specific PO line. Auto-derives AFOP, IO & WP&B.">
            {(() => {
              const cpYear = f.cp ? parseInt(f.cp.split('-')[0], 10) : null;
              const poOptions = c ? store.poLines.filter((p) => {
                const af = store.afopLines.find((a) => a.id === p.afopLineId);
                return af && af.contractId === c.id && !p.deletedAt && (!cpYear || p.year === cpYear);
              }).map((p) => {
                const af = store.afopLines.find((a) => a.id === p.afopLineId);
                return { value: p.id, label: 'AFOP #' + (af ? af.no : '?') + ': PO ' + p.erpPoNo + ' · ' + p.desc + ' (' + p.year + ')', afopId: p.afopLineId };
              }) : [];
              return <Select value={f.poLineId || ''} onChange={(v) => {
                const sel = poOptions.find((o) => o.value === v);
                setF({ ...f, poLineId: v, afopLineId: sel ? sel.afopId : null });
              }} placeholder={poOptions.length ? 'Select PO line…' : (c ? 'No PO lines for this contract/year' : 'Select a contract first')} options={poOptions} />;
            })()}
          </Field>
          <Field label="Status"><Select value={f.status} onChange={(v) => setF({ ...f, status: v })} options={store.lookups.acStatus.filter((s) => s.active).map((s) => ({ value: s.id, label: s.label }))} /></Field>
          {c && <Field label="ERP AC Number (auto)" span><div className="field-readonly mono">{c.no}-O-{f.acNumber || '###'}</div></Field>}
        </div>
      </Modal>}
    </>);
  }

  /* ---------------- AC Log list ---------------- */
  function ACLogModule() {
    const ctx = window.useStore();
    const { store, navigate, can, assignedContracts } = ctx;
    const toast = window.useToast();
    const [q, setQ] = useState('');
    const [fStatus, setFStatus] = useState('');
    const [fContract, setFContract] = useState('');
    const [fPoLine, setFPoLine] = useState('');
    const [fCp, setFCp] = useState('');
    const [fHasDocs, setFHasDocs] = useState(false);
    const [grouped, setGrouped] = useState(false);
    const [sort, setSort] = useState({ col: 'cashCallPeriod', dir: 'asc' });
    const [pendingMonth, setPendingMonth] = useState(false);
    const [showDeleted, setShowDeleted] = useState(false);
    const [editAcId, setEditAcId] = useState(null);
    const [deleteConfirm, setDeleteConfirm] = useState(null);
    const [statusHist, setStatusHist] = useState(null);
    const activeEditAc = store.acRecords.find((a) => a.id === editAcId);
    const activeEditContract = activeEditAc ? store.contracts.find((c) => c.id === activeEditAc.contractId) : null;
    const setEditPeriod = (a, cp) => {
      const [yy, mm] = cp.split('-').map(Number);
      if (!yy || !mm) return;
      const lastDay = new Date(yy, mm, 0).getDate();
      ctx.actions.setAcField(a.id, {
        cashCallPeriod: cp, year: yy,
        periodStart: cp + '-01',
        periodEnd: cp + '-' + String(lastDay).padStart(2, '0'),
      });
      toast('Cash Call Period → ' + E.cashLabel(cp) + ' · services period auto-set: ' + cp + '-01 → ' + cp + '-' + lastDay, 'good');
    };

    useEffect(() => {
      const p = JSON.parse(localStorage.getItem('imtrack_nav') || '{}').params || {};
      if (p.contract) setFContract((store.contracts.find((c) => c.no === p.contract) || {}).id || '');
      if (p.pendingMonth) { setPendingMonth(true); setFCp(E.CURRENT_MONTH); }
      if (p.focus) { setTimeout(() => { const el = document.getElementById('acrow-' + p.focus); if (el) { el.style.background = 'var(--color-blue-50)'; el.scrollIntoView && el.scrollIntoView({ block: 'center' }); } }, 200); }
    }, []);

    let rows = store.acRecords;
    if (assignedContracts !== 'all') rows = rows.filter((a) => assignedContracts.includes(a.contractId));
    if (!showDeleted) rows = rows.filter((a) => !a.deletedAt);
    rows = rows.filter((a) => {
      if (q && !(a.vendor.toLowerCase().includes(q.toLowerCase()) || a.contractNo.toLowerCase().includes(q.toLowerCase()) || a.acNumber.includes(q) || a.scope.toLowerCase().includes(q.toLowerCase()))) return false;
      if (fStatus && a.status !== fStatus) return false;
      if (fContract && a.contractId !== fContract) return false;
      if (fPoLine && a.poLineId !== fPoLine) return false;
      if (fCp && a.cashCallPeriod !== fCp) return false;
      const acDocCount = (store.documentReferences || []).filter((r) => r.entityType === 'ac_record' && r.entityId === a.id).length;
      if (fHasDocs && !acDocCount) return false;
      if (pendingMonth && !(a.cashCallPeriod === E.CURRENT_MONTH && a.status !== 's05')) return false;
      return true;
    });
    const acc = { pic: (a) => a.pic, vendor: (a) => a.vendor, contractNo: (a) => a.contractNo, scope: (a) => a.scope, acNumber: (a) => a.acNumber, valueSubmitted: (a) => a.valueSubmitted, status: (a) => a.status, cashCallPeriod: (a) => a.cashCallPeriod, erpAmount: (a) => a.erpAmount, poLineId: (a) => { const po = store.poLines.find((p) => p.id === a.poLineId); return po ? po.erpPoNo : ''; } };
    rows = window.sortRows(rows, sort, acc);

    const cpOptions = [...new Set(store.acRecords.map((a) => a.cashCallPeriod))].sort();
    // Detect duplicate month for contract
    const dupes = {};
    rows.forEach((a) => {
      const key = a.contractId + '|' + a.cashCallPeriod;
      dupes[key] = (dupes[key] || 0) + 1;
    });
    const doExport = () => {
      window.exportCsv('imtrack_ac_log.csv',
        ['PIC', 'Contractor', 'Company Contract No', 'Scope', 'Service Description', 'Services Period Start', 'Services Period End', 'AC Number', 'Currency', 'AC Value Submitted', 'Status', 'Status Changes', 'Cash Call Period', 'ERP AC Amount', 'Gap', 'Notes', 'Document Links'],
        rows.map((a) => { const gap = (a.erpAmount || 0) - (a.valueSubmitted || 0); const docCount = (store.documentReferences || []).filter((r) => r.entityType === 'ac_record' && r.entityId === a.id).length; return [a.pic, a.vendor, a.contractNo, a.scope, a.serviceDescription || '', a.periodStart, a.periodEnd, a.acNumber, a.cur, a.valueSubmitted, E.lkLabel(store, 'acStatus', a.status), (a.acStatusHistory || []).length, a.cashCallPeriod, a.erpAmount || '', a.erpAmount ? gap : '', a.notes || '', docCount || '']; }));
      toast('Exported ' + rows.length + ' AC records', 'good');
    };

    const groups = useMemo(() => {
      if (!grouped) return null;
      const g = {};
      rows.forEach((a) => { (g[a.cashCallPeriod] = g[a.cashCallPeriod] || []).push(a); });
      return Object.keys(g).sort().map((k) => ({ cp: k, rows: g[k], sub: g[k].reduce((s, a) => s + a.valueSubmitted, 0), erp: g[k].reduce((s, a) => s + (a.erpAmount || 0), 0) }));
    }, [grouped, rows]);

    const AcRow = ({ a }) => {
      const gap = (a.erpAmount || 0) - (a.valueSubmitted || 0);
      const hasErp = !!a.erpAmount;
      const acDocs = (store.documentReferences || []).filter((r) => r.entityType === 'ac_record' && r.entityId === a.id);
      const isDuped = dupes[(a.contractId + '|' + a.cashCallPeriod)] > 1;
      const historyCount = (a.acStatusHistory || []).length;
      const latestHistory = (a.acStatusHistory || [])[historyCount - 1];
      return (
        <tr id={'acrow-' + a.id} className={'clickable' + (a.deletedAt ? ' deleted-row' : '') + (isDuped ? ' dupe-row' : '')} onClick={() => navigate('contract', { id: a.contractId, tab: 'aclog' })}>
          <td>
            <div className="row" style={{ gap: 3 }}>
              {a.pic}
              {isDuped && <span className="badge badge-warning" style={{ fontSize: 9, padding: '0 4px', lineHeight: '16px' }} title="Duplicate: same contract and cash call period">dup</span>}
            </div>
          </td>
          <td>{a.vendor}</td>
          <td className="mono">{a.contractNo}</td>
          <td>
            <div>{a.scope}</div>
            {a.serviceDescription && <div className="muted" style={{ fontSize: 11 }}>{a.serviceDescription}</div>}
          </td>
          <td className="num" style={{ fontSize: 12 }}>{a.periodStart} → {a.periodEnd}</td>
          <td className="mono">{a.acNumber}</td>
          <td>{a.cur}</td>
          <td className="num-col"><Money v={a.valueSubmitted} dec={2} /></td>
          <td onClick={(e) => e.stopPropagation()}>
            {(() => {
              const sDef = store.lookups.acStatus.find(s => s.id === a.status);
              const isTerm = sDef?.is_terminal;
              if (isTerm || !can('aclog', 'edit') || a.deletedAt)
                return <StatusBadge store={store} list="acStatus" id={a.status} />;
              return <window.AcStatusSelect a={a} ctx={ctx} />;
            })()}
          </td>
          <td style={{ fontSize: 11 }}>
            {historyCount > 0
              ? <span className="link" onClick={(e) => { e.stopPropagation(); setStatusHist(a); }}>{historyCount} change{historyCount > 1 ? 's' : ''}</span>
              : <span className="dash">—</span>}
          </td>
          <td>{E.cashLabel(a.cashCallPeriod)}</td>
          <td style={{ fontSize: 11 }} onClick={(e) => { e.stopPropagation(); navigate('contract', { id: a.contractId, tab: 'afop' }); }}>
            {(() => {
              const po = store.poLines.find((p) => p.id === a.poLineId);
              if (!po) return <span className="muted">—</span>;
              const af = store.afopLines.find((x) => x.id === po.afopLineId);
              return <span className="link">AFOP #{af ? af.no : '?'} · {po.erpPoNo || 'PO ' + po.id.slice(0,6)}</span>;
            })()}
          </td>
          <td className="num-col"><Money v={a.erpAmount} dec={2} dash /></td>
          <td className="num-col">
            {hasErp
              ? <span className={gap >= 0 ? 'gap-positive' : 'gap-negative'}>{gap >= 0 ? '+' : ''}{E.fmtMoney(gap, a.cur, 2)}</span>
              : <span className="dash">—</span>}
          </td>
          <td style={{ fontSize: 11, maxWidth: 100 }}>
            {a.notes ? <span className="muted" title={a.notes}>{a.notes.length > 20 ? a.notes.slice(0, 20) + '…' : a.notes}</span> : <span className="dash">—</span>}
          </td>
          <td style={{ fontSize: 11 }}>
            {acDocs.length > 0
              ? <span style={{ color: 'var(--accent)', fontWeight: 500, cursor: 'pointer' }}
                  onClick={(e) => { e.stopPropagation(); toast(acDocs.map((d) => d.description + (d.url ? ': ' + d.url : '')).join(', '), 'info'); }}>
                  {acDocs.length} link{acDocs.length > 1 ? 's' : ''}</span>
              : <span className="dash">—</span>}
          </td>
          {can('aclog', 'edit') && (
            <td className="col-actions" onClick={(e) => e.stopPropagation()}>
              {!a.deletedAt ? (
                <div className="row" style={{ gap: 2 }}>
                  {(() => { const sDef = store.lookups.acStatus.find(s => s.id === a.status); const isTerm = sDef?.is_terminal; return isTerm ? (
                    <button className="icon-btn" title="View (read-only)" onClick={() => setEditAcId(a.id)}><Icon name="eye" size={13} /></button>
                  ) : (<>
                    <button className="icon-btn" title="Edit" onClick={() => setEditAcId(editAcId === a.id ? null : a.id)}><Icon name="pencil" size={13} /></button>
                    <button className="icon-btn danger" title="Soft delete" onClick={() => setDeleteConfirm(a)}><Icon name="trash-2" size={13} /></button>
                  </>); })()}
                  <button className="icon-btn" title="Copy as placeholder" onClick={() => { ctx.actions.duplicateAc(a.id); toast('AC copied as new placeholder', 'good'); }}><Icon name="copy" size={13} /></button>
                </div>
              ) : (
                <button className="icon-btn" title="Restore" onClick={() => { ctx.actions.restoreAcRecord(a.id); toast('AC ' + a.acNumber + ' restored', 'good'); }}><Icon name="rotate-ccw" size={13} /></button>
              )}
            </td>
          )}
        </tr>
      );
    };
    const headCols = (
      <tr>
        <SortHead label="PIC" col="pic" sort={sort} setSort={setSort} />
        <SortHead label="Contractor" col="vendor" sort={sort} setSort={setSort} />
        <SortHead label="Contract No" col="contractNo" sort={sort} setSort={setSort} />
        <SortHead label="Scope" col="scope" sort={sort} setSort={setSort} />
        <th>Services Period</th>
        <SortHead label="AC No" col="acNumber" sort={sort} setSort={setSort} />
        <th>Cur</th>
        <SortHead label="AC Submitted" col="valueSubmitted" sort={sort} setSort={setSort} numeric />
        <SortHead label="Status" col="status" sort={sort} setSort={setSort} />
        <th>Status History</th>
        <SortHead label="Cash Call" col="cashCallPeriod" sort={sort} setSort={setSort} />
        <SortHead label="PO Line" col="poLineId" sort={sort} setSort={setSort} />
        <SortHead label="ERP Amount" col="erpAmount" sort={sort} setSort={setSort} numeric />
        <th className="num-col">Gap</th>
        <th>Notes</th>
        <th>Document Links</th>{can('aclog', 'edit') && <th></th>}
      </tr>
    );

    return (
      <div className="page page-wide">
        <PageHeader title="AC Log" subtitle={rows.length + ' acceptance certificate records' + (assignedContracts !== 'all' ? ' (assigned contracts)' : '')}>
          <button className="btn btn-secondary" onClick={doExport}><Icon name="download" size={16} />Export CSV</button>
          {can('aclog', 'import') && <button className="btn btn-secondary" onClick={() => navigate('import', { back: 'aclog', type: 'ac' })}><Icon name="upload" size={16} />Import</button>}
          {can('aclog', 'create') && <PreplanButton />}
          {can('aclog', 'create') && <NewAcButton />}
        </PageHeader>

        <div className="filterbar">
          <div className="search-box"><Icon name="search" size={15} /><input className="input" placeholder="Search contractor, contract, AC no…" value={q} onChange={(e) => setQ(e.target.value)} style={{ width: 260 }} /></div>
          <select className="select" value={fStatus} onChange={(e) => setFStatus(e.target.value)}><option value="">All statuses</option>{store.lookups.acStatus.filter((s) => s.active).map((s) => <option key={s.id} value={s.id}>{s.label}</option>)}</select>
          <select className="select" value={fContract} onChange={(e) => setFContract(e.target.value)}><option value="">All contracts</option>{store.contracts.map((c) => <option key={c.id} value={c.id}>{c.no}</option>)}</select>
          <select className="select" value={fPoLine} onChange={(e) => setFPoLine(e.target.value)} style={{ maxWidth: 200 }}><option value="">All PO lines</option>{store.poLines.filter((p) => !p.deletedAt).map((p) => { const af = store.afopLines.find((a) => a.id === p.afopLineId); return <option key={p.id} value={p.id}>AFOP #{(af||{}).no}: {p.erpPoNo} ({p.year})</option>; })}</select>
          <select className="select" value={fCp} onChange={(e) => setFCp(e.target.value)}><option value="">All periods</option>{cpOptions.map((cp) => <option key={cp} value={cp}>{E.cashLabel(cp)}</option>)}</select>
          <label className="row" style={{ gap: 6, fontSize: 11, whiteSpace: 'nowrap', alignItems: 'center', cursor: 'pointer' }}>
            <input type="checkbox" checked={fHasDocs} onChange={(e) => setFHasDocs(e.target.checked)} />
            Has docs
          </label>
          <div className="spacer" />
          <label className="row muted" style={{ gap: 4, fontSize: 11, whiteSpace: 'nowrap' }}>
            <input type="checkbox" checked={showDeleted} onChange={(e) => setShowDeleted(e.target.checked)} />
            Show deleted
          </label>
          <button className={'btn btn-sm ' + (grouped ? 'btn-primary' : 'btn-secondary')} onClick={() => setGrouped((g) => !g)}><Icon name="bar-chart" size={14} />Monthly grouping</button>
        </div>
        {pendingMonth && <div className="filterbar"><span className="chip">Pending for {E.cashLabel(E.CURRENT_MONTH)}<button onClick={() => { setPendingMonth(false); setFCp(''); }}><Icon name="x" size={12} /></button></span></div>}
        <div className="card" style={{ overflow: 'hidden' }}>
          <div className="tbl-wrap">
            <table className="tbl tbl-compact">
              <thead style="position: sticky; top: 0; z-index: 1; background: var(--bg-default)">{headCols}</thead>
              <tbody>
                {!grouped && rows.map((a) => <AcRow key={a.id} a={a} />)}
                {grouped && groups.map((g) => (
                  <React.Fragment key={g.cp}>
                    <tr style={{ background: 'var(--color-navy-800)' }}>
                      <td colSpan={7} style={{ color: '#fff', fontWeight: 600, fontSize: 12, textTransform: 'uppercase', letterSpacing: '.03em' }}>{E.cashLabel(g.cp)} · {g.rows.length} AC</td>
                      <td className="num-col" style={{ color: '#fff' }}><span className="num">{E.fmtUSD2(g.sub)}</span></td>
                      <td colSpan={3}></td>
                      <td></td>
                      <td className="num-col" style={{ color: '#fff' }}><span className="num">{E.fmtUSD2(g.erp)}</span></td>
                      <td></td>
                      <td colSpan={can('aclog', 'edit') ? 4 : 3}></td>
                    </tr>
                    {g.rows.map((a) => <AcRow key={a.id} a={a} />)}
                  </React.Fragment>
                ))}
              </tbody>
            </table>
            {rows.length === 0 && <EmptyState icon="receipt" title="No AC records match your filters" sub="Adjust filters, or pre-plan placeholders for a contract." />}
          </div>
          <div className="table-meta"><span>{rows.length} rows</span><span>Only <strong>05 Done</strong> consumes budget; reversing status preserves ERP fields but excludes it from consumption.</span></div>
        </div>
        {activeEditAc && (() => {
            const statusDef = store.lookups.acStatus.find(s => s.id === activeEditAc.status);
            const isTerminal = statusDef?.is_terminal;
            const hasErpData = !!(activeEditAc.erpAmount);
            const isFullyLocked = isTerminal;
            return (
          <Modal title="Edit AC Record" subtitle={activeEditAc.contractNo + ' / AC ' + activeEditAc.acNumber} size="lg" onClose={() => setEditAcId(null)}
            footer={<>
              <button className="btn btn-secondary" onClick={() => setEditAcId(null)}>Cancel</button>
              <button className="btn btn-primary" onClick={() => { toast('Saved', 'good'); setEditAcId(null); }}><Icon name="save" size={14} />Save & Close</button>
            </>}>
            {hasErpData && (
              <div style={{ background: 'var(--color-blue-50)', border: '1px solid var(--color-blue-100)', borderRadius: 6, padding: '8px 12px', marginBottom: 12, fontSize: 'var(--text-xs)', color: 'var(--color-blue-700)', display: 'flex', alignItems: 'center', gap: 6 }}>
                <Icon name="lock" size={13} /> {isTerminal ? "🔒 05 Done — budget consumed. All fields locked. Notes remain editable." : "🔒 Submitted to ERP — ERP AC Amount and ERP AC Number are locked. Other fields remain editable until finalized."}
              </div>
            )}
            <div className="form-grid">
              <Field label="Company Contract No"><div className="field-readonly mono">{activeEditAc.contractNo}</div></Field>
              <Field label="Contractor"><div className="field-readonly">{activeEditAc.vendor}</div></Field>
              <Field label="AC Number">
                {isFullyLocked ? <div className="field-readonly mono">{activeEditAc.acNumber || '—'}</div> : <input className="input mono" value={activeEditAc.acNumber || ''} onChange={(e) => ctx.actions.setAcField(activeEditAc.id, { acNumber: e.target.value, erpAcNo: activeEditAc.contractNo + '-O-' + e.target.value })} />}
              </Field>
              <Field label="Cash Call Period">
                {isFullyLocked ? <div className="field-readonly mono">{E.cashLabel(activeEditAc.cashCallPeriod) || '—'}</div> : (() => {
                  const ccpOptions = [];
                  if (activeEditContract) {
                    const s = new Date(activeEditContract.start);
                    const e = new Date(activeEditContract.end);
                    const d = new Date(s.getFullYear(), s.getMonth(), 1);
                    while (d <= new Date(e.getFullYear() + 3, e.getMonth(), 1)) {
                      const val = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0');
                      ccpOptions.push({ value: val, label: window.IMTRACK_ENGINE.cashLabel(val) });
                      d.setMonth(d.getMonth() + 1);
                    }
                  }
                  return (
                    <select className="select" style={{ height: 30, fontSize: 12, width: 170 }} value={activeEditAc.cashCallPeriod || ''} onChange={(e) => setEditPeriod(activeEditAc, e.target.value)}>
                      <option value="">Select month…</option>
                      {ccpOptions.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
                    </select>
                  );
                })()}
              </Field>
              <Field label="Currency" hint={activeEditContract ? 'Matches contract currency' : ''}>
                <div className="field-readonly">{activeEditAc.cur || (activeEditContract ? activeEditContract.cur : 'USD')}</div>
              </Field>
              <Field label="Services Period">
                {isFullyLocked ? <div className="field-readonly">{activeEditAc.periodStart || '—'} → {activeEditAc.periodEnd || '—'}</div> : <div className="row" style={{ gap: 8, alignItems: 'center' }}><input className="input mono" type="date" value={activeEditAc.periodStart || ''} onChange={(e) => ctx.actions.setAcField(activeEditAc.id, { periodStart: e.target.value })} style={{ width: 160 }} /><span className="muted">→</span><input className="input mono" type="date" value={activeEditAc.periodEnd || ''} onChange={(e) => ctx.actions.setAcField(activeEditAc.id, { periodEnd: e.target.value })} style={{ width: 160 }} /></div>}
              </Field>
              <Field label="PO Line" span>
                {isFullyLocked ? (() => {
                  const curPo = store.poLines.find((p) => p.id === activeEditAc.poLineId);
                  const curAf = curPo ? store.afopLines.find((a) => a.id === curPo.afopLineId) : null;
                  return <div className="field-readonly">{curPo ? 'AFOP #' + (curAf ? curAf.no : '?') + ': PO ' + curPo.erpPoNo + ' · ' + curPo.desc : '—'}</div>;
                })() : (() => {
                  const poOptions = activeEditContract ? store.poLines.filter((p) => {
                    const af = store.afopLines.find((a) => a.id === p.afopLineId);
                    return af && af.contractId === activeEditContract.id && !p.deletedAt;
                  }).map((p) => {
                    const af = store.afopLines.find((a) => a.id === p.afopLineId);
                    return { value: p.id, label: 'AFOP #' + (af ? af.no : '?') + ': PO ' + p.erpPoNo + ' · ' + p.desc + ' (' + p.year + ')', afopId: p.afopLineId, ioId: af ? af.ioId : null, wId: af ? af.wId : null };
                  }) : [];
                  const selPo = poOptions.find((o) => o.value === activeEditAc.poLineId);
                  return (<>
                    <Select value={activeEditAc.poLineId || ''} onChange={(v) => {
                      const sel = poOptions.find((o) => o.value === v);
                      ctx.actions.setAcField(activeEditAc.id, {
                        poLineId: v,
                        afopLineId: sel ? sel.afopId : activeEditAc.afopLineId,
                        ioId: sel ? sel.ioId : activeEditAc.ioId,
                        wId: sel ? sel.wId : activeEditAc.wId,
                      });
                      toast('PO line updated — AFOP, IO & WP&B auto-derived', 'good');
                    }} placeholder={poOptions.length ? 'Select PO line…' : 'No PO lines for this contract'} options={poOptions} />
                    {activeEditAc.poLineId && (() => {
                      const p = store.poLines.find((x) => x.id === activeEditAc.poLineId);
                      const afLine = p ? store.afopLines.find((x) => x.id === p.afopLineId) : null;
                      return (
                        <div className="field-hint" style={{ marginTop: 4 }}>
                          → AFOP #{afLine ? afLine.no : '?'} → IO {activeEditAc.ioId ? (store.ios.find((x) => x.id === activeEditAc.ioId) || {}).io || activeEditAc.ioId : '—'} → WP&B {activeEditAc.wId ? (store.wpbItems.find((x) => x.id === activeEditAc.wId) || {}).name || activeEditAc.wId : '—'}
                        </div>
                      );
                    })()}
                  </>);
                })()}
              </Field>
              <Field label="AC Value Submitted">
                {isFullyLocked ? <div className="field-readonly num"><Money v={activeEditAc.valueSubmitted} cur={activeEditAc.cur} dec={2} /></div> : <input className="input num" type="number" value={activeEditAc.valueSubmitted || ''} onChange={(e) => ctx.actions.setAcField(activeEditAc.id, { valueSubmitted: Number(e.target.value) || 0 })} />}
              </Field>
              <Field label="ERP AC Amount" hint={hasErpData ? 'SAP-sourced — read-only' : 'Editable until submitted to ERP'}>
                <div className="field-readonly num"><Money v={activeEditAc.erpAmount} cur={activeEditAc.cur} dec={2} dash /></div>
              </Field>
              <Field label="Status">
                {isFullyLocked ? <StatusBadge store={store} list="acStatus" id={activeEditAc.status} /> : <Select value={activeEditAc.status} onChange={(v) => { ctx.actions.setAcStatus(activeEditAc.id, v); if (v === 's05') toast('AC ' + activeEditAc.acNumber + ' set to 05 Done — budget recalculated', 'good'); }} options={store.lookups.acStatus.filter((s) => s.active).map((s) => ({ value: s.id, label: s.label }))} />}
              </Field>
              <Field label="ERP AC Number"><div className="field-readonly mono">{activeEditAc.erpAcNo || '—'}</div></Field>
              <Field label="Notes" span>
                <textarea className="input" rows={3} value={activeEditAc.notes || ''} onChange={(e) => ctx.actions.setAcField(activeEditAc.id, { notes: e.target.value })} />
              </Field>
            </div>
          </Modal>
        ); })()}
        {statusHist && (
          <Modal title="Status History" subtitle={'AC ' + statusHist.acNumber + ' · ' + statusHist.contractNo} onClose={() => setStatusHist(null)}
            footer={<button className="btn btn-secondary" onClick={() => setStatusHist(null)}>Close</button>}>
            {(statusHist.acStatusHistory || []).length === 0 ? (
              <p className="muted">No status changes recorded.</p>
            ) : (
              <table className="tbl tbl-compact">
                <thead style="position: sticky; top: 0; z-index: 1; background: var(--bg-default)"><tr><th>From</th><th>To</th><th>Date</th></tr></thead>
                <tbody>
                  {(statusHist.acStatusHistory || []).map((h, i) => (
                    <tr key={i}>
                      <td>{h.from ? E.lkLabel(store, 'acStatus', h.from) : 'Created'}</td>
                      <td>{E.lkLabel(store, 'acStatus', h.to)}</td>
                      <td className="mono" style={{ fontSize: 12 }}>{h.changedAt || '—'}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </Modal>
        )}
        {deleteConfirm && (
          <Modal title="Soft Delete AC Record" subtitle={deleteConfirm.contractNo + ' / AC ' + deleteConfirm.acNumber} onClose={() => setDeleteConfirm(null)}
            footer={<><button className="btn btn-secondary" onClick={() => setDeleteConfirm(null)}>Cancel</button><button className="btn btn-destructive" onClick={() => { ctx.actions.deleteAcRecord(deleteConfirm.id); toast('AC ' + deleteConfirm.acNumber + ' soft-deleted. Admin can recover it from Settings.', 'warn'); setDeleteConfirm(null); }}>Soft delete</button></>}>
            <p className="muted" style={{ margin: 0 }}>This AC record will be hidden from the active list. Status history and SharePoint document references remain retained.</p>
          </Modal>
        )}
      </div>
    );
  }

  Object.assign(window, { ACLogModule, PreplanButton, NewAcButton });
})();
