// Client-side auth helpers + Login / SignUp screens.
//
// Provider-aware. /api/auth-config decides at runtime which auth is live:
//   - "clerk":  managed auth (clerk-js loaded from CDN, no build step). Prebuilt
//               sign-in / sign-up UI with email verification + Google/Microsoft SSO.
//   - "custom": the legacy email/password screens backed by /api/auth.
// window.Auth keeps the same shape for the rest of the app either way.

// ---------------------------------------------------------------------------
// Boot config (cached)
// ---------------------------------------------------------------------------
let _authConfigPromise = null;
function getAuthConfig() {
  if (!_authConfigPromise) {
    _authConfigPromise = fetch("/api/auth-config", { credentials: "same-origin" })
      .then((r) => r.json())
      .catch(() => ({ provider: "custom", clerkPublishableKey: null }));
  }
  return _authConfigPromise;
}

// ---------------------------------------------------------------------------
// Clerk loader (no build step — clerk-js from the instance's Frontend API CDN)
// ---------------------------------------------------------------------------
let _clerkPromise = null;

// The Frontend API host is base64-encoded inside the publishable key:
//   pk_test_<base64("clerk.your-app.lcl.dev$")>
function frontendApiHost(pk) {
  try {
    const decoded = atob(pk.split("_")[2] || "");
    return decoded.replace(/\$+$/, "") || null;
  } catch {
    return null;
  }
}

function loadScript(src, attrs = {}) {
  return new Promise((resolve, reject) => {
    const s = document.createElement("script");
    s.src = src;
    s.async = true;
    s.crossOrigin = "anonymous";
    for (const [k, v] of Object.entries(attrs)) s.setAttribute(k, v);
    s.onload = () => resolve();
    s.onerror = () => reject(new Error("Failed to load " + src));
    document.head.appendChild(s);
  });
}

function ensureClerk() {
  if (!_clerkPromise) {
    _clerkPromise = (async () => {
      const cfg = await getAuthConfig();
      if (cfg.provider !== "clerk" || !cfg.clerkPublishableKey) {
        throw new Error("Clerk is not configured.");
      }
      const host = frontendApiHost(cfg.clerkPublishableKey);
      if (!host) throw new Error("Invalid Clerk publishable key.");
      // Load the ESM build (clerk.mjs): it exports the Clerk class and resolves its
      // lazy UI chunks via import.meta.url — unlike clerk.browser.js, which derives
      // the chunk base from document.currentScript (null for async-injected scripts),
      // so mountSignIn there fails to load its UI chunk. `new Function` hides the
      // dynamic import() from the in-browser Babel transform so the browser runs it
      // natively as an ESM import (CORS on the FAPI host is `*`).
      const importESM = new Function("u", "return import(u)");
      // clerk-js v6 splits UI into a separate bundle. Load both: the ESM core
      // (clerk.mjs — chunks resolve via import.meta.url) and @clerk/ui's browser
      // bundle (self-contained; exposes window.__internal_ClerkUICtor). The UI ctor
      // must be passed to Clerk.load({ ui }) or mountSignIn throws "not loaded with
      // Ui components".
      const [mod] = await Promise.all([
        importESM(`https://${host}/npm/@clerk/clerk-js@6/dist/clerk.mjs`),
        loadScript(`https://${host}/npm/@clerk/ui@1/dist/ui.browser.js`),
      ]);
      const ClerkClass = (mod && (mod.Clerk || mod.default)) || null;
      const ClerkUI = window.__internal_ClerkUICtor;
      if (typeof ClerkClass !== "function") {
        throw new Error("clerk.mjs did not export a Clerk constructor.");
      }
      if (!ClerkUI) throw new Error("@clerk/ui failed to load (no ClerkUI ctor).");
      const clerk = new ClerkClass(cfg.clerkPublishableKey);
      await clerk.load({ ui: { ClerkUI } });
      window.Clerk = clerk; // normalize so the rest of the app uses the instance
      return clerk;
    })().catch((e) => {
      _clerkPromise = null; // don't cache failures — let a refresh/retry re-attempt
      throw e;
    });
  }
  return _clerkPromise;
}

// Turn an email local-part into a presentable name when no real name exists,
// e.g. "jordi.quoidbach@esade.edu" -> "Jordi Quoidbach". Used only as a fallback;
// real names from the sign-up form or SSO profile always win.
function friendlyNameFromEmail(email) {
  if (!email) return "";
  const local = String(email).split("@")[0];
  const tokens = local.split(/[._\-+]+/).filter(Boolean);
  const cap = (s) => (s ? s[0].toUpperCase() + s.slice(1) : s);
  return tokens.slice(0, 2).map(cap).join(" ");
}

