/* ============================================================
   IMTRACK — App shell, store, permissions, router
   Mounts last. Module components are read off window.
   ============================================================ */
const E = window.IMTRACK_ENGINE;

/* ---------------- Permissions ---------------- */
const ALL_ACTIONS = ['view', 'create', 'edit', 'delete', 'import', 'export', 'configure', 'assign'];
const PERMS = {
  admin: { dashboard: ALL_ACTIONS, procurement: ALL_ACTIONS, contracts: ALL_ACTIONS, aclog: ALL_ACTIONS, budget: ALL_ACTIONS, reports: ALL_ACTIONS, import: ALL_ACTIONS, settings: ALL_ACTIONS, users: ALL_ACTIONS },
  manager: { dashboard: ['view'], contracts: ['view', 'export'], aclog: ['view', 'export'], budget: ['view', 'export'], reports: ['view', 'export'] },
  staff: { dashboard: ['view'], contracts: ['view', 'export'], aclog: ['view', 'create', 'edit', 'export'] },
};
function can(role, module, action) {
  const m = PERMS[role] && PERMS[role][module];
  return !!(m && m.includes(action));
}
function moduleVisible(role, module) { return can(role, module, 'view'); }

const USER_FOR_ROLE = { admin: 'u1', manager: 'u2', staff: 'u3' };
const ROLE_SCOPE_NOTE = {
  admin: 'CBCC/super-user v1 scope',
  manager: 'Deferred mock role for future self-service review',
  staff: 'Deferred mock role for future self-service review',
};

/* ---------------- Store context ---------------- */
const StoreCtx = React.createContext(null);
const useStore = () => React.useContext(StoreCtx);

/* Concurrency model (v0.1):
   - Single CBCC user in v0.1 — last-write-wins is acceptable.
   - v0.2+: Procurement Plan uses optimistic locking (version column) for import conflicts.
   - Contracts/AC remain last-write-wins; amendment corrections are additive, not overwrites.
   - No pessimistic locking needed — edit windows are short and user count is low. */
