// Main app — shell, nav, persona switching, page routing

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "persona": "fbp",
  "scopeFilter": "All in scope",
  "density": "comfortable",
  "showAuditTrail": true
}/*EDITMODE-END*/;

// Nav groups
const NAV_GROUPS = [
  {
    label: "Rates",
    items: [
      { id: "rates", label: "Rate Repository", icon: "table" },
      { id: "health", label: "Rate Health", icon: "shield" },
      { id: "roe",   label: "ROE Table", icon: "globe" },
      { id: "calc",  label: "Rate Calculator", icon: "calc" },
      { id: "ai",    label: "AI Assistant", icon: "sparkle" },
      { id: "export",label: "Export", icon: "export" },
    ],
  },
  {
    label: "Contracts & Finance",
    items: [
      { id: "rebate", label: "Rebate Monitor", icon: "key" },
      { id: "volume", label: "Volume Tracker", icon: "table" },
      { id: "tco",    label: "TCO Analysis", icon: "bolt" },
    ],
  },
  {
    label: "Access",
    items: [
      { id: "access", label: "Access Requests", icon: "key" },
    ],
  },
  {
    label: "Administration",
    items: [
      { id: "admin", label: "Admin Console", icon: "settings", adminOnly: true },
    ],
  },
  {
    label: "Resources",
    items: [
      { id: "docs", label: "Documentation", icon: "book" },
    ],
  },
];