function mapClerkUser(u) {
  if (!u) return null;
  const meta = u.publicMetadata || {};
  const rawAllowUserKeys = meta.allowUserKeys ?? meta.allow_user_keys;
  const email =
    u.primaryEmailAddress?.emailAddress ||
    u.emailAddresses?.[0]?.emailAddress ||
    null;
  const realName =
    u.fullName || [u.firstName, u.lastName].filter(Boolean).join(" ") || "";
  return {
    id: u.id,
    email,
    fullName: realName || friendlyNameFromEmail(email) || "there",
    company: meta.company || null,
    aiAccessMode: meta.aiAccessMode || meta.ai_access_mode || null,
    allowUserKeys: typeof rawAllowUserKeys === "boolean" ? rawAllowUserKeys : null,
    companyLicenseName: meta.companyLicenseName || meta.company_license_name || null,
    companyLicenseSeats: meta.companyLicenseSeats || meta.company_license_seats || null,
  };
}

// Make Clerk's mounted card blend into our branded panel instead of looking like
// a card-in-a-card: hide Clerk's own header (we show our heading above it), strip
// the card chrome so it sits flush and centered, and match the CABL brand accent.
const CLERK_APPEARANCE = {
  variables: {
    colorPrimary: "#1E1E5A", // --ink
    borderRadius: "10px",
    fontFamily: "inherit",
  },
  elements: {
    rootBox: { width: "100%" },
    cardBox: { width: "100%", boxShadow: "none", border: "none" },
    card: { boxShadow: "none", border: "none", background: "transparent", padding: 0, margin: "0 auto" },
    header: { display: "none" }, // our AuthLayout already shows the heading
    footer: { background: "transparent" },
  },
};

// ---------------------------------------------------------------------------
// Legacy custom auth transport
// ---------------------------------------------------------------------------
async function authRequest(action, payload = {}) {
  const resp = await fetch("/api/auth", {
    method: "POST",
    headers: { "content-type": "application/json" },
    credentials: "same-origin",
    body: JSON.stringify({ action, ...payload }),
  });
  const data = await resp.json().catch(() => ({}));
  if (!resp.ok || data.error) throw new Error(data.error || "Authentication failed.");
  return data;
}

// ---------------------------------------------------------------------------
// window.Auth — uniform shape across providers
// ---------------------------------------------------------------------------
window.Auth = {
  async signUp({ company, fullName, email, password }) {
    // Custom-mode only; Clerk handles sign-up through its mounted UI.
    const data = await authRequest("signup", { company, fullName, email, password });
    return data.user;
  },

  async logIn({ email, password }) {
    // Custom-mode only; Clerk handles sign-in through its mounted UI.
    const data = await authRequest("login", { email, password });
    return data.user;
  },

  async logOut() {
    const cfg = await getAuthConfig();
    if (cfg.provider === "clerk") {
      try {
        const clerk = await ensureClerk();
        await clerk.signOut();
      } catch {
        /* ignore */
      }
      return;
    }
    await authRequest("logout");
  },

  async getCurrentUser() {
    const cfg = await getAuthConfig();
    if (cfg.provider === "clerk") {
      try {
        const clerk = await ensureClerk();
        return mapClerkUser(clerk.user);
      } catch {
        return null;
      }
    }
    try {
      const resp = await fetch("/api/auth", { credentials: "same-origin" });
      const data = await resp.json().catch(() => ({}));
      return data.user || null;
    } catch {
      return null;
    }
  },

  // Clerk session token for authenticating API calls (e.g. /api/cases).
  // Returns null in custom mode (cookie auth is used there instead).
  async getToken() {
    const cfg = await getAuthConfig();
    if (cfg.provider !== "clerk") return null;
    try {
      const clerk = await ensureClerk();
      return clerk.session ? await clerk.session.getToken() : null;
    } catch {
      return null;
    }
  },

  async apiHeaders(extra = {}) {
    const token = await this.getToken().catch(() => null);
    return {
      "content-type": "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...extra,
    };
  },

  async seedDemoAccount() {
    // Custom mode lazily seeds the demo account on demo login; no-op for Clerk.
  },

  getConfig: getAuthConfig,

  initials(user) {
    if (!user?.fullName) return "??";
    const parts = user.fullName.trim().split(/\s+/);
    if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
    return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
  },
};

