// Session 17 — requireAuth's on-demand users-row upsert path.
//
// The audit found that authenticated users with valid Supabase auth
// tokens were getting 401 "User profile not found" on /api/stripe/
// checkout because their corresponding row in public.users had never
// been inserted. The fix: when `.single()` on the users select
// returns PGRST116 ("no rows"), upsert a default row and re-read.
//
// These tests build a chainable Supabase fake that lets us steer the
// select to PGRST116 / success and assert which path requireAuth
// takes.

const { requireAuth } = require('../../src/middleware/auth');

function makeFake({ getUserResult, selectResult, selectAfterUpsertResult, upsertError = null }) {
  // The middleware calls `supabase.from('users')` multiple times in
  // the missing-profile recovery path (initial select, then upsert,
  // then reread). State that distinguishes "first select" from
  // "select-after-upsert" has to live on the supabase fake, not on
  // a fresh-per-call `from()` proxy. We track `hasUpserted` and
  // route `.single()` to the appropriate result.
  const upserts = [];
  let hasUpserted = false;

  const supabase = {
    auth: { getUser: () => Promise.resolve(getUserResult) },
    from() {
      const proxy = {
        select() { return proxy; },
        eq() { return proxy; },
        single() {
          return Promise.resolve(hasUpserted ? selectAfterUpsertResult : selectResult);
        },
        upsert(row, opts) {
          upserts.push({ row, opts });
          if (!upsertError) hasUpserted = true;
          return Promise.resolve({ data: null, error: upsertError });
        },
      };
      return proxy;
    },
    _upserts: upserts,
  };
  return supabase;
}

let mockSupabaseFake;
jest.mock('../../src/utils/supabase', () => ({
  getSupabaseServiceClient: () => mockSupabaseFake,
}));

function runMiddleware(req) {
  return new Promise((resolve) => {
    const res = {
      _status: null,
      _body: null,
      status(code) { this._status = code; return this; },
      json(body) { this._body = body; resolve({ status: this._status, body, fellThrough: false }); return this; },
    };
    requireAuth(req, res, () => resolve({ status: 200, body: null, fellThrough: true, req }));
  });
}

const VALID_USER = { id: 'user-1', email: 'a@b.com' };
const EXISTING_PROFILE = {
  id: 'user-1', email: 'a@b.com', tier: 'free', scan_count: 0,
  scan_reset_date: '2026-06-12', founder_status: false,
  grace_period_until: null, stripe_customer_id: null,
};

describe('requireAuth — pre-Session 17 behavior preserved', () => {
  test('valid token + existing profile → next() called with req.user set', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: EXISTING_PROFILE, error: null },
    });
    const req = { headers: { authorization: 'Bearer good-token' } };
    const out = await runMiddleware(req);
    expect(out.fellThrough).toBe(true);
    expect(req.user.id).toBe('user-1');
    expect(req.user.tier).toBe('free');
    expect(mockSupabaseFake._upserts).toHaveLength(0);
  });

  test('missing Authorization header → 401', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: null }, error: { message: 'no' } },
      selectResult: { data: null, error: null },
    });
    const out = await runMiddleware({ headers: {} });
    expect(out.status).toBe(401);
    expect(out.body.error).toMatch(/Authentication required/);
  });

  test('non-Bearer scheme → 401', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: null }, error: { message: 'no' } },
      selectResult: { data: null, error: null },
    });
    const out = await runMiddleware({ headers: { authorization: 'Basic xyz' } });
    expect(out.status).toBe(401);
  });

  test('Supabase getUser error → 401 invalid token', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: null }, error: { message: 'token expired' } },
      selectResult: { data: null, error: null },
    });
    const out = await runMiddleware({ headers: { authorization: 'Bearer bad' } });
    expect(out.status).toBe(401);
    expect(out.body.error).toMatch(/Invalid or expired/);
  });
});

describe('requireAuth — Session 17 missing-profile recovery', () => {
  test('missing users row (PGRST116) → upsert default + retry succeeds', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: null, error: { code: 'PGRST116', message: 'no rows' } },
      selectAfterUpsertResult: { data: EXISTING_PROFILE, error: null },
    });
    const req = { headers: { authorization: 'Bearer good' } };
    const out = await runMiddleware(req);
    expect(out.fellThrough).toBe(true);
    expect(req.user.id).toBe('user-1');
    expect(mockSupabaseFake._upserts).toHaveLength(1);
    const u = mockSupabaseFake._upserts[0];
    expect(u.row.id).toBe('user-1');
    expect(u.row.email).toBe('a@b.com');
    expect(u.row.tier).toBe('free');
    expect(u.row.scan_count).toBe(0);
    expect(u.row.founder_status).toBe(false);
    expect(u.opts.onConflict).toBe('id');
  });

  test('PGRST116 detected by message text (no code field)', async () => {
    // Some Supabase client versions surface the error sans code field.
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: null, error: { message: 'JSON object requested, multiple (or no) rows returned' } },
      selectAfterUpsertResult: { data: EXISTING_PROFILE, error: null },
    });
    const out = await runMiddleware({ headers: { authorization: 'Bearer good' } });
    expect(out.fellThrough).toBe(true);
    expect(mockSupabaseFake._upserts).toHaveLength(1);
  });

  test('upsert itself fails → 401 with distinct message', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: null, error: { code: 'PGRST116', message: 'no rows' } },
      selectAfterUpsertResult: { data: EXISTING_PROFILE, error: null },
      upsertError: { message: 'check_violation' },
    });
    const out = await runMiddleware({ headers: { authorization: 'Bearer good' } });
    expect(out.status).toBe(401);
    expect(out.body.error).toMatch(/User profile creation failed/);
  });

  test('post-upsert reread still empty → 401 User profile not found', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: null, error: { code: 'PGRST116', message: 'no rows' } },
      selectAfterUpsertResult: { data: null, error: null },
    });
    const out = await runMiddleware({ headers: { authorization: 'Bearer good' } });
    expect(out.status).toBe(401);
    expect(out.body.error).toMatch(/User profile not found/);
  });

  test('non-PGRST116 select error → 401 immediately (no upsert)', async () => {
    mockSupabaseFake = makeFake({
      getUserResult: { data: { user: VALID_USER }, error: null },
      selectResult: { data: null, error: { code: '42501', message: 'permission denied' } },
      selectAfterUpsertResult: { data: EXISTING_PROFILE, error: null },
    });
    const out = await runMiddleware({ headers: { authorization: 'Bearer good' } });
    expect(out.status).toBe(401);
    // Critically, no upsert attempt — we don't recover from a real DB error.
    expect(mockSupabaseFake._upserts).toHaveLength(0);
  });
});