function canSee(page, perms) {
  if (perms.accessOnly) return page === "access" || page === "docs";
  switch (page) {
    case "rates":  return perms.viewRates;
    case "health": return perms.rateHealth;
    case "roe":    return perms.roeTable;
    case "calc":   return true;
    case "ai":     return perms.aiAssistant;
    case "export": return perms.export;
    case "rebate": return perms.rebateMonitor;
    case "volume": return perms.volumeTracker;
    case "tco":    return perms.tcoAnalysis;
    case "access": return true;
    case "admin":  return perms.admin;
    case "docs":   return true;
  }
  return true;
}

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Use real SSO user when available; fall back to persona simulator for local dev
  const [ssoUser, setSsoUser] = useState(window.OCMS_USER || null);
  const [permsMap, setPermsMap] = useState(window.OCMS_PERMS || PERMS);
  const [tweaksPanelOpen, setTweaksPanelOpen] = useState(false);

  useEffect(() => {
    const handler = (e) => {
      setSsoUser(e.detail);
      if (e.detail?.role && PERSONAS[e.detail.role]) {
        setTweak("persona", e.detail.role);
      }
    };
    window.addEventListener('ocms:user-ready', handler);
    return () => window.removeEventListener('ocms:user-ready', handler);
  }, []);

  useEffect(() => {
    const handler = (e) => setPermsMap(e.detail);
    window.addEventListener('ocms:perms-ready', handler);
    return () => window.removeEventListener('ocms:perms-ready', handler);
  }, []);

  useEffect(() => {
    const handler = (e) => { setPage(e.detail); setShowGuide(false); };
    window.addEventListener('tcs:navigate', handler);
    return () => window.removeEventListener('tcs:navigate', handler);
  }, []);

  useEffect(() => {
    const onMsg = (e) => {
      if (e.data?.type === '__edit_mode_dismissed') {
        setTweaksPanelOpen(false);
        if (ssoUser?.role && PERSONAS[ssoUser.role]) {
          setTweak("persona", ssoUser.role);
        }
        // Refresh permissions from API in case admin changed them
        if (window.OCMS_PERMS) setPermsMap({ ...window.OCMS_PERMS });
      }
    };
    window.addEventListener('message', onMsg);
    return () => window.removeEventListener('message', onMsg);
  }, [ssoUser]);

  const _devEnv  = ['localhost', '127.0.0.1', 'ocms-cdt.maersk.io'].includes(window.location.hostname);
  const _isAdmin = ssoUser
    ? (ssoUser.role === 'admin' || (window.location.hostname === 'localhost' && ssoUser.roles.length === 0))
    : true;
  const showTweaks = _devEnv && _isAdmin;

  // Persona switcher only overrides when TweaksPanel is actually open.
  // While SSO auth is still in progress (ssoUser=null), use PERSONAS.unknown so no data
  // screens render and no API calls are made before the token is ready.
  // In dev, the tweaks panel can override to a simulator persona at any time.
  const user  = (showTweaks && tweaksPanelOpen) ? (PERSONAS[tweaks.persona] || PERSONAS.fbp) : (ssoUser || PERSONAS.unknown);
  const perms = user.role === 'unknown' ? PERMS.unknown : (permsMap[user.role] || permsMap.read || PERMS.read);

  function getDefaultPage(perms, role) {
    if (perms.accessOnly) return "access";
    if (role === "admin") return "admin";
    if (role === "rate_calculator") return "calc";
    return "rates";
  }
  const defaultPage = getDefaultPage(perms, user.role);
  const [page, setPage] = useState(defaultPage);
  const [visited, setVisited] = useState(() => new Set([defaultPage]));
  useEffect(() => { setVisited(v => { v.add(page); return new Set(v); }); }, [page]);
  useEffect(() => {
    if (!canSee(page, perms)) setPage(getDefaultPage(perms, user.role));
  }, [tweaks.persona]);

  // When auth completes, redirect to the role-appropriate default page.
  // useState(defaultPage) only runs at mount when ssoUser=null (role='unknown'),
  // so page would be stuck on "access" without this correction.
  useEffect(() => {
    if (ssoUser) {
      setPage(getDefaultPage(perms, ssoUser.role));
      startMetaRefreshTimer();   // prime metadata cache + start 20-min background refresh
    }
  }, [ssoUser?.role]);

  const [healthIssueCount, setHealthIssueCount] = useState(null);
  // Fetch validation issue count eagerly on login so the sidebar badge shows without visiting the health page
  useEffect(() => {
    if (!ssoUser || !perms.rateHealth) return;
    apiFetch("/api/health/validation")
      .then(v => setHealthIssueCount((v?.findings || []).length))
      .catch(() => {});
  }, [ssoUser?.role]);
  const [showWelcome, setShowWelcome] = useState(true);
  const dismissWelcome = () => setShowWelcome(false);
  const [showGuide, setShowGuide] = useState(false);

  const [openRate, setOpenRate] = useState(null);
  const [showAdd, setShowAdd] = useState(false);
  const [showAddPrefill, setShowAddPrefill] = useState(null);
  const [showImport, setShowImport] = useState(false);
  const [showAI, setShowAI] = useState(false);
  const [toast, setToast] = useState("");
  const [rateRefreshKey, setRateRefreshKey] = useState(0);
  const [healthRefreshKey, setHealthRefreshKey] = useState(0);
  const [rateNavCount, setRateNavCount] = useState(null);
  const [accessPendingCount, setAccessPendingCount] = useState(0);
  const [accessCountKey, setAccessCountKey] = useState(0);

  useEffect(() => {
    if (!ssoUser || !perms.viewRates) return;
    apiFetch("/api/rates/count").then(r => {
      if (r?.total != null) setRateNavCount(r.total);
    }).catch(() => {});
  }, [ssoUser, perms.viewRates, rateRefreshKey]);

  // Fetch pending access request count — returns 0 for non-approvers without 403
  useEffect(() => {
    if (!ssoUser) return;
    apiFetch("/api/access-requests/pending-count")
      .then(r => setAccessPendingCount(r?.count ?? 0))
      .catch(() => setAccessPendingCount(0));
  }, [ssoUser, accessCountKey]);

  const fmtCount = n => n == null ? "" : n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, "") + "k" : String(n);

  const allNavItems = NAV_GROUPS.flatMap(g => g.items);
  const pageMeta = allNavItems.find(p => p.id === page);
  const groupMeta = NAV_GROUPS.find(g => g.items.some(i => i.id === page));

  return (
    <div className="app" data-screen-label="TCS">
      {/* SIDEBAR */}
      <aside className="sidebar" data-screen-label="Sidebar">
        <div className="brand">
          <div className="brand-mark">
            <img src="favicon.ico" alt="TCS" width="20" height="20" style={{objectFit:"contain"}} />
          </div>
          <div className="brand-name">
            <b>TCS</b>
            <span>Ocean Procurement</span>
          </div>
        </div>
        <div className="nav">
          {NAV_GROUPS.map(g => {
            const visibleItems = g.items.filter(it => canSee(it.id, perms) || it.adminOnly);
            if (visibleItems.length === 0) return null;
            // hide admin group for non-admin
            if (g.label === "Administration" && !perms.admin) return null;
            return (
              <React.Fragment key={g.label}>
                <div className="nav-label">{g.label}</div>
                {visibleItems.map(p => {
                  const ok = canSee(p.id, perms);
                  return (
                    <button key={p.id} className={`nav-item ${page === p.id ? "active" : ""} ${!ok ? "disabled" : ""}`}
                      onClick={() => ok && setPage(p.id)}>
                      <Icon name={p.icon} size={15} className="nav-icon" />
                      {p.label}
                      {p.id === "rates" && perms.viewRates && rateNavCount != null && <span className="nav-count">{fmtCount(rateNavCount)}</span>}
                      {p.id === "ai" && perms.aiAssistant && <span className="nav-count" style={{ background: "#14a3bd", color: "#061425" }}>AI</span>}
                      {p.id === "health" && perms.rateHealth && healthIssueCount > 0 && <span className="nav-count" style={{ background: "#b86a1b", color: "#fff" }}>{healthIssueCount}</span>}
                      {p.id === "rebate" && perms.rebateMonitor && <span className="nav-count">{REBATES.length}</span>}
                      {p.id === "access" && accessPendingCount > 0 && <span className="nav-count" style={{ background: "var(--amber)", color: "#fff" }}>{accessPendingCount}</span>}
                      {p.id === "docs" && <span className="nav-count" style={{ background: "#14a3bd", color: "#061425" }}>v1.1</span>}
                    </button>
                  );
                })}
              </React.Fragment>
            );
          })}
        </div>
        <div className="sidebar-foot" style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          {showTweaks && (
            <div style={{ display: "flex", justifyContent: "flex-end" }}>
              <button title="Tweaks panel" onClick={() => { setTweaksPanelOpen(true); window.postMessage({ type: '__activate_edit_mode' }, '*'); }}
                style={{ background: "transparent", border: "1px solid #2a4a6c", borderRadius: 5, color: "#6a85a3", padding: "4px 7px", fontSize: 13, cursor: "pointer", lineHeight: 1 }}>
                ⚙
              </button>
            </div>
          )}
          <span style={{ fontSize: 9, color: "#546a82", lineHeight: 1.4 }}>© 2026 APA Ocean Finance. All rights reserved.</span>
        </div>
      </aside>

      {/* TOP BAR */}
      <header className="topbar">
        <div className="crumb">
          <span className="ghost">TCS</span>
          <span className="sep">/</span>
          {groupMeta && <><span className="ghost">{groupMeta.label}</span><span className="sep">/</span></>}
          <span className="here">{pageMeta?.label || "—"}</span>
        </div>
        <div className="top-right">
          <div className="scope" style={{ position: "relative" }} onMouseEnter={e => { const t = e.currentTarget.querySelector(".scope-tip"); if (t) t.style.display = "block"; }} onMouseLeave={e => { const t = e.currentTarget.querySelector(".scope-tip"); if (t) t.style.display = "none"; }}>
            <Icon name="pin" size={11} />
            Scope:
            <b>{user.regions.length > 1 ? `${user.regions.length} regions` : (user.regions[0] || "—")}</b>
            {(user.regions.length > 0 && user.regions[0] !== "—") || user.areas?.length > 0 ? (
              <div className="scope-tip" style={{ display: "none", position: "absolute", top: "calc(100% + 6px)", right: 0, zIndex: 500, background: "var(--ink)", color: "#fff", borderRadius: 8, padding: "10px 14px", minWidth: 200, maxWidth: 320, boxShadow: "0 4px 16px rgba(0,0,0,.25)", fontSize: 12, lineHeight: 1.6, whiteSpace: "normal" }}>
                <div style={{ fontWeight: 600, marginBottom: 6, color: "var(--teal-light, #4dd9c0)" }}>Regions</div>
                {user.regions.map(r => <div key={r} style={{ marginBottom: 2 }}>· {r}</div>)}
                {user.areas && user.areas.length > 0 && (
                  <>
                    <div style={{ fontWeight: 600, marginTop: 8, marginBottom: 6, color: "var(--teal-light, #4dd9c0)" }}>Areas</div>
                    {user.areas.map(a => <div key={a} style={{ marginBottom: 2 }}>· {a}</div>)}
                  </>
                )}
              </div>
            ) : null}
          </div>
          <button className="icon-btn" title="Page guide" onClick={() => setShowGuide(g => !g)}
            style={{ position: "relative", background: showGuide ? "var(--teal-soft, #e6f7fa)" : undefined, borderColor: showGuide ? "var(--teal)" : undefined }}>
            <Icon name="help" size={16} stroke={showGuide ? "var(--teal)" : "var(--text-2, #6b7280)"} />
          </button>
          {perms.aiAssistant && (
            <button className="icon-btn" onClick={() => setShowAI(true)} title="AI Assistant">
              <Icon name="sparkle" size={14} stroke="var(--teal)" />
            </button>
          )}
          <button className="icon-btn" title="Notifications">
            <Icon name="bell" size={14} />
            <span style={{ position: "absolute", top: 6, right: 6, width: 6, height: 6, background: "var(--amber)", borderRadius: "50%", boxShadow: "0 0 0 2px #fff" }} />
          </button>
          <div className="user-chip">
            <Avatar user={ssoUser || user} size={26} />
            <div>
              <div className="name">{ssoUser ? ssoUser.name : user.name}</div>
              <div className="role">{user.role}</div>
            </div>
          </div>
        </div>
      </header>

      {/* MAIN */}
      <main className="main">
        {/* Stateful pages: lazy-mount on first visit, then CSS hide/show to preserve state */}
        {visited.has("rates") && perms.viewRates && (
          <div style={{ display: page === "rates" ? "" : "none" }}>
            <RateList user={user} perms={perms}
              onOpenRate={setOpenRate}
              onAdd={() => setShowAdd(true)}
              onAddPrefill={(prefill) => { setShowAddPrefill(prefill); setShowAdd(true); }}
              onImport={() => setShowImport(true)}
              onOpenAI={() => setShowAI(true)}
              onToast={setToast}
              refreshKey={rateRefreshKey} />
          </div>
        )}
        {page === "rates" && !perms.viewRates && <NoAccess role={user.role} feature="Rate Repository" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("health") && perms.rateHealth && (
          <div style={{ display: page === "health" ? "" : "none" }}>
            <RateHealth user={user} perms={perms}
              onAddRate={(prefill) => { setShowAddPrefill(prefill); setShowAdd(true); }}
              onOpenRate={setOpenRate}
              refreshKey={healthRefreshKey}
              onValidationData={v => setHealthIssueCount((v?.findings || []).length)} />
          </div>
        )}
        {page === "health" && !perms.rateHealth && <NoAccess role={user.role} feature="Rate Health" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("roe") && perms.roeTable && (
          <div style={{ display: page === "roe" ? "" : "none" }}>
            <ROETable perms={perms} />
          </div>
        )}
        {page === "roe" && !perms.roeTable && <NoAccess role={user.role} feature="ROE Table" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("calc") && (
          <div style={{ display: page === "calc" ? "" : "none" }}>
            <Calculator user={user} perms={perms} />
          </div>
        )}

        {visited.has("ai") && perms.aiAssistant && (
          <div style={{ display: page === "ai" ? "" : "none" }}>
            <AIAssistant user={user} perms={perms} />
          </div>
        )}
        {page === "ai" && !perms.aiAssistant && <NoAccess role={user.role} feature="AI Assistant" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("export") && perms.export && (
          <div style={{ display: page === "export" ? "" : "none" }}>
            <ExportPage user={user} />
          </div>
        )}
        {page === "export" && !perms.export && <NoAccess role={user.role} feature="Export" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("rebate") && canSee("rebate", perms) && (
          <div style={{ display: page === "rebate" ? "" : "none" }}>
            <RebateMonitor user={user} perms={perms} />
          </div>
        )}
        {page === "rebate" && !canSee("rebate", perms) && <NoAccess role={user.role} feature="Rebate Monitor" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("volume") && canSee("volume", perms) && (
          <div style={{ display: page === "volume" ? "" : "none" }}>
            <VolumeTracker user={user} perms={perms} />
          </div>
        )}
        {page === "volume" && !canSee("volume", perms) && <NoAccess role={user.role} feature="Volume Tracker" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("tco") && canSee("tco", perms) && (
          <div style={{ display: page === "tco" ? "" : "none" }}>
            <TCOAnalysis user={user} perms={perms} />
          </div>
        )}
        {page === "tco" && !canSee("tco", perms) && <NoAccess role={user.role} feature="TCO Analysis" canDo="Rate Calculator" onGoCalc={() => setPage("calc")} />}

        {visited.has("access") && (
          <div style={{ display: page === "access" ? "" : "none" }}>
            <AccessRequests user={user} perms={perms} onApprovalAction={() => setAccessCountKey(k => k + 1)} />
          </div>
        )}
        {visited.has("admin") && perms.admin && (
          <div style={{ display: page === "admin" ? "" : "none" }}>
            <Admin user={user} />
          </div>
        )}
        {visited.has("docs") && (
          <div style={{ display: page === "docs" ? "" : "none" }}>
            <Documentation user={user} perms={perms} />
          </div>
        )}
      </main>

      {openRate && <RateDetail rate={openRate} onClose={() => setOpenRate(null)} perms={perms}
        onEdit={rate => { setOpenRate(null); setShowAddPrefill(rate); setShowAdd(true); }}
        onDuplicate={rate => { setOpenRate(null); setShowAddPrefill({ ...rate, id: undefined, status: "Draft", created_at: undefined, modified_at: undefined }); setShowAdd(true); }} />}
      {showAdd && <AddRateForm prefill={showAddPrefill} onClose={() => { setShowAdd(false); setShowAddPrefill(null); }} onSave={msg => { setShowAdd(false); setShowAddPrefill(null); setToast(msg); setRateRefreshKey(k => k + 1); setHealthRefreshKey(k => k + 1); }} />}
      {showImport && <ImportWizard onClose={() => setShowImport(false)} onDone={msg => { setShowImport(false); setToast(msg); setRateRefreshKey(k => k + 1); }} />}
      {showAI && <AIAssistant user={user} perms={perms} drawerMode onClose={() => setShowAI(false)} />}
      <Toast msg={toast} onClose={() => setToast("")} />

      {showWelcome && <WelcomeOverlay onDismiss={dismissWelcome} />}
      {showGuide && <TourOverlay page={page} role={user.role} onClose={() => setShowGuide(false)} />}

      {/* TWEAKS PANEL — local/dev env + admin role only */}
      {showTweaks && <TweaksPanel title="Tweaks · Persona Simulator">
        <TweakSection title="Identity">
          <TweakRadio
            label="Active role"
            value={tweaks.persona}
            onChange={v => setTweak("persona", v)}
            options={[
              { value: "rate_calculator", label: "Calc only" },
              { value: "read", label: "Read" },
              { value: "edit", label: "Rate Focal (edit)" },
              { value: "fbp", label: "FBP" },
              { value: "procurement", label: "Procurement" },
              { value: "admin", label: "Admin" },
            ]}
          />
          <div className="tiny muted" style={{ marginTop: 6, lineHeight: 1.45 }}>
            <b>rate_calculator</b> · Calculator only · rates hidden<br/>
            <b>read</b> · browse rates + Health Dashboard<br/>
            <b>edit</b> · Rate Focal — create / import rates + Volume upload<br/>
            <b>fbp</b> · adds AI, Export, Rebate, TCO<br/>
            <b>procurement</b> · adds contract upload + Rebate settlement<br/>
            <b>admin</b> · full system + Admin console
          </div>
        </TweakSection>
        <TweakSection title="Preview">
          <TweakToggle label="Show audit trail badges" value={tweaks.showAuditTrail} onChange={v => setTweak("showAuditTrail", v)} />
        </TweakSection>
      </TweaksPanel>}
    </div>
  );
}


