// Frontend bootstrap layer: fetches /config.json from the backend, instantiates
// the Supabase client, and exposes a fetch helper that auto-injects the Bearer
// token. Globals after bootstrap:
//   window.gtConfig   — { apiUrl, supabaseUrl, supabasePublishableKey }
//   window.gtSupabase — Supabase client
//   window.gtAPI      — { bootstrap, fetch }
//   window.gtAuth     — { signIn, signUp, signOut, getUser, onAuthChange }

// Same-origin: backend (Hono) is mounted under /api. Locally the same Hono
// process serves web/ as static; on Vercel the static dir is served by the
// platform and /api/* is rewritten to a single function.
const API_URL = "/api";

let _config = null;
let _supabase = null;
let _bootstrapPromise = null;

async function bootstrap() {
  if (_supabase) return _supabase;
  if (_bootstrapPromise) return _bootstrapPromise;
  _bootstrapPromise = (async () => {
    const res = await fetch(`${API_URL}/config.json`);
    if (!res.ok) throw new Error(`config fetch failed: ${res.status}`);
    const remote = await res.json();
    _config = { apiUrl: API_URL, ...remote };
    window.gtConfig = _config;
    _supabase = window.supabase.createClient(
      _config.supabaseUrl,
      _config.supabasePublishableKey,
      { auth: { persistSession: true, autoRefreshToken: true } }
    );
    window.gtSupabase = _supabase;
    // Wait for INITIAL_SESSION so OAuth callback hash processing is complete
    // before any consumer calls getUser()/getSession().
    await Promise.race([
      new Promise((resolve) => {
        const sub = _supabase.auth.onAuthStateChange((event) => {
          if (event === "INITIAL_SESSION") {
            sub.data.subscription.unsubscribe();
            resolve();
          }
        });
      }),
      new Promise((resolve) => setTimeout(resolve, 1500)),
    ]);
    return _supabase;
  })();
  return _bootstrapPromise;
}

async function api(path, init = {}) {
  await bootstrap();
  const { data: { session } } = await _supabase.auth.getSession();
  const headers = new Headers(init.headers || {});
  if (session && session.access_token) {
    headers.set("Authorization", `Bearer ${session.access_token}`);
  }
  if (init.body && typeof init.body === "string" && !headers.has("Content-Type")) {
    headers.set("Content-Type", "application/json");
  }
  const res = await fetch(`${_config.apiUrl}${path}`, { ...init, headers });
  if (!res.ok) {
    let msg = `HTTP ${res.status}`;
    try {
      const body = await res.json();
      if (body && body.error && body.error.message) msg = body.error.message;
    } catch (_) {}
    const err = new Error(msg);
    err.status = res.status;
    throw err;
  }
  // 204 No Content / empty body: don't try to parse JSON (DELETE endpoints).
  if (res.status === 204) return null;
  const text = await res.text();
  if (!text) return null;
  try { return JSON.parse(text); } catch (_) { return null; }
}

window.gtAPI = { bootstrap, fetch: api };

const auth = {
  async signInWithGoogle(redirectTo) {
    await bootstrap();
    const { data, error } = await _supabase.auth.signInWithOAuth({
      provider: "google",
      options: { redirectTo: redirectTo || window.location.origin },
    });
    if (error) throw error;
    return data;
  },
  async signOut() {
    await bootstrap();
    const { error } = await _supabase.auth.signOut();
    if (error) throw error;
  },
  async getUser() {
    await bootstrap();
    const { data: { session } } = await _supabase.auth.getSession();
    return session && session.user ? session.user : null;
  },
  onAuthChange(cb) {
    let cancelled = false;
    let realUnsub = null;
    bootstrap().then(() => {
      if (cancelled) return;
      const sub = _supabase.auth.onAuthStateChange((event, session) => {
        cb(session && session.user ? session.user : null, event);
      });
      realUnsub = () => sub.data.subscription.unsubscribe();
    });
    return () => {
      cancelled = true;
      if (realUnsub) realUnsub();
    };
  },
};

window.gtAuth = auth;
