/* global React, ReactDOM, I, S, M, SB,
          Landing, Auth, AdminApp, UserApp, VendorApp,
          ToastProvider, useToast, useHashRoute */
/* PAMP — main app */

const { useState, useEffect, useCallback } = React;

const LS_THEME    = "pamp:theme";
const LS_SIDEBAR  = "pamp:sidebar";

function readTheme() {
  const v = localStorage.getItem(LS_THEME);
  if (v === "light" || v === "dark") return v;
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}

// Default sidebar to collapsed on small screens unless the user has an
// explicit persisted preference.
function readInitialSidebar() {
  const persisted = localStorage.getItem(LS_SIDEBAR);
  if (persisted === "expanded" || persisted === "collapsed") return persisted;
  return window.innerWidth < 900 ? "collapsed" : "expanded";
}

/* ====================================================================
   ErrorBoundary
   --------------------------------------------------------------------
   Catches render errors so a crash in any single portal doesn't
   white-screen the whole app. Offers reload + sign out actions.
   ==================================================================== */
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  componentDidCatch(error, info) {
    // eslint-disable-next-line no-console
    console.error("[Pamp ErrorBoundary]", error, info);
  }
  render() {
    if (this.state.hasError) {
      return (
        <div className="app-root" style={{ alignItems: "center", justifyContent: "center", padding: 24 }}>
          <div className="card" style={{ maxWidth: 480, padding: 28, textAlign: "center" }}>
            <div style={{
              width: 56, height: 56, borderRadius: 14, margin: "0 auto 16px",
              background: "color-mix(in oklch, var(--neg) 14%, transparent)",
              color: "var(--neg)",
              display: "inline-flex", alignItems: "center", justifyContent: "center",
            }}>
              <I.alert size={26} />
            </div>
            <div className="display" style={{ fontSize: 22, letterSpacing: "-0.02em" }}>
              Something went sideways
            </div>
            <p className="muted" style={{ fontSize: 13.5, lineHeight: 1.6, marginTop: 8 }}>
              We hit an unexpected error rendering this page. Reload to try again.
              If it keeps happening, sign out and back in.
            </p>
            {this.state.error?.message && (
              <pre style={{
                marginTop: 12, padding: 10, background: "var(--surface-2)",
                borderRadius: 8, fontSize: 11, color: "var(--ink-3)",
                textAlign: "left", whiteSpace: "pre-wrap", wordBreak: "break-word",
                maxHeight: 120, overflow: "auto",
              }}>{this.state.error.message}</pre>
            )}
            <div className="row" style={{ justifyContent: "center", gap: 8, marginTop: 18 }}>
              <S.Btn variant="ghost" onClick={async () => {
                try { await SB.signOut(); } catch {}
                window.location.hash = "#/";
                window.location.reload();
              }}>
                Sign out and reload
              </S.Btn>
              <S.Btn variant="primary" icon={<I.refresh size={14} />} onClick={() => window.location.reload()}>
                Reload page
              </S.Btn>
            </div>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
}

function App() {
  const [route, setRoute] = useHashRoute({ name: "landing" });
  const [theme, setTheme] = useState(readTheme);
  const [sidebar, setSidebar] = useState(readInitialSidebar);
  const [session, setSession] = useState(null);
  const [bootstrapped, setBootstrapped] = useState(false);

  // Apply theme + persist
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    localStorage.setItem(LS_THEME, theme);
  }, [theme]);

  // Persist sidebar
  useEffect(() => { localStorage.setItem(LS_SIDEBAR, sidebar); }, [sidebar]);

  // Bootstrap session on load
  useEffect(() => {
    (async () => {
      try {
        const s = await window.PampData.bootstrapSession();
        if (s) {
          setSession(s);
          window._PAMP_ADMIN_SESSION = s;
          if (route.name === "landing" || route.name === "auth") {
            const portal = window.PampData.portalForRole(s.profile?.role);
            setRoute({ name: portal, page: "dashboard" });
          }
        }
      } finally {
        setBootstrapped(true);
      }
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Listen for auth state changes from anywhere. SIGNED_OUT bounces to
  // landing. TOKEN_REFRESHED updates our stored session token so that
  // long-lived sessions don't start failing with 401s after the
  // original token expires.
  useEffect(() => {
    const sub = SB.client.auth.onAuthStateChange((event, sess) => {
      if (event === "SIGNED_OUT") {
        window._PAMP_ADMIN_SESSION = null;
        setSession(null);
        setRoute({ name: "landing" });
      } else if (event === "TOKEN_REFRESHED" && sess?.access_token) {
        setSession(prev => prev ? { ...prev, accessToken: sess.access_token } : prev);
        if (window._PAMP_ADMIN_SESSION) {
          window._PAMP_ADMIN_SESSION.accessToken = sess.access_token;
        }
      }
    });
    return () => sub?.data?.subscription?.unsubscribe?.();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const toggleTheme = () => setTheme((t) => (t === "dark" ? "light" : "dark"));
  const toggleSidebar = () => setSidebar((s) => (s === "expanded" ? "collapsed" : "expanded"));
  const collapsed = sidebar === "collapsed";

  const handleSignedIn = useCallback(async (newSession) => {
    setSession(newSession);
    window._PAMP_ADMIN_SESSION = newSession;
    const portal = window.PampData.portalForRole(newSession.profile?.role);
    setRoute({ name: portal, page: "dashboard" });
  }, []);

  const handleExitPortal = useCallback(async () => {
    await SB.signOut();
    window._PAMP_ADMIN_SESSION = null;
    setSession(null);
    setRoute({ name: "landing" });
  }, []);

  if (!bootstrapped) {
    return (
      <div className="app-root" style={{ alignItems: "center", justifyContent: "center" }}>
        <div className="loading-state">
          <div className="spin" />
          <div>Loading Pamp…</div>
        </div>
      </div>
    );
  }

  const portalProps = {
    collapsed,
    onToggle: toggleSidebar,
    onTheme: toggleTheme,
    theme,
    onExit: handleExitPortal,
    session,
    setSession,
    onNavigate: (page) => setRoute({ name: route.name, page }),
    currentPage: route.page || "dashboard",
  };

  // ── Route guards: bounce + RETURN EARLY so we never momentarily
  // render a portal the user isn't allowed in. Previously we let the
  // wrong portal start rendering while a setTimeout queued the
  // redirect, which caused flashes of failed queries.
  const requestedPortal = route.name;
  const userRole = session?.profile?.role;
  const isPortalRoute = ["admin", "user", "vendor"].includes(requestedPortal);

  if (!session && isPortalRoute) {
    setTimeout(() => setRoute({ name: "auth", role: requestedPortal }), 0);
    return (
      <div className="app-root" style={{ alignItems: "center", justifyContent: "center" }}>
        <div className="loading-state"><div className="spin" /><div>Redirecting…</div></div>
      </div>
    );
  }

  if (session && isPortalRoute) {
    const expected = window.PampData.portalForRole(userRole);
    if (requestedPortal !== expected && userRole !== "admin") {
      setTimeout(() => setRoute({ name: expected, page: "dashboard" }), 0);
      return (
        <div className="app-root" style={{ alignItems: "center", justifyContent: "center" }}>
          <div className="loading-state"><div className="spin" /><div>Redirecting…</div></div>
        </div>
      );
    }
  }

  // Backdrop dismissal of the sidebar overlay on mobile - rendered
  // alongside the portal so a tap anywhere outside the sidebar closes it.
  const mobileBackdrop = !collapsed ? (
    <div
      className="sidebar-backdrop"
      onClick={() => setSidebar("collapsed")}
      aria-hidden="true"
    />
  ) : null;

  return (
    <div className="app-root">
      {route.name === "landing" && (
        <Landing
          onPortal={(p) => setRoute({ name: "auth", role: p })}
          onTheme={toggleTheme}
          theme={theme}
        />
      )}
      {route.name === "auth" && (
        <Auth
          role={route.role || "user"}
          onBack={() => setRoute({ name: "landing" })}
          onSignedIn={handleSignedIn}
        />
      )}
      {route.name === "admin"  && <ErrorBoundary><AdminApp  {...portalProps} />{mobileBackdrop}</ErrorBoundary>}
      {route.name === "user"   && <ErrorBoundary><UserApp   {...portalProps} />{mobileBackdrop}</ErrorBoundary>}
      {route.name === "vendor" && <ErrorBoundary><VendorApp {...portalProps} />{mobileBackdrop}</ErrorBoundary>}
    </div>
  );
}

function Root() {
  return (
    <ToastProvider>
      <App />
    </ToastProvider>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