function NoAccess({ role, feature, canDo, onGoCalc }) {
  return (
    <div style={{ display: "grid", placeItems: "center", padding: "80px 20px" }}>
      <div className="card" style={{ padding: 32, maxWidth: 480, textAlign: "center" }}>
        <div style={{ width: 48, height: 48, borderRadius: 8, background: "var(--paper-2)", color: "var(--ink-mute)", display: "grid", placeItems: "center", margin: "0 auto 14px" }}>
          <Icon name="eyeOff" size={22} />
        </div>
        <div className="serif" style={{ fontSize: 20, fontWeight: 500, marginBottom: 8 }}>{feature} is not available for your role</div>
        <div className="muted" style={{ marginBottom: 20 }}>
          Your role <span className="mono bold" style={{ color: "var(--ink)" }}>{role}</span> does not include access to <b>{feature}</b>.
          You can use the <b>{canDo}</b> for cost estimation, or submit a new access request.
        </div>
        <div className="row gap-8" style={{ justifyContent: "center" }}>
          <button className="btn" onClick={onGoCalc}><Icon name="calc" size={14} /> Open Calculator</button>
          <button className="btn primary"><Icon name="key" size={14} /> Request Access</button>
        </div>
      </div>
    </div>
  );
}

// ─── Tour Guide Overlay ───────────────────────────────────────────────────────