function StoreProvider({ children }) {
  const [store, setStore] = useState(() => E.buildStore());
  const [role, setRole] = useState(() => localStorage.getItem('imtrack_role') || 'admin');
  const [nav, setNav] = useState(() => {
    try { return JSON.parse(localStorage.getItem('imtrack_nav')) || { view: 'dashboard', params: {} }; }
    catch (e) { return { view: 'dashboard', params: {} }; }
  });

  useEffect(() => { localStorage.setItem('imtrack_role', role); }, [role]);
  useEffect(() => { localStorage.setItem('imtrack_nav', JSON.stringify(nav)); }, [nav]);

  const navigate = (view, params) => { setNav({ view, params: params || {} }); document.querySelector('.main') && (document.querySelector('.main').scrollTop = 0); };
  const update = (fn) => setStore((prev) => { const next = { ...prev }; fn(next); return next; });

  const currentUser = store.users.find((u) => u.id === USER_FOR_ROLE[role]);
  const assignedContracts = (currentUser && currentUser.assigned !== 'all') ? currentUser.assigned : 'all';

  const actions = {
    setAcStatus(acId, statusId, erpAmount) {
      update((s) => {
        s.acRecords = s.acRecords.map((a) => {
          if (a.id !== acId) return a;
          const done = statusId === 's05';
          const prevStatus = a.status;
          const next = { ...a, status: statusId };
          if (done) {
            next.erpAmount = (erpAmount !== undefined && erpAmount !== null && erpAmount !== '') ? Number(erpAmount) : (a.erpAmount || a.valueSubmitted);
            next.sharedFolder = true;
            if (!a.erpAchieve) { next.erpAchieve = a.periodEnd; next.erpCreate = a.periodEnd; }
          }
          next.acStatusHistory = [...(a.acStatusHistory || []), {
            from: prevStatus, to: statusId,
            changedBy: a.pic || 'CBCC',
            changedAt: E.fmtDate(E.TODAY),
            source: 'manual',
          }];
          return next;
        });
      });
    },
    setAcField(acId, patch) {
      update((s) => {
        s.acRecords = s.acRecords.map((a) => {
          if (a.id !== acId) return a;
          const prev = a.status;
          const next = { ...a, ...patch };
          if (patch.valueSubmitted !== undefined) {
            next.erpAmount = patch.valueSubmitted;
          }
          if (patch.status && patch.status !== prev) {
            next.acStatusHistory = [...(a.acStatusHistory || []), {
              from: prev, to: patch.status,
              changedBy: a.pic || 'CBCC',
              changedAt: E.fmtDate(E.TODAY),
              source: 'manual',
            }];
          }
          return next;
        });
      });
    },
    deleteAcRecord(acId) {
      update((s) => {
        s.acRecords = s.acRecords.map((a) => a.id === acId ? { ...a, deletedAt: E.fmtDate(E.TODAY) } : a);
      });
    },
    restoreAcRecord(acId) {
      update((s) => {
        s.acRecords = s.acRecords.map((a) => a.id === acId ? { ...a, deletedAt: null } : a);
      });
    },
    addAcRecords(records) { update((s) => { s.acRecords = [...s.acRecords, ...records]; }); },
    duplicateAc(acId) {
      update((s) => {
        const src = s.acRecords.find((a) => a.id === acId);
        if (!src) return;
        const sameContract = s.acRecords.filter((a) => a.contractId === src.contractId);
        const maxN = Math.max(0, ...sameContract.map((a) => parseInt(a.acNumber, 10) || 0));
        const newNum = String(maxN + 1).padStart(3, '0');
        const copy = { ...src, id: 'ac' + Date.now(), acNumber: newNum, erpAcNo: src.contractNo + '-O-' + newNum, status: 's01', erpAmount: 0, sharedFolder: false, erpCreate: null, erpAchieve: null, erpPost: null, erpClear: null, erpPoNo: src.erpPoNo, notes: '', acStatusHistory: [{ from: null, to: 's01', changedBy: 'A. Controller', changedAt: E.fmtDate(E.TODAY), source: 'duplicate' }] };
        s.acRecords = [...s.acRecords, copy];
      });
    },
    setAfopAmount(afId, amt) { update((s) => { s.afopLines = s.afopLines.map((a) => a.id === afId ? { ...a, amt: Number(amt) } : a); }); },
    setAfopField(afId, patch) { update((s) => { s.afopLines = s.afopLines.map((a) => a.id === afId ? { ...a, ...patch } : a); }); },
    addAfopLine(line) { update((s) => { s.afopLines = [...s.afopLines, line]; }); },
    deleteAfopLine(afId) { update((s) => { s.afopLines = s.afopLines.map((a) => a.id === afId ? { ...a, deletedAt: new Date().toISOString().slice(0,10) } : a); }); },
    addPoLine(line) { update((s) => { s.poLines = [...s.poLines, line]; }); },
    setPoField(poId, patch) { update((s) => { s.poLines = s.poLines.map((p) => p.id === poId ? { ...p, ...patch } : p); }); },
    deletePoLine(poId) { update((s) => { s.poLines = s.poLines.map((p) => p.id === poId ? { ...p, deletedAt: new Date().toISOString().slice(0,10) } : p); }); },
    addFxRate(r) { update((s) => { s.fxRates = [...s.fxRates, { id: 'fx_' + Date.now(), ...r }]; }); },
    setWpbApproved(itemId, amt) { update((s) => { s.wpbItems = s.wpbItems.map((w) => w.id === itemId ? { ...w, approved: Number(amt) } : w); }); },
    addWpbItem(item) { update((s) => { s.wpbItems = [...s.wpbItems, { ...item, active: true }]; }); },
    deactivateWpbItem(itemId) { update((s) => { s.wpbItems = s.wpbItems.map((w) => w.id === itemId ? { ...w, active: false } : w); }); },
    setIoField(ioId, patch) { update((s) => { s.ios = s.ios.map((io) => io.id === ioId ? { ...io, ...patch } : io); }); },
    addIoRecord(io) { update((s) => { s.ios = [...s.ios, { ...io, active: true }]; }); },
    deactivateIo(ioId) { update((s) => { s.ios = s.ios.map((io) => io.id === ioId ? { ...io, active: false } : io); }); },
    toggleAlert(id, patch) { update((s) => { s.alertConfig = s.alertConfig.map((a) => a.id === id ? { ...a, ...patch } : a); }); },
    setCockpitActionConfig(ruleId, patch) { update((s) => { s.cockpitActionsConfig = s.cockpitActionsConfig.map((c) => c.id === ruleId ? { ...c, ...patch } : c); }); },
    setSystemConfig(patch) { update((s) => { s.systemConfig = { ...s.systemConfig, ...patch }; }); },
    setFxRate(id, patch) { update((s) => { s.fxRates = s.fxRates.map((r) => r.id === id ? { ...r, ...patch } : r); }); },
    addLookup(list, value) { update((s) => { s.lookups = { ...s.lookups, [list]: [...s.lookups[list], value] }; }); },
    updateLookup(list, id, patch) { update((s) => { s.lookups = { ...s.lookups, [list]: s.lookups[list].map((v) => v.id === id ? { ...v, ...patch } : v) }; }); },
    updateContract(id, patch) { update((s) => { s.contracts = s.contracts.map((c) => c.id === id ? { ...c, ...patch } : c); }); },
    addContract(c) { update((s) => { s.contracts = [c, ...s.contracts]; }); },
    addAmendment(contractId, amend) {
      update((s) => {
        s.contracts = s.contracts.map((c) => {
          if (c.id !== contractId) return c;
          const next = { ...c, amendments: [...(c.amendments || []), {
            ...amend,
            nextStepOptions: amend.nextStepOptions || E.defaultNextStepOptions(s, amend.type),
            nextStep: amend.nextStep || E.defaultNextStepOptions(s, amend.type)[0],
          }] };
          amend.changes.forEach((ch) => {
            if (ch.field === 'End Date') next.end = ch.neu;
            if (ch.field === 'Current Contract Value') { const v = parseFloat(ch.neu.replace(/[^0-9.]/g, '')); if (!isNaN(v)) next.current = v; }
          });
          return next;
        });
      });
    },
    updateAmendment(contractId, amendNo, patch) {
      update((s) => {
        s.contracts = s.contracts.map((c) => {
          if (c.id !== contractId) return c;
          return {
            ...c,
            amendments: (c.amendments || []).map((a) =>
              a.no === amendNo ? { ...a, ...patch } : a
            ),
          };
        });
      });
    },
    recordCorrectionAmendment(contractId, originalAmendmentId) {
      update((s) => {
        s.contracts = s.contracts.map((c) => {
          if (c.id !== contractId) return c;
          const updatedAmendments = (c.amendments || []).map((a) =>
            a.id === originalAmendmentId ? { ...a, status: 'Superseded', supersededAt: E.fmtDate(E.TODAY) } : a
          );
          const original = updatedAmendments.find((a) => a.id === originalAmendmentId);
          if (original) {
            const corrNo = 'Amend ' + (updatedAmendments.length + 1);
            updatedAmendments.push({
              id: 'am_' + contractId + '_corr_' + Date.now(),
              no: corrNo,
              type: original.type,
              date: E.fmtDate(E.TODAY),
              letter: '',
              desc: 'Correction to ' + original.no,
              changes: [],
              status: 'Draft',
              supersedesAmendmentId: originalAmendmentId,
              nextStep: 'Close Out',
              nextStepOptions: E.defaultNextStepOptions(original.type),
            });
          }
          return { ...c, amendments: updatedAmendments };
        });
      });
    },
    updateProcPlan(id, patch) { update((s) => { s.procurementPlan = s.procurementPlan.map((p) => p.id === id ? { ...p, ...patch } : p); }); },
    addDocumentReference(ref) { update((s) => { s.documentReferences = [...(s.documentReferences || []), ref]; }); },
    removeDocumentReference(id) { update((s) => { s.documentReferences = (s.documentReferences || []).map((r) => r.id === id ? { ...r, deletedAt: E.fmtDate(E.TODAY), deletedBy: 'CBCC' } : r); }); },
    restoreDocumentReference(id) { update((s) => { s.documentReferences = (s.documentReferences || []).map((r) => r.id === id ? { ...r, deletedAt: null, deletedBy: null } : r); }); },
    updateDocumentReference(id, patch) { update((s) => { s.documentReferences = (s.documentReferences || []).map((r) => r.id === id ? { ...r, ...patch } : r); }); },
    addProcPlan(p) { update((s) => { s.procurementPlan = [p, ...s.procurementPlan]; }); },
    addIoWpbMapping(m) { update((s) => { s.ioWpbMappings = [...(s.ioWpbMappings || []), m]; }); },
    removeIoWpbMapping(id) { update((s) => { s.ioWpbMappings = (s.ioWpbMappings || []).filter((m) => m.id !== id); }); },
    resetSettingsSection(section) {
      const seed = window.IMTRACK_SEED;
      update((s) => {
        if (section === 'lookups') s.lookups = JSON.parse(JSON.stringify(seed.lookups));
        else if (section === 'fx') s.fxRates = seed.fxRates.map((x) => ({ ...x }));
        else if (section === 'alerts') s.alertConfig = seed.alertConfig.map((x) => ({ ...x }));
        else if (section === 'cockpit_actions') s.cockpitActionsConfig = seed.cockpitActionsConfig.map((x) => ({ ...x }));
        else if (section === 'wpb') { s.wpbSchedules = seed.wpbSchedules.map((x) => ({ ...x })); s.wpbLines = seed.wpbLines.map((x) => ({ ...x })); s.wpbItems = seed.wpbItems.map((x) => ({ ...x })); }
        else if (section === 'io') s.ios = seed.ios.map((x) => ({ ...x }));
      });
    },
    addUser(user) { update((s) => { s.users = [...s.users, { id: 'u' + (s.users.length + 1), initials: user.name.split(' ').map((n) => n[0]).join('').toUpperCase().slice(0, 2), active: true, assigned: 'all', ...user }]; }); },
    updateUser(uid, patch) { update((s) => { s.users = s.users.map((u) => u.id === uid ? { ...u, ...patch } : u); }); },
    deactivateUser(uid) { update((s) => { s.users = s.users.map((u) => u.id === uid ? { ...u, active: false } : u); }); },
  };

  const value = { store, setStore, role, setRole, nav, navigate, actions, can: (m, a) => can(role, m, a), moduleVisible: (m) => moduleVisible(role, m), currentUser, assignedContracts };
  return <StoreCtx.Provider value={value}>{children}</StoreCtx.Provider>;
}

