/* ============================================================
   VKTR TCO — Firebase Auth + RBAC (V1.7)
   ------------------------------------------------------------
   Scope (see Notion: V1.7 Roadmap — Firebase, RBAC & Handover):
   - Sign-in is OPTIONAL for using the platform. Guests (no
     sign-in) can use every TCO/depot/report feature normally.
   - Sign-in is REQUIRED only to unlock API-gated features
     (Claude needs-interpretation smart-fill; future Gmaps
     calls). This file owns that gate — it never redirects or
     blocks the main app.
   - Enforcement of API-gating is NOT this file's job. The real
     enforcement happens server-side in the Cloudflare Worker
     proxy (cf-worker/), which re-checks the ID token and the
     Firestore permission doc on every call. Anything read here
     (role, apiAccess, tokenLimit) is for UI display only — treat
     it as advisory, never as the security boundary.

   SETUP REQUIRED BEFORE THIS WORKS:
   1. Fill in window.FIREBASE_CONFIG below (Firebase Console →
      Project Settings → General → Add app → Web). This config
      is NOT a secret — Firebase's web config is safe to embed
      client-side by design.
   2. Firebase Console → Authentication → Sign-in method →
      enable Google. (Already done as of 2026-07-03.)
   3. Microsoft/Outlook sign-in additionally needs an Azure AD
      app registration (client ID + secret) wired into Firebase
      Console → Authentication → Sign-in method → Microsoft.
      Until that exists, the "Sign in with Microsoft" button
      will fail with auth/operation-not-allowed — expected, not
      a bug, until the Azure app is registered.
   ============================================================ */

// ---- Real config from Firebase Console → Project Settings → General → Web app (2026-07-06) ----
window.FIREBASE_CONFIG = {
  apiKey: "AIzaSyA3fZUbx3pIfqTiZYf51_haCsSKx1wY8dc",
  authDomain: "vktr-tco-platform.firebaseapp.com",
  projectId: "vktr-tco-platform",
  storageBucket: "vktr-tco-platform.firebasestorage.app",
  messagingSenderId: "711210420047",
  appId: "1:711210420047:web:c05e99c52046c1e40be73a",
  measurementId: "G-40DKCS1G1T",
};

// The Cloudflare Worker URL that proxies Claude/Gmaps calls. Set once the
// Worker is deployed (see cf-worker/README.md). Never call Anthropic/Google
// APIs directly from the browser — always through this proxy, which is the
// only place holding the real API keys.
window.API_PROXY_URL = "https://vktr-api-proxy.vktr-tco-platform.workers.dev"; // deployed 2026-07-07

const AuthContext = React.createContext({
  user: null,          // Firebase Auth user object, or null if signed out
  profile: null,       // Firestore users/{uid} doc: { role, apiAccess, tokenLimit, tokensUsed, resetCadence, notes }
  loading: true,
  signInGoogle: () => {},
  signInMicrosoft: () => {},
  signOutUser: () => {},
  requestAccess: () => {},
});
function useAuth() { return React.useContext(AuthContext); }

// role/apiAccess helpers — UI-display only, see file header note above
//
// v1.9.8 (2026-07-21): three clean, strictly-nested tiers per Rija's
// explicit spec, replacing an inverted/inconsistent version of this same
// idea that had real bugs (canUseGatedApi excluded "observer" instead of
// including it; the Admin Panel opened read-only for "observer" even
// though that tier was never meant to see it at all):
//   - "user"      (stored value unchanged) -- every TCO/depot/report
//     feature except the Anthropic-backed AI features.
//   - "observer"  (stored value UNCHANGED to avoid a Firestore migration
//     of already-live accounts -- only the DISPLAYED label changed, to
//     "AI-enabled User") -- everything "user" has, PLUS the AI features
//     (Live Assistant, AI-assisted needs intake).
//   - "admin"     -- everything "observer" has, PLUS the Admin Panel.
// Each tier is a strict superset of the one below it -- isAdmin() implies
// isAiEnabled() implies ordinary access. apiAccess is no longer an
// independently-toggled field an admin can accidentally leave out of sync
// with role: admin_panel.jsx's role <select> now sets both atomically,
// so this checks apiAccess too only as a belt-and-suspenders consistency
// check against the server's own quota gate (cf-worker checkQuota), not
// because apiAccess can meaningfully diverge from role anymore.
function isAdmin(profile) { return !!profile && profile.role === "admin"; }
function isAiEnabled(profile) { return !!profile && (profile.role === "observer" || profile.role === "admin"); }
// Old name kept as an alias -- other files may still reference it directly.
function isObserver(profile) { return !!profile && profile.role === "observer"; }
function canUseGatedApi(profile) {
  return !!profile && profile.apiAccess === true && isAiEnabled(profile);
}
// v1.7.5 landing-page gate: does this signed-in account get past the
// "request access" screen? Admins/AI-enabled users always do (they're
// already a privileged, admin-granted tier); everyone else needs
// accessGranted===true, set automatically for @vktr.id at first sign-in
// (firestore.rules enforces this against the verified token email, not a
// client-supplied value) or manually by an admin via the Admin Panel's
// Access Requests tab.
function hasAccess(profile) {
  return !!profile && (isAiEnabled(profile) || profile.accessGranted === true);
}

