// supabase-client.jsx — Supabase auth + profile API for Mahamon House
// Dedicated project (no longer shared with Sprompt). Table: public.profiles

const SUPABASE_URL = 'https://zrzybukyqixavcjkpexu.supabase.co';
const SUPABASE_KEY = 'sb_publishable_FDUChLdiK4FEmbgGsju-4w_t6Yd1YUe';

// Default starting balance for fresh accounts. Mirrors APP_TWEAKS in app.jsx
// so a brand-new user, an OAuth user with no profile yet, and a returning
// device that lost localStorage all converge on the same number.
const DEFAULT_BALANCE = 2000;

// `window.supabase` is loaded from CDN in index.html
const supabaseClient = window.supabase
  ? window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY, {
      auth: {
        persistSession: true,
        autoRefreshToken: true,
        storage: window.localStorage,
        storageKey: 'mahamon.sb-session',
      },
    })
  : null;

const sb = {
  client: supabaseClient,
  isAvailable: () => !!supabaseClient,

  // ─── Auth ────────────────────────────────────────────
  async signUp({ email, pin, name }) {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const { data, error } = await supabaseClient.auth.signUp({
      email,
      password: pin,
      options: { data: { name } },
    });
    if (error) throw error;
    if (data.session && data.user) {
      await sb.ensureProfile(data.user, { name });
    }
    return data;
  },

  async signIn({ email, pin }) {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const { data, error } = await supabaseClient.auth.signInWithPassword({
      email,
      password: pin,
    });
    if (error) throw error;
    if (data.user) await sb.ensureProfile(data.user);
    return data;
  },

  async signOut() {
    if (!supabaseClient) return;
    await supabaseClient.auth.signOut();
  },

  // Google OAuth — kicks off the redirect dance.
  async signInWithGoogle() {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const redirectTo = window.location.origin + window.location.pathname;
    const { error } = await supabaseClient.auth.signInWithOAuth({
      provider: 'google',
      options: { redirectTo },
    });
    if (error) throw error;
  },

  // Make sure a profiles row exists for this user. Idempotent — safe to call
  // on every SIGNED_IN event. OAuth signups skip our signUp() so this is the
  // only place the row gets created for them.
  // The INSERT explicitly sets balance = DEFAULT_BALANCE so a new account
  // immediately matches the in-app initial state, with no race against
  // hydrate() that previously made new users appear with stale localStorage
  // values.
  async ensureProfile(user, hint) {
    if (!supabaseClient || !user) return;
    // Fetch the FULL row, not just user_id. Otherwise callers fall back to
    // OAuth metadata for the name and lose any user-chosen rename.
    // (Supabase re-syncs auth.user_metadata from Google on every sign-in,
    // so the user's chosen 'Khey' would otherwise revert to Google's 'Denis'.)
    const { data: existing } = await supabaseClient
      .from('profiles')
      .select('*')
      .eq('user_id', user.id)
      .maybeSingle();
    if (existing) return existing;
    const meta = user.user_metadata || {};
    const fallbackName =
      (hint && hint.name) ||
      meta.name || meta.full_name ||
      (user.email || '').split('@')[0] ||
      'guest';
    const name = String(fallbackName).slice(0, 24);
    const { data: inserted, error } = await supabaseClient
      .from('profiles')
      .insert({
        user_id: user.id,
        name,
        email: user.email,
        balance: DEFAULT_BALANCE,
      })
      .select()
      .maybeSingle();
    // 23505 = unique_violation — concurrent insert raced us, treat as success
    if (error && error.code !== '23505') throw error;
    return inserted || null;
  },

  // Send a reset-PIN email.
  async requestPinReset(email) {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const redirectTo = window.location.origin + window.location.pathname;
    const { error } = await supabaseClient.auth.resetPasswordForEmail(email, { redirectTo });
    if (error) throw error;
  },

  async updatePin(newPin) {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const { error } = await supabaseClient.auth.updateUser({ password: newPin });
    if (error) throw error;
  },

  async getSession() {
    if (!supabaseClient) return null;
    const { data } = await supabaseClient.auth.getSession();
    return data.session || null;
  },

  onAuthChange(handler) {
    if (!supabaseClient) return () => {};
    const { data } = supabaseClient.auth.onAuthStateChange((event, session) => {
      handler(event, session);
    });
    return () => data.subscription.unsubscribe();
  },

  // ─── Profile ─────────────────────────────────────────
  async getProfile(userId) {
    if (!supabaseClient) return null;
    const { data, error } = await supabaseClient
      .from('profiles')
      .select('*')
      .eq('user_id', userId)
      .maybeSingle();
    if (error) throw error;
    return data;
  },

  async updateProfile(userId, patch) {
    if (!supabaseClient) return;
    const { error } = await supabaseClient
      .from('profiles')
      .update(patch)
      .eq('user_id', userId);
    if (error) throw error;
  },

  // Persist the user's display name in BOTH places:
  //   - auth.users.user_metadata (so a fresh signIn returns the new name)
  //   - public.profiles.name (so the ranking shows it)
  async updateUserName(userId, name) {
    if (!supabaseClient) throw new Error('Supabase not loaded');
    const trimmed = (name || '').toString().slice(0, 24);
    const { error: aErr } = await supabaseClient.auth.updateUser({ data: { name: trimmed } });
    if (aErr) throw aErr;
    const { error: pErr } = await supabaseClient
      .from('profiles')
      .update({ name: trimmed })
      .eq('user_id', userId);
    if (pErr) throw pErr;
  },

  // ─── Balance / tipped sync ───────────────────────────
  async setBalance(userId, value) {
    if (!supabaseClient) return null;
    const v = Math.max(0, Math.floor(Number(value) || 0));
    const { data, error } = await supabaseClient
      .from('profiles')
      .update({ balance: v })
      .eq('user_id', userId)
      .select('balance, balance_updated_at')
      .maybeSingle();
    if (error) throw error;
    return data;
  },

  async addToTipped(userId, amount) {
    if (!supabaseClient || amount <= 0) return;
    const { data: cur } = await supabaseClient
      .from('profiles')
      .select('tipped')
      .eq('user_id', userId)
      .single();
    if (!cur) return;
    await supabaseClient
      .from('profiles')
      .update({ tipped: Number(cur.tipped) + amount })
      .eq('user_id', userId);
  },

  async unlockGame(userId, gameId) {
    if (!supabaseClient) return;
    const { data: cur } = await supabaseClient
      .from('profiles')
      .select('unlocked_games')
      .eq('user_id', userId)
      .single();
    if (!cur) return;
    const set = new Set(cur.unlocked_games || []);
    set.add(gameId);
    await supabaseClient
      .from('profiles')
      .update({ unlocked_games: Array.from(set) })
      .eq('user_id', userId);
  },

  async deleteAccount(userId) {
    if (!supabaseClient) return;
    await supabaseClient.from('profiles').delete().eq('user_id', userId);
    await supabaseClient.auth.signOut();
  },

  // ─── Bonus claims (server-clock to prevent clock-rollback exploits) ──
  // The claim_* RPCs run as SECURITY DEFINER on the DB and use NOW() — so
  // a user who sets their device clock back can't bypass the cooldown.
  // Returns the new last-claim timestamp (or the existing one if still
  // on cooldown, in which case the chips DON'T get added client-side).
  async claimDailyOnServer(userId) {
    if (!supabaseClient) return null;
    const { data, error } = await supabaseClient.rpc('claim_daily', { p_user_id: userId });
    if (error) throw error;
    return data;  // ISO timestamp string
  },
  async claimGiftOnServer(userId) {
    if (!supabaseClient) return null;
    const { data, error } = await supabaseClient.rpc('claim_gift', { p_user_id: userId });
    if (error) throw error;
    return data;
  },

  // ─── Ranking ─────────────────────────────────────────
  async getRanking(limit = 50) {
    if (!supabaseClient) return [];
    const { data, error } = await supabaseClient
      .from('profiles')
      .select('user_id, name, tipped')
      .order('tipped', { ascending: false })
      .limit(limit);
    if (error) throw error;
    return data || [];
  },
};

Object.assign(window, { sb, supabaseClient });