/* ---------------- Sidebar ---------------- */
const NAV_MAIN = [
  { module: 'dashboard', label: 'Action Cockpit', icon: 'layout-dashboard' },
  { module: 'procurement', label: 'Procurement Plan', icon: 'clipboard-list' },
  { module: 'contracts', label: 'Contracts', icon: 'file-text' },
  { module: 'aclog', label: 'AC Log', icon: 'receipt' },
  { module: 'budget', label: 'Budget', icon: 'bar-chart-2' },
  { module: 'reports', label: 'Reports', icon: 'bar-chart' },
  { module: 'import', label: 'Import', icon: 'upload' },
];
const NAV_ADMIN = [
  { module: 'settings', label: 'Settings', icon: 'settings' },
  { module: 'users', label: 'Users & Roles', icon: 'users' },
];

function Sidebar() {
  const { nav, navigate, moduleVisible, store } = useStore();
  const alertCount = useMemo(() => E.alerts(store).filter((a) => a.sev === 'critical').length, [store]);
  const activeView = nav.view === 'contract' ? 'contracts' : nav.view;
  const Item = ({ item }) => {
    const visible = moduleVisible(item.module);
    return (
      <div className={'nav-item' + (activeView === item.module ? ' active' : '') + (visible ? '' : ' disabled')}
        onClick={() => visible && navigate(item.module)}>
        <Icon name={item.icon} size={19} />
        <span>{item.label}</span>
        {item.module === 'dashboard' && alertCount > 0 && visible && <span className="nav-count">{alertCount}</span>}
      </div>
    );
  };
  const showAdmin = NAV_ADMIN.some((n) => moduleVisible(n.module));
  return (
    <nav className="sidebar">
      {NAV_MAIN.map((it) => <Item key={it.module} item={it} />)}
      {showAdmin && <><div className="nav-divider" /><div className="nav-section-label">Administration</div>
        {NAV_ADMIN.map((it) => <Item key={it.module} item={it} />)}</>}
      <div style={{ flex: 1 }} />
      <div className="nav-section-label" style={{ opacity: .5 }}>IMTRACK v0.1 · Mockup</div>
    </nav>
  );
}