function AuthProvider({ children }) {
  const [user, setUser] = React.useState(null);
  const [profile, setProfile] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const fbReady = typeof firebase !== "undefined" && window.FIREBASE_CONFIG.apiKey !== "REPLACE_ME";

  React.useEffect(() => {
    if (!fbReady) { setLoading(false); return; }
    if (!firebase.apps.length) firebase.initializeApp(window.FIREBASE_CONFIG);
    const auth = firebase.auth();
    const db = firebase.firestore();
    // "Stay logged in" across browser restarts (v1.7.5 ask) -- this IS
    // already the Firebase JS SDK's default, but set it explicitly rather
    // than rely on an implicit default that could change.
    auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL).catch(() => {});
    let unsubProfile = null;
    const unsubAuth = auth.onAuthStateChanged((u) => {
      if (unsubProfile) { unsubProfile(); unsubProfile = null; }
      setUser(u);
      if (!u) { setProfile(null); setLoading(false); return; }
      // Live-subscribe so an admin toggling apiAccess/tokenLimit takes
      // effect in this tab immediately, without a re-login.
      unsubProfile = db.collection("users").doc(u.uid).onSnapshot(
        (snap) => {
          if (snap.exists) {
            setProfile(snap.data());
          } else {
            // First sign-in for this account: create a locked-down default
            // profile. Admin must explicitly grant apiAccess afterward.
            // accessGranted (v1.7.5 landing-page gate) auto-true only for
            // @vktr.id — firestore.rules independently re-derives this same
            // condition from the verified token email, so a client can't
            // just write accessGranted:true for an arbitrary domain.
            const fresh = {
              email: u.email,
              displayName: u.displayName || u.email,
              role: "user",
              apiAccess: false,
              accessGranted: /@vktr\.id$/i.test(u.email || ""),
              tokenLimit: 0,
              tokensUsed: 0,
              resetCadence: "monthly",
              periodStart: firebase.firestore.FieldValue.serverTimestamp(),
              notes: "",
              createdAt: firebase.firestore.FieldValue.serverTimestamp(),
            };
            // v1.9.10 (2026-07-21) audit: was fire-and-forget with a silent
            // catch -- on a transient failure the doc never got created, so
            // this same "first sign-in" branch re-ran on every future load
            // (never converging) AND the user never appeared in the Admin
            // Panel's user list for an admin to grant access to, with no
            // signal to anyone that anything was wrong. setProfile(fresh)
            // still runs unconditionally so this session isn't blocked --
            // only the persistence failure itself needs surfacing.
            db.collection("users").doc(u.uid).set(fresh).catch((e) => {
              console.error("Failed to create user profile doc:", e);
              alert(tr(VKTRStore.loadLang(),
                "Couldn't set up your account — check your connection and reload the page. If this keeps happening, contact an admin.",
                "Gagal menyiapkan akun Anda — periksa koneksi Anda dan muat ulang halaman. Jika ini terus terjadi, hubungi admin.")
                + (e && e.message ? `\n(${e.message})` : ""));
            });
            setProfile(fresh);
          }
          setLoading(false);
        },
        () => setLoading(false)
      );
    });
    return () => { unsubAuth(); if (unsubProfile) unsubProfile(); };
  }, [fbReady]);

  const signInGoogle = () => {
    if (!fbReady) { alert("Firebase belum dikonfigurasi (lihat auth.jsx / FIREBASE_CONFIG)."); return; }
    const provider = new firebase.auth.GoogleAuthProvider();
    firebase.auth().signInWithPopup(provider).catch((err) => alert("Sign-in gagal: " + err.message));
  };
  const signInMicrosoft = () => {
    if (!fbReady) { alert("Firebase belum dikonfigurasi (lihat auth.jsx / FIREBASE_CONFIG)."); return; }
    const provider = new firebase.auth.OAuthProvider("microsoft.com");
    firebase.auth().signInWithPopup(provider).catch((err) => alert("Sign-in gagal: " + err.message));
  };
  const signOutUser = () => { if (fbReady) firebase.auth().signOut(); };

  // v1.7.5: a signed-in user without accessGranted requests it -- shows up
  // in the Admin Panel's Access Requests tab for a direct in-app approve/
  // deny, no email service involved. Firestore rules only allow a user to
  // touch these two fields on their own doc, never accessGranted itself.
  //
  // v1.9.8 (2026-07-21): now returns a boolean the caller can actually
  // check. Previously this silently swallowed any write failure
  // (`.catch(() => {})`) while access_gate.jsx's button handler set its
  // local "request sent" state unconditionally regardless of the outcome
  // -- meaning a failed write still showed the requester a confident
  // "pending admin approval" message. That's a real, indistinguishable-
  // from-the-inside failure mode matching a reported bug (2026-07-21): a
  // colleague saw the expected request-access screen and apparently
  // completed it, but no request ever reached the admin. Fixed at both
  // ends -- this returns success/failure; access_gate.jsx now awaits it
  // and only claims success when the write actually landed.
  const requestAccess = async (note) => {
    if (!fbReady || !user) return false;
    try {
      await firebase.firestore().collection("users").doc(user.uid).update({
        accessRequestedAt: firebase.firestore.FieldValue.serverTimestamp(),
        accessRequestNote: note || "",
      });
      return true;
    } catch (e) {
      return false;
    }
  };

  return (
    <AuthContext.Provider value={{ user, profile, loading, signInGoogle, signInMicrosoft, signOutUser, requestAccess }}>
      {children}
    </AuthContext.Provider>
  );
}

