/* ============================================================
   IMTRACK — Settings + Users & Roles (Admin)
   ============================================================ */
(function () {
  const E = window.IMTRACK_ENGINE;
  const { useState, useMemo } = React;

  function ResetButton({ ctx, section, label }) {
    const toast = window.useToast();
    const [confirming, setConfirming] = useState(false);
    if (confirming) return (
      <span style={{ display: 'inline-flex', gap: 4, alignItems: 'center' }}>
        <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>Reset {label}?</span>
        <button className="btn btn-destructive btn-sm" onClick={() => { ctx.actions.resetSettingsSection(section); toast(label + ' reset to defaults', 'warn'); setConfirming(false); }}>Yes</button>
        <button className="btn btn-secondary btn-sm" onClick={() => setConfirming(false)}>No</button>
      </span>
    );
    return <button className="btn btn-secondary btn-sm" onClick={() => setConfirming(true)} style={{ marginLeft: 'auto' }}>Reset defaults</button>;
  }

  const LOOKUP_LISTS = [
    ['currency', 'Currency'], ['contractStatus', 'Contract Status'], ['acStatus', 'AC Status'], ['poStatus', 'PO Status'],
    ['operationalFlags', 'Operational Flags'], ['procMethod', 'Procurement Method'], ['amendmentType', 'Amendment Type'], ['wpInitiation', 'WP Initiation'],
    ['wpCategory', 'WP Category'], ['wpGroup', 'WP Group'], ['prStatus', 'PR Status'], ['procPlanCode', 'Procurement Plan Code'],
    ['billingType', 'Billing Type'], ['documentCategory', 'Document Category'],
    ['entity', 'Entity'], ['department', 'Department'],
  ];

  /* ---------------- Settings ---------------- */
  function SettingsModule() {
    const ctx = window.useStore();
    const params = (JSON.parse(localStorage.getItem('imtrack_nav') || '{}').params) || {};
    const [section, setSection] = useState(params.section || 'lookups');
    const SECTIONS = [
      ['lookups',          'Lookup Lists'],
      ['fx',               'FX Rates'],
      ['alerts',           'Alert Configuration'],
      ['cockpit_actions',  'Action Cockpit'],
      ['amendment_steps',  'Amendment Next Steps'],
      ['wpb',              'WP&B Master Data'],
      ['io',               'IO Master Data'],
      ['import_config',    'Import Configuration'],
      ['recently_deleted', 'Recently Deleted'],
    ];
    return (
      <div className="page page-wide">
        <PageHeader title="Settings" subtitle="Configurable foundation — every module reads from here" />
        <div className="settings-layout">
          <div className="settings-nav">
            {SECTIONS.map(([k, l]) => <button key={k} className={section === k ? 'active' : ''} onClick={() => setSection(k)}>{l}</button>)}
          </div>
          <div>
            {section === 'lookups'          && <LookupSection ctx={ctx} />}
            {section === 'fx'               && <FxSection ctx={ctx} />}
            {section === 'alerts'           && <AlertSection ctx={ctx} />}
            {section === 'cockpit_actions'  && <CockpitActionsSection ctx={ctx} />}
            {section === 'amendment_steps'  && <AmendmentNextStepsSection ctx={ctx} />}
            {section === 'wpb'              && <WpbSection ctx={ctx} />}
            {section === 'io'               && <IoSection ctx={ctx} />}
            {section === 'import_config'    && <ImportConfigSection ctx={ctx} />}
            {section === 'recently_deleted' && <RecentlyDeletedSection ctx={ctx} />}
          </div>
        </div>
      </div>
    );
  }

  function LookupSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const [list, setList] = useState('contractStatus');
    const [newVal, setNewVal] = useState('');
    const [semanticWarn, setSemanticWarn] = useState(null); // { value, action }
    const values = store.lookups[list] || [];
    const SEMANTIC_LISTS = ['contractStatus', 'acStatus', 'operationalFlags', 'entity', 'department'];
    const isSemantic = SEMANTIC_LISTS.includes(list);
    const [editingId, setEditingId] = useState(null);
    const [editingLabel, setEditingLabel] = useState('');
    const add = () => { if (!newVal.trim()) { toast('Enter a value before adding', 'warn'); return; } ctx.actions.addLookup(list, { id: list + '_' + Date.now(), label: newVal.trim(), color: 'neutral', active: true }); setNewVal(''); toast('Value added', 'good'); };
    const startRename = (v) => { setEditingId(v.id); setEditingLabel(v.label); };
    const saveRename = () => { if (editingLabel.trim()) { ctx.actions.updateLookup(list, editingId, { label: editingLabel.trim() }); toast('Renamed — updates everywhere it is referenced', 'good'); } setEditingId(null); };
    const cancelRename = () => { setEditingId(null); };
    const activeCount = values.filter((v) => v.active).length;
    const toggleActive = (v) => {
      if (v.active && isSemantic) {
        setSemanticWarn({ value: v, action: 'deactivate' });
        return;
      }
      ctx.actions.updateLookup(list, v.id, { active: !v.active });
    };
    const COLORS = ['neutral', 'green', 'warning', 'critical', 'blue', 'flag', 'navy'];
    const isLastActive = (v) => v.active && isSemantic && activeCount <= 1;
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        {isSemantic && (
          <div style={{ background: 'var(--color-flag-50)', borderBottom: '1px solid var(--color-flag-100)', padding: '8px 14px', fontSize: 'var(--text-xs)', color: 'var(--color-flag-700)', display: 'flex', alignItems: 'center', gap: 6 }}>
            <Icon name="info" size={14} />Semantic list — values are referenced by system logic. Deactivation requires confirmation.
          </div>
        )}
        <div className="card-head">
          <select className="select" value={list} onChange={(e) => setList(e.target.value)} style={{ width: 260 }}>{LOOKUP_LISTS.map(([k, l]) => <option key={k} value={k}>{l}</option>)}</select>
          <ResetButton ctx={ctx} section="lookups" label="Lookup Lists" />
          <div className="row" style={{ gap: 8 }}>
            <input className="input" placeholder="New value…" value={newVal} onChange={(e) => setNewVal(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && add()} style={{ width: 180 }} />
            <button className="btn btn-primary btn-sm" onClick={add}><Icon name="plus" size={14} />Add</button>
          </div>
        </div>
        <table className="tbl tbl-compact">
          <thead><tr><th>Label</th><th>Preview</th><th>Color</th><th>State</th><th></th></tr></thead>
          <tbody>
            {values.map((v) => (
              <tr key={v.id} style={{ opacity: v.active ? 1 : 0.6 }}>
                <td className={v.active ? '' : 'muted'} style={{ textDecoration: v.active ? 'none' : 'line-through' }} onClick={(e) => e.stopPropagation()}>
                  {editingId === v.id ? (
                    <div className="row" style={{ gap: 4 }}>
                      <input className="input" style={{ height: 28, fontSize: 12, width: 180 }} value={editingLabel} onChange={(e) => setEditingLabel(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') saveRename(); if (e.key === 'Escape') cancelRename(); }} autoFocus />
                      <button className="icon-btn" title="Save" onClick={saveRename}><Icon name="check" size={14} /></button>
                      <button className="icon-btn" title="Cancel" onClick={cancelRename}><Icon name="x" size={14} /></button>
                    </div>
                  ) : <>{v.code ? <span className="mono">{v.code} </span> : ''}{v.label}</>}
                </td>
                <td><StatusBadge store={store} list={list} id={v.id} dot /></td>
                <td><select className="select" style={{ height: 28, fontSize: 12, width: 110 }} value={v.color} onChange={(e) => ctx.actions.updateLookup(list, v.id, { color: e.target.value })}>{COLORS.map((c) => <option key={c} value={c}>{c}</option>)}</select></td>
                <td>{v.active ? <span className="badge badge-green">Active</span> : <span className="badge badge-neutral">Deactivated</span>}</td>
                <td className="col-actions"><div className="cell-actions">
                  <button className="icon-btn" title="Rename" onClick={() => startRename(v)}><Icon name="pencil" size={14} /></button>
                  <button
                    className="icon-btn"
                    disabled={isLastActive(v)}
                    title={isLastActive(v) ? 'Cannot deactivate — at least one active value must remain. Add a replacement first.' : (v.active ? 'Deactivate' : 'Reactivate')}
                    onClick={() => toggleActive(v)}
                    style={isLastActive(v) ? { opacity: 0.35, cursor: 'not-allowed' } : undefined}
                  >
                    <Icon name={v.active ? 'x' : 'check'} size={14} />
                  </button>
                </div></td>
              </tr>
            ))}
          </tbody>
        </table>
        <div className="table-meta"><span>{values.filter((v) => v.active).length} active · {values.filter((v) => !v.active).length} deactivated</span><span>Values are never hard-deleted — deactivated values stay visible on historical records</span></div>
        {semanticWarn && (
          <ConfirmDialog
            title="Deactivate Semantic Value"
            message={`"${semanticWarn.value.label}" is a semantic value in the "${LOOKUP_LISTS.find(([k]) => k === list)?.[1] || list}" list. System logic (Action Cockpit rules, status workflows, operational flags) may reference it. Deactivating it may affect cockpit behaviour and status transitions.\n\nThis value will remain visible on historical records but will not be selectable for new records.`}
            confirmLabel="Deactivate"
            destructive
            onConfirm={() => {
              ctx.actions.updateLookup(list, semanticWarn.value.id, { active: false });
              toast(`"${semanticWarn.value.label}" deactivated`, 'info');
            }}
            onClose={() => setSemanticWarn(null)}
          />
        )}
      </div>
    );
  }

  function FxSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const [year, setYear] = useState('');
    let rows = store.fxRates;
    if (year) rows = rows.filter((r) => String(r.year) === year);
    const [addOpen, setAddOpen] = useState(false);
    const [newFx, setNewFx] = useState({ year: '' + E.CY, pair: 'USD/IDR', type: 'Regulatory Authority', rate: '' });
    const addFx = () => { if (!newFx.rate) { toast('Enter a rate', 'warn'); return; } const dup = store.fxRates.find((r) => String(r.year) === String(newFx.year) && r.pair === newFx.pair && r.type === newFx.type); if (dup) { toast('Duplicate — a rate for (' + newFx.year + ', ' + newFx.pair + ', ' + newFx.type + ') already exists', 'warn'); return; } ctx.actions.addFxRate({ year: Number(newFx.year), pair: newFx.pair, type: newFx.type, rate: Number(newFx.rate) }); toast('FX rate added', 'good'); setAddOpen(false); setNewFx({ year: '' + E.CY, pair: 'USD/IDR', type: 'Regulatory Authority', rate: '' }); };
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head"><span className="card-title">FX Rates</span>
          <ResetButton ctx={ctx} section="fx" label="FX Rates" />
          <button className="btn btn-primary btn-sm" onClick={() => setAddOpen(!addOpen)}><Icon name="plus" size={14} />Add</button>
          <select className="select" value={year} onChange={(e) => setYear(e.target.value)} style={{ width: 130 }}>
            <option value="">All years</option>
            {[...new Set(store.fxRates.map((r) => r.year))].sort().map((y) => <option key={y} value={y}>FY{y}</option>)}
          </select>
        </div>
        <table className="tbl tbl-compact">
          <thead><tr><th>Year</th><th>Currency Pair</th><th>Rate Type</th><th className="num-col">Rate</th></tr></thead>
          <tbody>
            {addOpen && (<tr style={{ background: 'var(--color-blue-50)' }}><td><input className="input num" style={{ width: 70, height: 30 }} type="number" value={newFx.year} onChange={(e) => setNewFx({ ...newFx, year: e.target.value })} /></td><td><select className="select" style={{ height: 30, fontSize: 12 }} value={newFx.pair} onChange={(e) => setNewFx({ ...newFx, pair: e.target.value })}>{[...new Set([...store.fxRates.map((r) => r.pair), 'USD/IDR', 'USD/JPY', 'IDR/USD', 'JPY/USD'])].sort().map((p) => <option key={p} value={p}>{p}</option>)}</select></td><td><select className="select" style={{ height: 30, fontSize: 12 }} value={newFx.type} onChange={(e) => setNewFx({ ...newFx, type: e.target.value })}><option>Regulatory Authority</option><option>Internal Planning</option></select></td><td className="num-col"><div className="row" style={{ gap: 4, justifyContent: 'flex-end' }}><input className="input mono" style={{ width: 110, height: 30, textAlign: 'right' }} type="number" step="any" value={newFx.rate} onChange={(e) => setNewFx({ ...newFx, rate: e.target.value })} onKeyDown={(e) => e.key === 'Enter' && addFx()} /><button className="btn btn-primary btn-sm" onClick={addFx}><Icon name="check" size={14} /></button></div></td></tr>)}
            {rows.map((r) => (
              <tr key={r.id}>
                <td className="mono">{r.year}</td>
                <td className="mono">{r.pair}</td>
                <td>{r.type}</td>
                <td className="num-col">
                  <input
                    className="input mono"
                    style={{ width: 120, height: 30, textAlign: 'right' }}
                    type="number"
                    value={r.rate}
                    onChange={(e) => { ctx.actions.setFxRate(r.id, { rate: Number(e.target.value) }); toast('FX rate updated — IDR/JPY calculations recalculate instantly', 'good'); }}
                  />
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        <div className="table-meta"><span>{rows.length} rates</span><span>Both Regulatory Authority and Internal Planning rates stored per pair/year</span></div>
      </div>
    );
  }

  function AlertSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head">
          <span className="card-title">Alert Configuration</span>
          <ResetButton ctx={ctx} section="alerts" label="Alert Config" />
          <span className="field-hint">Six fixed alert types · changes take effect immediately across alerts and Action Cockpit cards</span>
        </div>
        <table className="tbl">
          <thead><tr><th>Alert Type</th><th style={{ width: 90 }}>Enabled</th><th style={{ width: 150 }}>Threshold</th><th style={{ width: 130 }}>Severity <span className="field-hint" style={{ fontWeight: 400, fontSize: 10 }} title="CRITICAL = immediate action, WARNING = attention soon, FLAG = informational">ⓘ</span></th></tr></thead>
          <tbody>{store.alertConfig.map((a) => (
            <tr key={a.id}>
              <td><div style={{ fontWeight: 500 }}>{a.label}</div><div className="field-hint">{a.desc}</div></td>
              <td><label className="row" style={{ gap: 6, cursor: 'pointer' }}><input type="checkbox" checked={a.on} onChange={(e) => { ctx.actions.toggleAlert(a.id, { on: e.target.checked }); toast('Alert ' + (e.target.checked ? 'enabled' : 'disabled'), 'info'); }} /> {a.on ? 'On' : 'Off'}</label></td>
              <td>{a.unit === 'days' || a.unit === '%' || a.unit === 'day of month' ? <div className="row" style={{ gap: 6 }}><input className="input num" style={{ width: 70, height: 30 }} type="number" value={a.threshold} onChange={(e) => ctx.actions.toggleAlert(a.id, { threshold: Number(e.target.value) })} /><span className="muted" style={{ fontSize: 12 }}>{a.unit}</span></div> : <span className="dash">—</span>}</td>
              <td><select className="select" style={{ height: 30, fontSize: 12 }} value={a.severity} onChange={(e) => ctx.actions.toggleAlert(a.id, { severity: e.target.value })}><option value="critical">CRITICAL</option><option value="warning">WARNING</option><option value="flag">FLAG</option></select></td>
            </tr>
          ))}</tbody>
        </table>
        <div className="table-meta"><span>6 fixed types (FRD-SET-018)</span><span>WP&amp;B utilization threshold is configured separately in Action Cockpit settings</span></div>
      </div>
    );
  }

  function CockpitActionsSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const configs = (store.cockpitActionsConfig || []).slice().sort((a, b) => (a.sortOrder || 99) - (b.sortOrder || 99));
    const ROLE_OPTIONS = [
      { value: 'cbcc', label: 'CBCC' },
      { value: 'admin', label: 'Admin' },
      { value: 'manager', label: 'Manager (v0.2+)' },
      { value: 'staff', label: 'Staff (v0.2+)' },
    ];
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head">
          <span className="card-title">Action Cockpit Configuration</span>
          <ResetButton ctx={ctx} section="cockpit_actions" label="Cockpit Actions" />
          <span className="field-hint">Which bounded action categories appear in Action Cockpit and at what thresholds they fire</span>
        </div>
        <table className="tbl">
          <thead><tr>
            <th>Category</th>
            <th style={{ width: 70 }}>Enabled</th>
            <th>Description</th>
            <th style={{ width: 100 }}>Owner Role</th>
            <th style={{ width: 200 }}>Threshold</th>
          </tr></thead>
          <tbody>
            {configs.map((c) => (
              <tr key={c.id}>
                <td style={{ fontWeight: 500 }}>{c.label}</td>
                <td>
                  <label className="row" style={{ gap: 6, cursor: 'pointer' }}>
                    <input type="checkbox" checked={c.on}
                      onChange={(e) => { ctx.actions.setCockpitActionConfig(c.id, { on: e.target.checked }); toast(c.label + (e.target.checked ? ' enabled' : ' disabled'), 'info'); }} />
                    {c.on ? 'On' : 'Off'}
                  </label>
                </td>
                <td><div className="field-hint">{c.desc}</div></td>
                <td>
                  <select className="select" style={{ height: 30, fontSize: 12, width: 120 }} value={c.ownerRole || 'cbcc'}
                    onChange={(e) => { ctx.actions.setCockpitActionConfig(c.id, { ownerRole: e.target.value }); toast('Owner role updated', 'info'); }}>
                    {ROLE_OPTIONS.map((r) => <option key={r.value} value={r.value}>{r.label}</option>)}
                  </select>
                </td>
                <td>
                  {c.warningThreshold != null ? (
                    <div className="row" style={{ gap: 6, alignItems: 'center' }}>
                      <input className="input num" style={{ width: 70, height: 30 }} type="number" min="1" max="99"
                        value={c.warningThreshold}
                        onChange={(e) => { ctx.actions.setCockpitActionConfig(c.id, { warningThreshold: Number(e.target.value) }); toast('WP&B warning threshold updated', 'good'); }} />
                      <span className="muted" style={{ fontSize: 12 }}>% consumed</span>
                    </div>
                  ) : c.cashCallScope != null ? (
                    <div className="row" style={{ gap: 6, alignItems: 'center' }}>
                      <input className="input num" style={{ width: 70, height: 30 }} type="number" min="1" max="12"
                        value={c.cashCallScope}
                        onChange={(e) => { ctx.actions.setCockpitActionConfig(c.id, { cashCallScope: Number(e.target.value) }); toast('AC pending scope updated to ' + e.target.value + ' month(s)', 'good'); }} />
                      <span className="muted" style={{ fontSize: 12 }}>past month(s)</span>
                    </div>
                  ) : (
                    <span className="muted" style={{ fontSize: 12 }}>Driven by Alert Config thresholds</span>
                  )}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        <div className="table-meta">
          <span>{configs.filter((c) => c.on).length} of {configs.length} categories enabled</span>
          <span>WP&amp;B threshold is independent — it governs the cockpit card, not the sidebar critical count</span>
        </div>
      </div>
    );
  }

  function AmendmentNextStepsSection({ ctx }) {
    const { store } = ctx;
    const types = (store.lookups.amendmentType || []).filter((t) => t.active);
    const stepMeta = store.lookups.amendmentNextStep || [];
    const metaFor = (label) => stepMeta.find((s) => s.label === label || s.id === label) || {};
    return (
      <div>
        <div className="card card-pad" style={{ maxWidth: 760 }}>
          <div className="section-title">Amendment Next Step Options</div>
          <div className="field-hint mb16">
            Default next-step options shown when recording an amendment per Amendment Type. The system auto-generates follow-up steps after an amendment is saved (e.g. "Update AFOP", "Update IO", "Close Out"). Data-update steps deep-link to the related page and appear in the Action Cockpit until completed. <strong>Admins configure which options are available per amendment type here.</strong>
          </div>
          {types.map((t, idx) => {
            const opts = E.NEXT_STEP_OPTIONS[t.id] || ['Close Out'];
            return (
              <div key={t.id} style={{ marginBottom: 14, paddingBottom: 14, borderBottom: idx < types.length - 1 ? '1px solid var(--border-default)' : 'none' }}>
                <div style={{ fontWeight: 600, marginBottom: 6 }}>{t.label}</div>
                <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
                  {opts.map((opt) => {
                    const meta = metaFor(opt);
                    const target = meta.targetModule ? ' · ' + meta.targetModule + (meta.targetTab ? '/' + meta.targetTab : '') : '';
                    return <span key={opt} className={meta.requiresDataUpdate ? 'badge badge-warning' : 'badge badge-neutral'} style={{ fontSize: 12 }}>{opt}{target}</span>;
                  })}
                </div>
              </div>
            );
          })}
          <div className="field-hint mt8" style={{ color: 'var(--color-warning-700)', display: 'flex', gap: 6, alignItems: 'flex-start' }}>
            <Icon name="info" size={13} />
            <span>Options are bounded in this version: admins can see the target behavior, while per-type editing stays system-managed for the prototype.</span>
          </div>
        </div>
      </div>
    );
  }

  function WpbSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const [wpbYear, setWpbYear] = useState(E.CY);
    const years = [...new Set([E.CY, E.CY + 1])];
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head"><span className="card-title">WP&amp;B Master Data · FY{E.CY}</span>
          <button className="btn btn-primary btn-sm" onClick={() => setWpbAdd(!wpbAdd)} style={{ marginLeft: 8 }}><Icon name="plus" size={14} />Add</button><button className="btn btn-secondary btn-sm" onClick={() => toast('Copied FY' + wpbYear + ' structure to FY' + (wpbYear + 1) + ' with amounts reset to zero (mock)', 'good')}><Icon name="copy" size={14} />Copy prior year</button>
        </div>
        <table className="tbl tbl-compact"><thead><tr><th>Composite Key</th><th>BS</th><th>Line</th><th>WP&amp;B Description</th><th>Group</th><th className="num-col">Approved Budget (USD)</th></tr></thead>
          <tbody>{store.wpbItems.map((w) => (
            <tr key={w.id}><td className="mono" style={{ fontSize: 11 }}>{w.key}</td><td className="mono">{w.schedule}</td><td className="mono">{w.line}</td><td>{w.name}</td><td>{E.lkLabel(store, 'wpGroup', w.group)}</td>
              <td className="num-col"><input className="input mono" style={{ width: 130, height: 30, textAlign: 'right' }} type="number" value={w.approved} onChange={(e) => ctx.actions.setWpbApproved(w.id, e.target.value)} /></td></tr>
          ))}</tbody>
        </table>
        <div className="table-meta"><span>{store.wpbItems.length} WP&amp;B line items</span><span>Editing Approved Budget recalculates headroom &amp; utilization everywhere</span></div>
      </div>
    );
  }

  function IoSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const [ioYear, setIoYear] = useState(E.CY);
    const years2 = [...new Set([E.CY, E.CY + 1])];
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head"><span className="card-title">IO Master Data · FY{ioYear}</span>
          <select className="select" style={{ height: 30, fontSize: 12, width: 90 }} value={String(ioYear)} onChange={(e) => setIoYear(Number(e.target.value))}>{years2.map((y) => <option key={y} value={String(y)}>FY{y}</option>)}</select>
          <button className="btn btn-secondary btn-sm" onClick={() => toast('Copied IO records to FY' + (ioYear + 1) + ' with amounts reset (mock)', 'good')}><Icon name="copy" size={14} />Copy prior year</button>
        </div>
        <table className="tbl tbl-compact"><thead><tr><th>Internal Order</th><th>Activities</th><th className="num-col">Budget (USD)</th><th className="num-col">Budget (IDR)</th></tr></thead>
          <tbody>{store.ios.map((io) => (
            <tr key={io.id}><td className="mono" style={{ fontWeight: 600 }}>{io.io}</td><td>{io.activities}</td>
              <td className="num-col"><input className="input mono" style={{ width: 120, height: 30, textAlign: 'right' }} type="number" value={io.usd} onChange={(e) => ctx.actions.setIoField(io.id, { usd: Number(e.target.value) })} /></td>
              <td className="num-col"><input className="input mono" style={{ width: 150, height: 30, textAlign: 'right' }} type="number" value={Math.round(io.usd * ((store.fxRates.find((r) => r.year === ioYear && r.pair === 'USD/IDR') || store.fxRates.find((r) => r.pair === 'USD/IDR') || { rate: 16000 }).rate || 16000))} readOnly style={{ background: 'var(--color-slate-50)', cursor: 'default' }} /></td></tr>
          ))}</tbody>
        </table>
        <div className="table-meta"><span>{store.ios.length} internal orders</span><span>Editing allocation recalculates IO variance &amp; "IOs at risk"</span></div>
      </div>
    );
  }

  function ImportConfigSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const cfg = store.systemConfig || {};
    const sources = (store.lookups.importSourceType || []).slice().sort((a, b) => (a.sortOrder || 99) - (b.sortOrder || 99));
    const allowedTypes = cfg.importAllowedFileTypes || ['csv', 'xlsx'];
    const priority = cfg.sourcePriority || [];
    return (
      <div>
        <div className="card card-pad" style={{ maxWidth: 860 }}>
          <div className="section-title">Import Configuration</div>
          <div style={{ background: 'var(--color-blue-50)', border: '1px solid var(--color-blue-100)', borderRadius: 6, padding: '10px 14px', marginBottom: 16, fontSize: 'var(--text-sm)', color: 'var(--color-blue-700)' }}>
            <strong>What this controls:</strong> System-level import policies (badge retention, recovery window, file types, unknown-column handling, duplicate detection). The 8 import source types below are <strong>fixed by architecture</strong> — each maps to a specific ERP export or CBCC workbook. You can enable/disable them but cannot add or remove types. To configure what each import type maps to, see the Import module's column mapping.
          </div>
          <div className="field-hint mb16">Controls bounded import behavior, duplicate warnings, and soft-deleted data recovery.</div>
          <div className="form-grid">
            <Field label="Import Badge Retention" hint="Days the 'Imported' badge stays visible on Procurement Plan records after import. FRD-SET-039.">
              <div className="row" style={{ gap: 8, alignItems: 'center' }}>
                <input className="input num" style={{ width: 80, height: 32 }} type="number" min="1" max="180"
                  value={cfg.badgeRetentionDays || 14}
                  onChange={(e) => { ctx.actions.setSystemConfig({ badgeRetentionDays: Number(e.target.value) }); toast('Badge retention updated', 'good'); }} />
                <span className="muted" style={{ fontSize: 12 }}>days</span>
              </div>
            </Field>
            <Field label="Recovery Window" hint="Days deleted Procurement Plan records can be restored before auto-purge.">
              <div className="row" style={{ gap: 8, alignItems: 'center' }}>
                <input className="input num" style={{ width: 80, height: 32 }} type="number" min="1" max="30"
                  value={cfg.recoveryWindowDays || 7}
                  onChange={(e) => { ctx.actions.setSystemConfig({ recoveryWindowDays: Number(e.target.value) }); toast('Recovery window updated', 'good'); }} />
                <span className="muted" style={{ fontSize: 12 }}>days</span>
              </div>
            </Field>
            <Field label="Allowed File Types" hint="Direct file storage stays out of v1; these are accepted import workbook formats only.">
              <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
                {allowedTypes.map((t) => <span key={t} className="badge badge-neutral">{t.toUpperCase()}</span>)}
              </div>
            </Field>
            <Field label="Unknown Columns" hint="Unknown columns are reviewed and ignored unless mapped; they do not create new business fields.">
              <select className="select" value={cfg.importUnknownColumnPolicy || 'review_then_ignore'}
                onChange={(e) => { ctx.actions.setSystemConfig({ importUnknownColumnPolicy: e.target.value }); toast('Unknown-column policy updated', 'good'); }}>
                <option value="review_then_ignore">Review then ignore</option>
                <option value="block_until_mapped">Block until mapped</option>
              </select>
            </Field>
            <Field label="Duplicate Handling" hint="Duplicate keys create warning-level review items, not silent overwrites.">
              <select className="select" value={cfg.duplicateMatchMode || 'soft_warning'}
                onChange={(e) => { ctx.actions.setSystemConfig({ duplicateMatchMode: e.target.value }); toast('Duplicate handling updated', 'good'); }}>
                <option value="soft_warning">Soft warning</option>
                <option value="block_exact_duplicates">Block exact duplicates</option>
              </select>
            </Field>
          </div>
        </div>

        <div className="card mt16" style={{ overflow: 'hidden' }}>
          <div className="card-head">
            <span className="card-title">Import Source Types</span>
            <span className="field-hint">{sources.filter((s) => s.active).length} active of {sources.length} configured source types</span>
          </div>
          <table className="tbl tbl-compact">
            <thead><tr><th>Source Type</th><th>Target</th><th>Source System</th><th>Accepted</th><th>Duplicate Keys</th><th style={{ width: 90 }}>Enabled</th></tr></thead>
            <tbody>{sources.map((s) => (
              <tr key={s.id} style={{ opacity: s.active ? 1 : 0.6 }}>
                <td><div style={{ fontWeight: 600 }}>{s.label}</div><div className="field-hint">{s.group}{s.lockedFieldSource ? ' · source-owned fields' : ' · locally reviewable fields'}</div></td>
                <td>{s.target}</td>
                <td>{s.sourceSystem}</td>
                <td>{(s.accepts || []).map((t) => <span key={t} className="badge badge-neutral" style={{ marginRight: 4 }}>{t.toUpperCase()}</span>)}</td>
                <td style={{ maxWidth: 260 }}>{(s.duplicateKeys || []).map((k) => <span key={k} className="badge badge-neutral" style={{ marginRight: 4 }}>{k}</span>)}</td>
                <td><label className="row" style={{ gap: 6, cursor: 'pointer' }}><input type="checkbox" checked={!!s.active}
                  onChange={(e) => { ctx.actions.updateLookup('importSourceType', s.id, { active: e.target.checked }); toast(s.label + (e.target.checked ? ' enabled' : ' disabled'), 'info'); }} />{s.active ? 'On' : 'Off'}</label></td>
              </tr>
            ))}</tbody>
          </table>
          <div className="table-meta"><span>Duplicate keys are warning keys</span><span>Source priority: {priority.join(' → ')}</span></div>
        </div>
      </div>
    );
  }

  function RecentlyDeletedSection({ ctx }) {
    const { store } = ctx;
    const toast = window.useToast();
    const recoveryWindowDays = (store.systemConfig || {}).recoveryWindowDays || 7;
    const deleted = [
      ...(store.procurementPlan || []).filter((p) => p.deletedAt).map((p) => ({
        type: 'Procurement Plan', id: p.id, ref: p.ppNo, title: p.title, deletedAt: p.deletedAt,
        statusList: 'procPlanCode', statusId: p.code,
      })),
      ...(store.acRecords || []).filter((a) => a.deletedAt).map((a) => ({
        type: 'AC Log', id: a.id, ref: a.contractNo + ' / AC ' + a.acNumber, title: a.vendor, deletedAt: a.deletedAt,
        statusList: 'acStatus', statusId: a.status,
      })),
    ];
    const restore = (row) => {
      if (row.type === 'AC Log') ctx.actions.restoreAcRecord(row.id);
      else ctx.actions.updateProcPlan(row.id, { deletedAt: null });
      toast(row.type + ' record restored', 'good');
    };
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head"><span className="card-title">Recently Deleted Records</span>
          <span className="field-hint">Recovery window: {recoveryWindowDays} days · configurable in Import Configuration</span>
        </div>
        {deleted.length === 0 ? (
          <div style={{ padding: 32, textAlign: 'center' }}>
            <Icon name="trash-2" size={32} color="#cbd5e1" />
            <div className="muted mt8">No recently deleted records</div>
          </div>
        ) : (
          <table className="tbl tbl-compact">
            <thead><tr><th>Type</th><th>Reference</th><th>Title</th><th>Deleted</th><th>Status</th><th>Recovery</th><th></th></tr></thead>
            <tbody>{deleted.map((row) => {
              const deletedAt = new Date(row.deletedAt);
              const expiresAt = new Date(deletedAt.getTime() + recoveryWindowDays * 86400000);
              const daysLeft = Math.ceil((expiresAt - new Date()) / 86400000);
              const expired = daysLeft <= 0;
              return <tr key={row.type + row.id} style={{ opacity: 0.7 }}>
                <td>{row.type}</td>
                <td className="mono">{row.ref}</td>
                <td style={{ maxWidth: 220, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{row.title}</td>
                <td style={{ fontSize: 12 }}>{deletedAt.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })}</td>
                <td><StatusBadge store={store} list={row.statusList} id={row.statusId} dot /></td>
                <td>{expired ? <span className="badge badge-neutral">Expired</span> : <span className="badge badge-warning">{daysLeft}d left</span>}</td>
                <td>{expired ? <span className="muted" style={{ fontSize: 12 }}>Purged</span> : <button className="btn btn-primary btn-sm" onClick={() => restore(row)}><Icon name="rotate-ccw" size={13} />Restore</button>}</td>
              </tr>;
            })}</tbody>
          </table>
        )}
        <div className="table-meta"><span>{deleted.length} deleted record(s)</span><span>Deleted records count toward import duplicate detection</span></div>
      </div>
    );
  }

  /* ---------------- Users & Roles ---------------- */
  function UsersModule() {
    const ctx = window.useStore();
    const { store } = ctx;
    const toast = window.useToast();
    const PERMS = window.IMTRACK_PERMS || {};
    const ACTIONS = window.IMTRACK_ACTIONS_LIST || ['view', 'create', 'edit', 'delete', 'import', 'export', 'configure', 'assign'];
    const [tab, setTab] = useState('users');
    const [activeRole, setActiveRole] = useState('admin');
    const [userModal, setUserModal] = useState(false);
    const [editUid, setEditUid] = useState(null);
    const [userForm, setUserForm] = useState({ name: '', email: '', role: 'cbcc', assigned: 'all' });
    const MODULES = [['dashboard', 'Action Cockpit'], ['procurement', 'Procurement Plan'], ['contracts', 'Contracts'], ['aclog', 'AC Log'], ['budget', 'Budget'], ['reports', 'Reports'], ['import', 'Import'], ['settings', 'Settings'], ['users', 'Users & Roles']];

    return (
      <div className="page page-wide">
        <PageHeader title="Users & Roles" subtitle="Access control · Admin only">
          <span className="demo-banner"><Icon name="info" size={14} />v0.1 mock — production enforces this server-side</span>
        </PageHeader>
        <div className="tabs">
          <div className={'tab' + (tab === 'users' ? ' active' : '')} onClick={() => setTab('users')}>Users</div>
          <div className={'tab' + (tab === 'roles' ? ' active' : '')} onClick={() => setTab('roles')}>Roles &amp; Permissions</div>
        </div>

        {tab === 'users' && (
          <div className="card" style={{ overflow: 'hidden' }}>
            <div className="table-toolbar"><span className="card-title">Users</span><div className="spacer" /><button className="btn btn-primary btn-sm" onClick={() => { setEditUid(null); setUserForm({ name: '', email: '', role: 'cbcc', assigned: 'all' }); setUserModal(true); }}><Icon name="plus" size={14} />Add User</button></div>
            <table className="tbl"><thead><tr><th>User</th><th>Email</th><th>Role</th><th>Status</th><th>Record Scope</th><th></th></tr></thead>
              <tbody>{store.users.map((u) => {
                const admins = store.users.filter((x) => x.role === 'admin' && x.active).length;
                const isLastAdmin = u.role === 'admin' && u.active && admins <= 1;
                return (
                <tr key={u.id} style={{ opacity: u.active ? 1 : 0.55 }}><td><div className="row" style={{ gap: 8 }}><div className="avatar" style={{ width: 28, height: 28, fontSize: 11, background: 'var(--color-navy-600)' }}>{u.initials}</div><span style={{ textDecoration: u.active ? 'none' : 'line-through' }}>{u.name}</span></div></td>
                  <td className="mono" style={{ fontSize: 12 }}>{u.email}</td><td><span className="badge badge-navy">{store.roles.find((r) => r.id === u.role).name}</span></td>
                  <td>{u.active ? <span className="badge badge-green">Active</span> : <span className="badge badge-neutral">Inactive</span>}</td>
                  <td>{u.assigned === 'all' ? 'All records' : 'Own only'}</td>
                  <td><div className="cell-actions"><button className="icon-btn" title="Edit" onClick={(e) => { e.stopPropagation(); setEditUid(u.id); setUserForm({ name: u.name, email: u.email, role: u.role, assigned: u.assigned }); setUserModal(true); }}><Icon name="pencil" size={14} /></button><button className="icon-btn" disabled={isLastAdmin} title={isLastAdmin ? 'Cannot deactivate last admin' : (u.active ? 'Deactivate' : 'Reactivate')} onClick={(e) => { e.stopPropagation(); if (isLastAdmin) { toast('Cannot deactivate — at least one active Admin must remain', 'warn'); return; } ctx.actions.deactivateUser(u.id); toast(u.name + (u.active ? ' deactivated' : ' reactivated'), 'info'); }} style={isLastAdmin ? { opacity: 0.35, cursor: 'not-allowed' } : undefined}><Icon name={u.active ? 'x' : 'check'} size={14} /></button></div></td>
                </tr>
              );})}</tbody>
            </table>
          </div>
        )}

        {userModal && <Modal title={editUid ? 'Edit User' : 'Add User'} subtitle="v0.1 mock — server-enforced in production" onClose={() => setUserModal(false)}
          footer={<><button className="btn btn-secondary" onClick={() => setUserModal(false)}>Cancel</button><button className="btn btn-primary" onClick={() => { if (!userForm.name || !userForm.email) { toast('Name and email required', 'warn'); return; } if (editUid) { ctx.actions.updateUser(editUid, userForm); toast('User updated', 'good'); } else { ctx.actions.addUser(userForm); toast('User added — mock', 'info'); } setUserModal(false); }}>{editUid ? 'Save' : 'Add'}</button></>}>
          <div className="form-grid">
            <Field label="Name" req><input className="input" value={userForm.name} onChange={(e) => setUserForm({ ...userForm, name: e.target.value })} /></Field>
            <Field label="Email" req><input className="input mono" value={userForm.email} onChange={(e) => setUserForm({ ...userForm, email: e.target.value })} /></Field>
            <Field label="Role"><select className="select" value={userForm.role} onChange={(e) => setUserForm({ ...userForm, role: e.target.value })}>{store.roles.map((r) => <option key={r.id} value={r.id}>{r.name}</option>)}</select></Field>
            <Field label="Record Scope"><select className="select" value={userForm.assigned} onChange={(e) => setUserForm({ ...userForm, assigned: e.target.value })}><option value="all">All records</option><option value="own">Own only</option></select></Field>
          </div>
        </Modal>}

        {tab === 'roles' && (
          <div className="settings-layout">
            <div className="settings-nav">
              {store.roles.map((r) => <button key={r.id} className={activeRole === r.id ? 'active' : ''} onClick={() => setActiveRole(r.id)}>{r.name}</button>)}
            </div>
            <div className="card" style={{ overflow: 'hidden' }}>
              <div className="card-head"><span className="card-title">Permission Matrix — {store.roles.find((r) => r.id === activeRole).name}</span>
                <span className="field-hint">{store.roles.find((r) => r.id === activeRole).desc}</span></div>
              <div className="tbl-wrap"><table className="tbl tbl-compact perm-tbl">
                <thead><tr><th>Module</th>{ACTIONS.map((a) => <th key={a} style={{ textTransform: 'capitalize' }}>{a}</th>)}</tr></thead>
                <tbody>{MODULES.map(([m, label]) => {
                  const granted = (PERMS[activeRole] && PERMS[activeRole][m]) || [];
                  const applicable = (PERMS.admin && PERMS.admin[m]) || [];
                  return <tr key={m}><td style={{ fontWeight: 500 }}>{label}</td>{ACTIONS.map((a) => (
                    <td key={a}>{!applicable.includes(a) ? <span className="dash">—</span> : granted.includes(a) ? <Icon name="check" size={15} color="#16a34a" className="perm-yes" /> : <Icon name="x" size={14} color="#cbd5e1" />}</td>
                  ))}</tr>;
                })}</tbody>
              </table></div>
              <div className="table-meta"><span>Scope: {store.roles.find((r) => r.id === activeRole).scope === 'own' ? 'Own assigned records only' : 'All records'}</span><span>"—" = action not applicable to module</span></div>
            </div>
          </div>
        )}
      </div>
    );
  }

  Object.assign(window, { SettingsModule, UsersModule });
})();