// Seed the demo account on first load so the prospect can log in immediately.
window.Auth.seedDemoAccount();

// ---------------------------------------------------------------------------
// React screens
// ---------------------------------------------------------------------------
const { useState: useStateAuth, useEffect: useEffectAuth, useRef: useRefAuth } = React;

// Resolves the active provider once, exposing null until known.
function useAuthProvider() {
  const [provider, setProvider] = useStateAuth(null);
  useEffectAuth(() => {
    let mounted = true;
    getAuthConfig().then((cfg) => {
      if (mounted) setProvider(cfg.provider || "custom");
    });
    return () => {
      mounted = false;
    };
  }, []);
  return provider;
}

function AuthLayout({ children, mode, onSwitch, hideSwitch }) {
  return (
    <div className="auth-screen">
      <div className="auth-bg" aria-hidden="true"/>
      <div className="auth-card">
        <div className="auth-brand">
          <img src="assets/cabl-logo.png" alt="CABL" className="auth-logo"/>
          <div className="auth-brand-text">
            <div className="auth-brand-product">CABL Procurement Lab</div>
            <div className="auth-brand-strap">Procurement Negotiation Intelligence</div>
          </div>
        </div>
        {children}
        {!hideSwitch && (
          <div className="auth-foot">
            {mode === "login" ? (
              <>New here? <a href="#signup" onClick={(e) => { e.preventDefault(); onSwitch("signup"); }}>Create an account</a></>
            ) : (
              <>Already have an account? <a href="#login" onClick={(e) => { e.preventDefault(); onSwitch("login"); }}>Sign in</a></>
            )}
          </div>
        )}
      </div>
      <div className="auth-foot-bar">
        <span>Procurement Negotiation Intelligence.</span>
        <span className="auth-foot-bar-r">© CABL & Decision Lab</span>
      </div>
    </div>
  );
}

// --- Clerk-mounted screen (prebuilt UI: email verification + SSO) -----------
function ClerkAuthScreen({ mode, onAuthed, onSwitch }) {
  const mountRef = useRefAuth(null);
  const [status, setStatus] = useStateAuth("loading"); // loading | ready | error
  const [errMsg, setErrMsg] = useStateAuth("");

  useEffectAuth(() => {
    let cancelled = false;
    let removeListener = null;
    (async () => {
      try {
        const clerk = await ensureClerk();
        if (cancelled) return;
        if (clerk.user) {
          onAuthed(mapClerkUser(clerk.user));
          return;
        }
        setStatus("ready");
        const el = mountRef.current;
        if (el) {
          const opts = { appearance: CLERK_APPEARANCE };
          if (mode === "signup") clerk.mountSignUp(el, opts);
          else clerk.mountSignIn(el, opts);
        }
        removeListener = clerk.addListener(({ user }) => {
          if (user) onAuthed(mapClerkUser(user));
        });
      } catch (e) {
        if (!cancelled) {
          setErrMsg(String((e && e.message) || e || "unknown error"));
          setStatus("error");
        }
      }
    })();
    return () => {
      cancelled = true;
      if (typeof removeListener === "function") removeListener();
    };
  }, [mode]);

  return (
    <AuthLayout mode={mode} onSwitch={onSwitch} hideSwitch>
      <h1 className="auth-h1">{mode === "signup" ? "Create your workspace." : "Welcome back."}</h1>
      <p className="auth-lede">
        {mode === "signup"
          ? "Set up your procurement team's negotiation intelligence in under a minute."
          : "Sign in to access your procurement workspace."}
      </p>
      {status === "error" && (
        <div className="auth-error">
          Sign-in is temporarily unavailable. Please refresh and try again.
          {errMsg ? <div style={{ marginTop: 6, fontSize: 12, opacity: 0.85 }}>Details: {errMsg}</div> : null}
        </div>
      )}
      <div ref={mountRef} className="clerk-mount"/>
    </AuthLayout>
  );
}