// ---- Small header widget: sign-in state + entry point to Admin panel ----
function AuthHeaderWidget({ onOpenAdmin }) {
  const { lang } = useLang();
  const { user, profile, loading, signInGoogle, signInMicrosoft, signOutUser } = useAuth();
  const [menuOpen, setMenuOpen] = React.useState(false);

  if (loading) return null;

  if (!user) {
    return (
      <div className="auth-widget">
        <button type="button" className="io-btn" onClick={() => setMenuOpen((v) => !v)}>
          <span className="io-icon" aria-hidden="true">🔐</span>
          <span className="io-label"><Tr en="Sign in" id="Masuk" /></span>
        </button>
        {menuOpen && (
          <div className="auth-menu">
            <button type="button" onClick={signInGoogle}>🟢 <Tr en="Sign in with Google" id="Masuk dengan Google" /></button>
            <button type="button" onClick={signInMicrosoft}>🟦 <Tr en="Sign in with Microsoft" id="Masuk dengan Microsoft" /></button>
            <div className="auth-menu-note">
              <Tr en="Live route search/routing just needs sign-in. AI-powered features (smart-fill, Live Assistant) additionally need admin approval. Everything else works without signing in."
                  id="Pencarian/perutean rute langsung hanya perlu masuk. Fitur berbasis AI (isi otomatis, Asisten Langsung) tambahan perlu persetujuan admin. Fitur lainnya tetap berjalan tanpa masuk." />
            </div>
          </div>
        )}
      </div>
    );
  }

  const roleLabel = isAdmin(profile) ? "Admin" : isAiEnabled(profile) ? tr(lang, "AI-enabled User", "Pengguna AI") : tr(lang, "User", "Pengguna");
  return (
    <div className="auth-widget">
      <button type="button" className="io-btn" onClick={() => setMenuOpen((v) => !v)}>
        <span className="io-icon" aria-hidden="true">👤</span>
        <span className="io-label">{user.displayName || user.email}</span>
      </button>
      {menuOpen && (
        <div className="auth-menu">
          <div className="auth-menu-you">{user.email} · <Badge kind={isAdmin(profile) ? "warn" : "info"}>{roleLabel}</Badge></div>
          <div className="auth-menu-note">
            {canUseGatedApi(profile)
              ? <Tr en="AI features: enabled" id="Fitur AI: aktif" />
              : <Tr en="AI features: not enabled for this account" id="Fitur AI: belum aktif untuk akun ini" />}
          </div>
          {isAdmin(profile) && (
            <button type="button" onClick={() => { setMenuOpen(false); onOpenAdmin(); }}>
              🛡 <Tr en="Admin panel" id="Panel Admin" />
            </button>
          )}
          <button type="button" onClick={signOutUser}>↪ <Tr en="Sign out" id="Keluar" /></button>
        </div>
      )}
    </div>
  );
}

window.AuthProvider = AuthProvider;
window.useAuth = useAuth;
window.AuthHeaderWidget = AuthHeaderWidget;
window.isAdmin = isAdmin;
window.isAiEnabled = isAiEnabled;
window.isObserver = isObserver;
window.canUseGatedApi = canUseGatedApi;
window.hasAccess = hasAccess;