const TOUR_STEPS = {
  rates: {
    admin: [
      { selector: ".filterbar",               title: "Filter Rates",   body: "Narrow by terminal, cost type, equipment, status, or date range. Filters stack — use multiple at once.", position: "bottom" },
      { selector: ".grid-4",                  title: "KPI Summary",    body: "Active rates, total loaded, drafts, and expiring rates at a glance across your current scope.", position: "bottom" },
      { selector: ".page-header .btn.primary",title: "Add Rate",       body: "Create a single rate via the form — set terminal, cost type, effective date, and local rate.", position: "bottom" },
      { selector: ".table-wrap",              title: "Rate Table",     body: "Click any row to open the full rate drawer with slab tiers, change history, and edit options.", position: "top" },
    ],
    edit: [
      { selector: ".page-header .btn.primary",title: "Add Rate",       body: "Create a new rate — fill in terminal, cost type, effective date, and rate value, then save or save as draft.", position: "bottom" },
      { selector: ".filterbar",               title: "Filter & Find",  body: "Filter by your terminals, cost type, or status to locate existing rates quickly before editing.", position: "bottom" },
      { selector: ".table-wrap",              title: "Batch Edit",     body: "Tick multiple rows with the checkbox, then click Edit Selected in the toolbar to update shared fields in one action.", position: "top" },
    ],
    _default: [
      { selector: ".filterbar",               title: "Filter Rates",   body: "Use filters to narrow rates by terminal, cost type, status, or date range.", position: "bottom" },
      { selector: ".table-wrap",              title: "Rate Table",     body: "Click any row to view full rate details and audit history. Your role allows viewing and exporting only.", position: "top" },
    ],
  },
  health: {
    admin: [
      { selector: ".grid-4",                  title: "Health KPIs",        body: "Overall completeness %, rates expiring in 60 days, and active QV errors — refreshed on each load.", position: "bottom" },
      { selector: ".tabs",                    title: "Three Tabs",         body: "Switch between Completeness, Expiry Alerts, and Quality Validation using the tab bar.", position: "bottom" },
      { selector: ".page-header .btn",        title: "Run Validation",     body: "Click Run Validation to scan all Active rates for QV rule violations — results appear in the Validation tab.", position: "bottom" },
      { selector: ".table-wrap",              title: "QV Findings",        body: "Click any rate ID in the validation table to jump directly to that rate in Rate Repository and fix the issue.", position: "top" },
    ],
    _default: [
      { selector: ".grid-4",                  title: "Health KPIs",        body: "See completeness %, expiry alert count, and active QV error count across your scope.", position: "bottom" },
      { selector: ".tabs",                    title: "Three Tabs",         body: "Switch between Completeness, Expiry Alerts, and Quality Validation to see detailed breakdowns.", position: "bottom" },
      { selector: ".table-wrap",              title: "Completeness Table", body: "Review which rate combinations are Present, Missing, or Expiring across your terminals.", position: "top" },
    ],
  },
  roe: {
    admin: [
      { selector: ".page-header .btn.primary",title: "Add Exchange Rate",  body: "Create a ROE entry with currency, rate value, and effective/expiry date. Covers a date range.", position: "bottom" },
      { selector: ".table-wrap",              title: "ROE Table",          body: "Edit or expire entries here. Adding a missing ROE resolves QV-07 warnings in Rate Health.", position: "top" },
    ],
    _default: [
      { selector: ".table-wrap",              title: "ROE Table",          body: "Browse active exchange rates per currency. Used by the Rate Calculator to convert local currency to USD.", position: "top" },
    ],
  },
  calc: {
    _default: [
      { selector: ".card",                    title: "Configure Scenario", body: "Select terminal, cost type, equipment type, and container size to set up your cost scenario.", position: "right" },
      { selector: ".btn.teal",                title: "Load Rates",         body: "Click to fetch Active rates for the selected terminal and cost type, then enter quantities to calculate.", position: "top" },
    ],
  },
  access: {
    admin: [
      { selector: ".filterbar",              title: "Filter by Status",   body: "Switch between Pending, Approved, and Rejected to manage requests. Pending items need your action.", position: "bottom" },
      { selector: ".card .table-wrap",       title: "Review Requests",    body: "Click a row to see the requester's role, region, and justification, then Approve or Reject.", position: "top" },
    ],
    _default: [
      { selector: ".page-header .btn.primary",title: "New Request",       body: "Submit a request for a role and region. Provide a clear business reason to help the approver decide quickly.", position: "bottom" },
      { selector: ".card .table-wrap",       title: "Track Status",       body: "Your submitted requests appear here with their current status: Pending, Approved, or Rejected.", position: "top" },
    ],
  },
  admin: {
    admin: [
      { selector: ".page-header",            title: "Admin Console",      body: "Select a section from the left sidebar: Users, Permissions, Metadata, Coverage Matrix, Approval Routing, or Audit Log.", position: "bottom" },
      { selector: ".table-wrap",             title: "Manage Data",        body: "View and edit reference data, user roles, and system configuration in the table below.", position: "top" },
    ],
    _default: [
      { selector: null,                      title: "Admin Console",      body: "This section is restricted to Admin role users. Contact your system administrator to request elevated access.", position: null },
    ],
  },
  docs: {
    _default: [
      { selector: ".filterbar, .search",     title: "Search Articles",    body: "Type any keyword to find articles, tags, or content across the TCS Knowledge Base.", position: "bottom" },
      { selector: ".card",                   title: "Browse by Category", body: "Use the left panel to navigate by section: Modules, Workflows, FAQ, and Release Notes.", position: "right" },
    ],
  },
  _default: {
    _default: [
      { selector: null,                       title: "Welcome to TCS", body: "Use the left menu to navigate between Rate Repository, Rate Health, ROE Table, Rate Calculator, and more.", position: null },
      { selector: null,                       title: "Page Guide",     body: "Click the ? button on any page to open this guided tour — it adapts to the current page and your role.", position: null },
    ],
  },
};