// --- Custom email/password screens (legacy fallback) ------------------------
function CustomLogInScreen({ onAuthed, onSwitch }) {
  const [email, setEmail] = useStateAuth("demo@acme.com");
  const [password, setPassword] = useStateAuth("");
  const [busy, setBusy] = useStateAuth(false);
  const [error, setError] = useStateAuth(null);

  async function submit(e) {
    e.preventDefault();
    setError(null);
    setBusy(true);
    try {
      const user = await window.Auth.logIn({ email: email.trim(), password });
      onAuthed(user);
    } catch (err) {
      setError(err.message || "Couldn't sign in.");
    } finally {
      setBusy(false);
    }
  }

  return (
    <AuthLayout mode="login" onSwitch={onSwitch}>
      <h1 className="auth-h1">Welcome back.</h1>
      <p className="auth-lede">Sign in to access your procurement workspace.</p>

      <div className="auth-demo-banner">
        <div className="auth-demo-kicker">Demo account</div>
        <div className="auth-demo-creds">
          <code>demo@acme.com</code> · password <code>Demo1234!</code>
        </div>
      </div>

      <form onSubmit={submit} className="auth-form">
        <label className="auth-field">
          <span>Work email</span>
          <input
            type="email"
            required
            autoComplete="email"
            value={email}
            onChange={e => setEmail(e.target.value)}
            placeholder="you@yourcompany.com"
          />
        </label>
        <label className="auth-field">
          <span>Password</span>
          <input
            type="password"
            required
            autoComplete="current-password"
            value={password}
            onChange={e => setPassword(e.target.value)}
            placeholder="Demo1234!"
          />
        </label>

        {error && <div className="auth-error">{error}</div>}

        <button type="submit" className="auth-submit" disabled={busy}>
          {busy ? "Signing in…" : "Sign in"}
        </button>

        <div className="auth-microcopy">
          <a href="#forgot" onClick={(e) => e.preventDefault()}>Forgot password?</a>
        </div>
      </form>
    </AuthLayout>
  );
}

function CustomSignUpScreen({ onAuthed, onSwitch }) {
  const [company, setCompany] = useStateAuth("");
  const [fullName, setFullName] = useStateAuth("");
  const [email, setEmail] = useStateAuth("");
  const [password, setPassword] = useStateAuth("");
  const [busy, setBusy] = useStateAuth(false);
  const [error, setError] = useStateAuth(null);

  async function submit(e) {
    e.preventDefault();
    setError(null);
    if (password.length < 8) {
      setError("Use at least 8 characters.");
      return;
    }
    setBusy(true);
    try {
      const user = await window.Auth.signUp({
        company: company.trim(),
        fullName: fullName.trim(),
        email: email.trim(),
        password,
      });
      onAuthed(user);
    } catch (err) {
      setError(err.message || "Couldn't create your account.");
    } finally {
      setBusy(false);
    }
  }

  return (
    <AuthLayout mode="signup" onSwitch={onSwitch}>
      <h1 className="auth-h1">Create your workspace.</h1>
      <p className="auth-lede">Set up your procurement team's negotiation intelligence in under a minute.</p>

      <form onSubmit={submit} className="auth-form">
        <label className="auth-field">
          <span>Company name</span>
          <input type="text" required value={company} onChange={e => setCompany(e.target.value)} placeholder="Your company"/>
        </label>
        <label className="auth-field">
          <span>Your full name</span>
          <input type="text" required autoComplete="name" value={fullName} onChange={e => setFullName(e.target.value)} placeholder="Jane Buyer"/>
        </label>
        <label className="auth-field">
          <span>Work email</span>
          <input type="email" required autoComplete="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="jane@yourcompany.com"/>
        </label>
        <label className="auth-field">
          <span>Password</span>
          <input type="password" required autoComplete="new-password" minLength={8} value={password} onChange={e => setPassword(e.target.value)} placeholder="At least 8 characters"/>
        </label>

        {error && <div className="auth-error">{error}</div>}

        <button type="submit" className="auth-submit" disabled={busy}>
          {busy ? "Creating your workspace…" : "Create account"}
        </button>

        <div className="auth-microcopy">
          Your workspace is private. Saved cases are encrypted, and uploaded files are not stored.
        </div>
      </form>
    </AuthLayout>
  );
}

// --- Provider-aware wrappers (what the app renders) -------------------------
function LogInScreen(props) {
  const provider = useAuthProvider();
  if (provider === null) return <div className="auth-screen"><div className="auth-bg" aria-hidden="true"/></div>;
  if (provider === "clerk") return <ClerkAuthScreen mode="login" {...props}/>;
  return <CustomLogInScreen {...props}/>;
}

function SignUpScreen(props) {
  const provider = useAuthProvider();
  if (provider === null) return <div className="auth-screen"><div className="auth-bg" aria-hidden="true"/></div>;
  if (provider === "clerk") return <ClerkAuthScreen mode="signup" {...props}/>;
  return <CustomSignUpScreen {...props}/>;
}

window.LogInScreen = LogInScreen;
window.SignUpScreen = SignUpScreen;
