const { getSupabaseServiceClient } = require('../utils/supabase');

/**
 * Session 17 — `requireAuth` was 401'ing legitimate users whose Supabase
 * auth account had never had a matching row inserted into the
 * `public.users` table. Signup writes to `auth.users` automatically;
 * the corresponding row in `public.users` was supposed to land via a
 * trigger or a post-signup handler, but for SSO callbacks and any
 * legacy account that pre-dated the trigger the row was simply
 * missing. The audit found this: checkout 401'd as "User profile not
 * found" for valid sessions.
 *
 * The fix is an on-demand upsert. When the select misses, we insert a
 * default row (tier='free') keyed by the Supabase auth user's id +
 * email, then re-select. This is idempotent — concurrent requests
 * land on the upsert's existing-row branch and re-read the same row.
 *
 * If the insert ITSELF fails (genuine DB issue, not a missing row),
 * we 401 with a distinct message ('User profile creation failed') so
 * the operator can distinguish missing-row recovery from real DB
 * outages in logs.
 */

const PROFILE_COLUMNS = 'id, email, tier, scan_count, scan_reset_date, founder_status, grace_period_until, stripe_customer_id';

async function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Authentication required' });
  }

  const token = authHeader.slice(7);
  const supabase = getSupabaseServiceClient();

  const { data: { user }, error } = await supabase.auth.getUser(token);
  if (error || !user) {
    return res.status(401).json({ error: 'Invalid or expired token' });
  }

  // First read. PostgREST returns `error.code === 'PGRST116'` when
  // `.single()` gets zero rows — that's the signal we use to trigger
  // the on-demand upsert below. Other errors (network, permission)
  // bubble up as a real 401.
  let { data: profile, error: profileError } = await supabase
    .from('users')
    .select(PROFILE_COLUMNS)
    .eq('id', user.id)
    .single();

  // Session 17 — if no row exists, create one on the fly. The
  // `auth.users` row already exists (we just resolved a token against
  // it), so we're not creating an identity — we're filling in the
  // app-side row that should have been created at signup time but
  // wasn't (SSO callbacks, legacy accounts pre-trigger, etc.).
  // Detect PostgREST's "single requested, zero rows returned" via
  // either the canonical error code (preferred) or the message
  // pattern Supabase emits when the code field is stripped by an
  // older client. Canonical message:
  // "JSON object requested, multiple (or no) rows returned"
  const isMissingRow = profileError && (
    profileError.code === 'PGRST116'
    || /\(or no\) rows/i.test(profileError.message || '')
    || /^no rows$/i.test(profileError.message || '')
  );
  if (!profile && isMissingRow) {
    const defaultRow = {
      id: user.id,
      email: user.email || null,
      tier: 'free',
      scan_count: 0,
      scan_reset_date: new Date().toISOString().slice(0, 10),
      founder_status: false,
    };
    const { error: upsertErr } = await supabase
      .from('users')
      .upsert(defaultRow, { onConflict: 'id' });
    if (upsertErr) {
      console.warn('[requireAuth] users-row upsert failed:', upsertErr.message);
      return res.status(401).json({ error: 'User profile creation failed' });
    }
    // Re-select. Use the same .single() shape so test mocks keep working.
    const reread = await supabase
      .from('users')
      .select(PROFILE_COLUMNS)
      .eq('id', user.id)
      .single();
    profile = reread.data;
    profileError = reread.error;
  }

  if (profileError || !profile) {
    return res.status(401).json({ error: 'User profile not found' });
  }

  req.user = profile;
  next();
}

module.exports = { requireAuth };