function TourOverlay({ page, role, onClose }) {
  const pageDef = TOUR_STEPS[page] || TOUR_STEPS._default;
  const steps = pageDef[role] || pageDef._default || TOUR_STEPS._default._default;

  const [idx, setIdx] = React.useState(0);
  const [rect, setRect] = React.useState(null);
  const step = steps[idx];
  const PAD = 10;
  const isLast = idx === steps.length - 1;
  const W = 320;

  const measure = React.useCallback(() => {
    if (!step?.selector) { setRect(null); return; }
    const el = document.querySelector(step.selector);
    if (!el) { setRect(null); return; }
    setRect(el.getBoundingClientRect());
  }, [idx, step]);

  React.useEffect(() => {
    setRect(null);
    if (!step?.selector) return;
    const el = document.querySelector(step.selector);
    if (!el) return;
    el.scrollIntoView({ behavior: "smooth", block: "nearest" });
    const t = setTimeout(measure, 340);
    return () => clearTimeout(t);
  }, [idx]);

  React.useEffect(() => {
    window.addEventListener("resize", measure);
    return () => window.removeEventListener("resize", measure);
  }, [measure]);

  const spot = (rect && rect.width > 0 && rect.height > 0) ? {
    top: rect.top - PAD, left: rect.left - PAD,
    width: rect.width + PAD * 2, height: rect.height + PAD * 2,
  } : null;

  const tipStyle = () => {
    const m = 16;
    if (!spot) return { position: "fixed", top: "50%", left: "50%", transform: "translate(-50%,-50%)", zIndex: 10000, width: W };
    const pos = step.position || "bottom";
    let s = { position: "fixed", zIndex: 10000, width: W };
    const cx = spot.left + spot.width / 2;
    if (pos === "bottom") {
      s.top = spot.top + spot.height + m;
      s.left = Math.max(m, Math.min(cx - W / 2, window.innerWidth - W - m));
    } else if (pos === "top") {
      s.bottom = window.innerHeight - spot.top + m;
      s.left = Math.max(m, Math.min(cx - W / 2, window.innerWidth - W - m));
    } else if (pos === "right") {
      s.top = Math.max(m, spot.top);
      s.left = Math.min(spot.left + spot.width + m, window.innerWidth - W - m);
    } else {
      s.top = Math.max(m, spot.top);
      s.right = window.innerWidth - spot.left + m;
    }
    return s;
  };

  return (
    <>
      <div onClick={onClose} style={{ position: "fixed", inset: 0, zIndex: 9997, background: spot ? "transparent" : "rgba(0,0,0,.55)", cursor: "default" }} />
      {spot && (
        <div style={{
          position: "fixed", top: spot.top, left: spot.left, width: spot.width, height: spot.height,
          borderRadius: 8, boxShadow: "0 0 0 9999px rgba(0,0,0,.55)",
          border: "2px solid var(--teal,#00a3ad)", zIndex: 9998, pointerEvents: "none",
          transition: "top .28s ease, left .28s ease, width .28s ease, height .28s ease",
        }} />
      )}
      <div style={{
        ...tipStyle(), background: "#fff", borderRadius: 12,
        boxShadow: "0 16px 48px rgba(0,0,0,.22)", padding: "18px 18px 14px",
        display: "flex", flexDirection: "column", gap: 12,
        animation: "tourIn .18s ease",
      }}>
        <style>{`@keyframes tourIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}`}</style>
        <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
          {steps.map((_, i) => (
            <div key={i} style={{
              width: i === idx ? 18 : 6, height: 6, borderRadius: 3,
              background: i === idx ? "var(--teal,#00a3ad)" : "var(--line,#e5e7eb)",
              transition: "width .2s",
            }} />
          ))}
          <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--ink-mute,#9ca3af)" }}>{idx + 1} / {steps.length}</span>
        </div>
        <div>
          <div style={{ fontWeight: 700, fontSize: 14, color: "var(--ink)", marginBottom: 5 }}>{step.title}</div>
          <div style={{ fontSize: 12.5, lineHeight: 1.65, color: "var(--ink-mute,#6b7280)" }}>{step.body}</div>
        </div>
        <div style={{ display: "flex", alignItems: "center", paddingTop: 2 }}>
          <button className="btn ghost" style={{ fontSize: 12, padding: "4px 10px", color: "var(--ink-mute)" }} onClick={onClose}>Skip</button>
          <div style={{ marginLeft: "auto", display: "flex", gap: 8 }}>
            {idx > 0 && <button className="btn ghost" style={{ fontSize: 12, padding: "4px 10px" }} onClick={() => setIdx(i => i - 1)}>← Back</button>}
            <button className="btn primary" style={{ fontSize: 12, padding: "4px 14px" }}
              onClick={() => isLast ? onClose() : setIdx(i => i + 1)}>
              {isLast ? "Done ✓" : "Next →"}
            </button>
          </div>
        </div>
      </div>
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