/* ---------------- Top bar ---------------- */
const MODULE_NAMES = { dashboard: 'Action Cockpit', procurement: 'Procurement Plan', contracts: 'Contracts', contract: 'Contract Detail', aclog: 'AC Log', budget: 'Budget', reports: 'Reports', settings: 'Settings', users: 'Users & Roles', import: 'Import' };
function TopBar() {
  const { nav, role, setRole, currentUser } = useStore();
  return (
    <header className="topbar">
      <div className="topbar-breadcrumb">
        <span>IMTRACK</span>
        <Icon name="chevron-right" size={14} />
        <span className="crumb-current">{MODULE_NAMES[nav.view] || 'Action Cockpit'}</span>
      </div>
      <div className="topbar-right">
        <div className="role-switch" title="Mock role switcher (v0.1 demo only)">
          <span className="role-tag">View as</span>
          {['admin', 'manager', 'staff'].map((r) => (
            <button key={r} className={role === r ? 'active' : ''} title={ROLE_SCOPE_NOTE[r]} onClick={() => setRole(r)}>{r.charAt(0).toUpperCase() + r.slice(1)}</button>
          ))}
        </div>
        <div className="user-chip">
          <div className="avatar">{currentUser ? currentUser.initials : '–'}</div>
          <div>
            <div className="uname">{currentUser ? currentUser.name : '–'}</div>
            <div className="urole">{role.charAt(0).toUpperCase() + role.slice(1)}</div>
          </div>
        </div>
      </div>
    </header>
  );
}

