/* global React, ReactDOM, Sidebar, Topbar, LoginPage, Dashboard, InventoryPage, CompaniesPage, UsersPage, LocationTypesPage, LookupsPage, AssetsPage, ReadersAdminPage, LocationsAdminPage, CustomFieldsPage, AssetTypesPage, StockingTargetsPage, TenantsPage, HubClientsPage, CompanyPagesPage, useTweaks, TweaksPanel, TweakSection, TweakToggle, TweakRadio */
const { useState, useEffect } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "sidebarCollapsed": false,
  "density": "comfortable"
}/*EDITMODE-END*/;

// Default scope when nothing is picked yet.  See data.jsx visibleCompanies()
// for how this is interpreted by every list page.
const ALL_SCOPE = { kind:'all', id:null };

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [loggedIn, setLoggedIn] = useState(false);
  const [user, setUser] = useState(null);
  const [route, setRoute] = useState('dashboard');
  // Active scope is one of:
  //   { kind:'all',     id:null }   — show everything in the user's role bound
  //   { kind:'tenant',  id:<n>  }   — narrow to a Tenant's companies
  //   { kind:'company', id:<n>  }   — narrow to a single Company
  // SystemAdmin and TenantAdmin can change this from the topbar dropdown;
  // CustomerAdmin / Operator / Viewer don't see a switcher at all.
  const [activeScope, setActiveScope] = useState(ALL_SCOPE);
  const [restoring, setRestoring] = useState(true);   // checking /api/auth/me on mount
  const collapsed = tweaks.sidebarCollapsed;

  // Set the active scope on both the React state AND the global, in one go.
  // Synchronously updating window.__activeScope here (instead of via a
  // useEffect that fires post-commit) is critical: <Page key=…> re-mounts
  // in the same render cycle and its useState reads the global immediately —
  // a useEffect would set the global too late.
  const setScope = (s) => {
    window.__activeScope = s;
    setActiveScope(s);
  };

  // Apply a LoginResponse-shape object to the React state.  Shared by the
  // login flow and the /api/auth/me restore flow.
  //
  // The HTML the server inlines on the initial GET / is built from an
  // *anonymous* snapshot (Tenants / Companies / Users / Assets are empty —
  // only lookup tables are populated) so nothing leaks before login.  After
  // a successful login we therefore re-fetch /api/snapshot, which the server
  // scopes to the caller's role, and overwrite CloudGate.DB in place before
  // the page re-renders.
  const applyUser = async (u) => {
    const initials = (u.UserName || u.Email || '?')
      .split(/[\s_\-./@]+/).filter(Boolean).slice(0,2).map(x => x[0]).join('').toUpperCase() || '?';
    const userObj = {
      id:          u.Id,
      name:        u.UserName,
      email:       u.Email,
      initials:    initials,
      role:        u.UserLevel,
      roleLabel:   u.UserLevel,
      companyId:   u.CompanyId,
      companyName: u.CompanyName,
      tenantId:    u.TenantId,
      tenantName:  u.TenantName,
    };
    window.__currentUser = userObj;
    try {
      const res = await fetch('/api/snapshot');
      if (res.ok) {
        const snap = await res.json();
        if (snap && snap.DB) Object.assign(window.CloudGate.DB, snap.DB);
      }
    } catch (_) { /* keep whatever was inlined; pages will mostly be empty */ }
    setUser(userObj);
    // Reset to the Dashboard for every fresh login.  Without this the previous
    // session's `route` carries over — e.g. an Admin viewing "Company Pages"
    // logs out, a Viewer logs in, and the main area keeps rendering that page
    // even though the menu no longer lists it (it's gone until a refresh).
    setRoute('dashboard');
    // Default scope: SystemAdmin and TenantAdmin start at "All" and may
    // narrow via the dropdown.  Lower roles have no switcher; their server
    // snapshot already contains only their own Company so 'all' is correct.
    setScope(ALL_SCOPE);
    setLoggedIn(true);
  };

  // On mount, ask the server who we are (cookie-bound).  If the cookie is
  // valid we skip the login screen entirely; otherwise we show it.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch('/api/auth/me');
        if (!cancelled && res.ok) {
          const u = await res.json();
          applyUser(u);
        }
      } catch (_) { /* offline / network — fall through to login */ }
      finally     { if (!cancelled) setRestoring(false); }
    })();
    return () => { cancelled = true; };
  }, []);

  // Logout: clear the cookie server-side, then drop local state.
  const handleLogout = async () => {
    try { await fetch('/api/auth/logout', { method: 'POST' }); } catch (_) {}
    window.__currentUser = null;
    setUser(null);
    setRoute('dashboard');
    setScope(ALL_SCOPE);
    setLoggedIn(false);
  };

  // ── Gates ─────────────────────────────────────────────────────────────
  // Empty render while we check the cookie — avoids a brief login-screen
  // flash for users who already have a valid session.
  if (restoring) return <div style={{padding:24, color:'var(--text-muted)', fontSize:13}}>Loading…</div>;

  if (!loggedIn || !user) {
    return <LoginPage onLogin={applyUser}/>;
  }

  // ── Topbar dropdown options ───────────────────────────────────────────
  // Build the scope-picker contents based on role:
  //   • SystemAdmin → "All" + every Tenant + every Company
  //   • TenantAdmin → "All <my tenant>" + every Company under that tenant
  //   • lower roles → no dropdown (the pill is hidden entirely)
  const allTenants   = (window.CloudGate?.DB?.Tenants)   || [];
  const allCompanies = (window.CloudGate?.DB?.Companies) || [];
  const showScopePicker = user.role === 'SystemAdmin' || user.role === 'TenantAdmin';

  let scopeOptions = null;     // null = no picker
  let scopeAllLabel = 'All';   // text shown for the kind:'all' option
  if (user.role === 'SystemAdmin') {
    scopeAllLabel = 'All';
    scopeOptions = {
      tenants:   allTenants,
      companies: allCompanies,
    };
  } else if (user.role === 'TenantAdmin') {
    const myTenant = allTenants.find(t => t.Id === user.tenantId);
    scopeAllLabel  = 'All ' + (myTenant?.TenantName ?? user.tenantName ?? 'companies');
    scopeOptions = {
      tenants:   [],   // a TenantAdmin doesn't pick another tenant — only their customers
      companies: allCompanies.filter(c => c.TenantId === user.tenantId),
    };
  }

  // Derive a human label for the currently-selected scope, used by the pill
  // when showScopePicker is false (Customer/Operator/Viewer don't see a
  // dropdown but the pill rendering code still needs *something* to show).
  const scopeLabel = (() => {
    if (activeScope.kind === 'tenant') {
      const t = allTenants.find(x => x.Id === activeScope.id);
      return t?.TenantName ?? '—';
    }
    if (activeScope.kind === 'company') {
      const c = allCompanies.find(x => x.Id === activeScope.id);
      return c?.Name ?? '—';
    }
    return scopeAllLabel;
  })();

  const routes = {
    dashboard: Dashboard,
    inventory: InventoryPage,
    'locations-admin': LocationsAdminPage,
    'readers-admin':   ReadersAdminPage,
    companies:         CompaniesPage,
    users:             UsersPage,
    'location-types':  LocationTypesPage,
    lookups:           LookupsPage,
    assets:            AssetsPage,
    'custom-fields':   CustomFieldsPage,
    'asset-types':     AssetTypesPage,
    'stocking-targets': StockingTargetsPage,
    tenants:           TenantsPage,
    'hub-clients':     HubClientsPage,
    'company-pages':   CompanyPagesPage,
  };
  const Page = routes[route] || Dashboard;

  // <Page key=…> re-mounts every list page when the scope changes, so each
  // page's `useState(initialFilteredRows)` re-evaluates against the new
  // scope without having to listen for a custom event.
  const pageKey = activeScope.kind + ':' + (activeScope.id ?? 0);

  return (
    <>
      <div className={"app" + (collapsed ? " collapsed" : "")}>
        <Sidebar route={route} setRoute={setRoute} collapsed={collapsed} user={user}/>
        <Topbar route={route}
          showScopePicker={showScopePicker}
          activeScope={activeScope}
          scopeLabel={scopeLabel}
          scopeAllLabel={scopeAllLabel}
          scopeOptions={scopeOptions}
          onSwitchScope={setScope}
          onToggle={() => setTweak('sidebarCollapsed', !collapsed)}
          onLogout={handleLogout}/>
        <main className="main"><Page key={pageKey} setRoute={setRoute}/></main>
      </div>
      <TweaksPanel title="Tweaks">
        <TweakSection title="Layout">
          <TweakToggle label="Collapsed sidebar" value={tweaks.sidebarCollapsed} onChange={v => setTweak('sidebarCollapsed', v)}/>
          <TweakRadio label="Density" value={tweaks.density} onChange={v => setTweak('density', v)} options={[
            { value:'compact', label:'Compact' },
            { value:'comfortable', label:'Comfortable' },
          ]}/>
        </TweakSection>
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