/* ---------------- No-permission state ---------------- */
function NoAccess() {
  return (
    <div className="page">
      <div className="card card-pad" style={{ maxWidth: 520, margin: '60px auto', textAlign: 'center' }}>
        <Icon name="alert-circle" size={40} color="#94a3b8" />
        <h2 style={{ fontSize: 'var(--text-lg)', margin: '12px 0 6px' }}>Access restricted</h2>
        <p className="muted" style={{ fontSize: 'var(--text-sm)', lineHeight: 1.6 }}>
          Your current role does not have access to this module. Switch back to a role with permission, or contact an administrator.
        </p>
      </div>
    </div>
  );
}

/* ---------------- Router ---------------- */
function Router() {
  const { nav, moduleVisible } = useStore();
  const W = window;
  const view = nav.view;
  const moduleForView = view === 'contract' ? 'contracts' : view;
  if (!moduleVisible(moduleForView)) return <NoAccess />;
  switch (view) {
    case 'dashboard': return <W.Dashboard />;
    case 'procurement': return <W.ProcurementModule />;
    case 'contracts': return <W.ContractsModule />;
    case 'contract': return <W.ContractDetail id={nav.params.id} initialTab={nav.params.tab} />;
    case 'aclog': return <W.ACLogModule />;
    case 'budget': return <W.BudgetModule />;
    case 'reports': return <W.ReportsModule />;
    case 'settings': return <W.SettingsModule />;
    case 'users': return <W.UsersModule />;
    case 'import': return <W.ImportModule />;
    default: return <W.Dashboard />;
  }
}

function TweaksLayer() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#2563eb", "density": "regular", "fontScale": "regular" }/*EDITMODE-END*/;
  const [t, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
  useEffect(() => {
    const r = document.documentElement.style;
    const accents = { '#2563eb': ['#2563eb', '#1d4ed8'], '#0f766e': ['#0f766e', '#115e59'], '#1e3a5f': ['#1e3a5f', '#0f1f3d'], '#7c3aed': ['#6d28d9', '#5b21b6'] };
    const [a, ah] = accents[t.accent] || [t.accent, t.accent];
    r.setProperty('--accent', a); r.setProperty('--accent-hover', ah);
    r.setProperty('--row-pad-y', t.density === 'compact' ? '5px' : t.density === 'comfy' ? '12px' : '8px');
    document.body.style.fontSize = t.fontScale === 'small' ? '12.5px' : t.fontScale === 'large' ? '14.5px' : '13px';
  }, [t]);
  const { TweaksPanel, TweakSection, TweakColor, TweakRadio } = window;
  if (!TweaksPanel) return null;
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Appearance" />
      <TweakColor label="Accent" value={t.accent} options={['#2563eb', '#0f766e', '#1e3a5f', '#7c3aed']} onChange={(v) => setTweak('accent', v)} />
      <TweakRadio label="Table density" value={t.density} options={['compact', 'regular', 'comfy']} onChange={(v) => setTweak('density', v)} />
      <TweakRadio label="Text size" value={t.fontScale} options={['small', 'regular', 'large']} onChange={(v) => setTweak('fontScale', v)} />
    </TweaksPanel>
  );
}

function App() {
  return (
    <ToastProvider>
      <StoreProvider>
        <div className="app">
          <div className="logo-area">
            <div className="logo-mark">IM</div>
            <div className="logo-word">IMTRACK</div>
          </div>
          <TopBar />
          <Sidebar />
          <main className="main"><Router /></main>
        </div>
        <TweaksLayer />
      </StoreProvider>
    </ToastProvider>
  );
}

window.useStore = useStore;
window.IMTRACK_can = can;
window.IMTRACK_PERMS = PERMS;
window.IMTRACK_ACTIONS_LIST = ALL_ACTIONS;
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
