// Security regression test for resolveGoogleUser: a Google sign-in must NEVER
// adopt a pre-existing local account by matching email (that would be account
// takeover). It links only by google_sub, otherwise provisions a fresh viewer.
// Skips without TEST_DATABASE_URL.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { isTestDbConfigured, setupTestDb } from '../helpers/setup-db.js';
import { hashPassword } from '../../src/auth/passwords.js';

const SKIP = !isTestDbConfigured() && 'TEST_DATABASE_URL not set';

async function loadResolve() {
  process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
  return (await import('../../src/routes/auth.js?v=' + Date.now())).resolveGoogleUser;
}

test('a Google login with an email matching a local admin does NOT take over that account', { skip: SKIP }, async () => {
  const pool = await setupTestDb();
  process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
  try {
    // Pre-existing local admin with a password and the same email the attacker controls.
    const adminId = (await pool.query(
      `INSERT INTO users (username, password_hash, role, email)
       VALUES ('boss', $1, 'admin', 'boss@wilddragon.net') RETURNING id`,
      [await hashPassword('a-real-password-12')])).rows[0].id;

    const resolveGoogleUser = await loadResolve();
    const user = await resolveGoogleUser({ sub: 'google-attacker-sub', email: 'boss@wilddragon.net', name: 'Not The Boss' });

    // Must be a brand-new account, NOT the admin.
    assert.notEqual(user.id, adminId, 'Google login must not resolve to the existing admin');
    const row = (await pool.query(`SELECT role, google_sub FROM users WHERE id = $1`, [user.id])).rows[0];
    assert.equal(row.role, 'viewer', 'auto-provisioned account must be a viewer');
    assert.equal(row.google_sub, 'google-attacker-sub');
    // The admin row is untouched (no google_sub linked onto it).
    const admin = (await pool.query(`SELECT google_sub FROM users WHERE id = $1`, [adminId])).rows[0];
    assert.equal(admin.google_sub, null, 'the existing admin must not have been linked');
  } finally { await pool.end(); }
});

test('a returning Google user resolves to the same account by google_sub', { skip: SKIP }, async () => {
  const pool = await setupTestDb();
  process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
  try {
    const resolveGoogleUser = await loadResolve();
    const first  = await resolveGoogleUser({ sub: 'sub-123', email: 'alice@wilddragon.net', name: 'Alice' });
    const second = await resolveGoogleUser({ sub: 'sub-123', email: 'alice@wilddragon.net', name: 'Alice' });
    assert.equal(first.id, second.id, 'same google_sub must map to the same user');
    const count = (await pool.query(`SELECT COUNT(*)::int AS n FROM users WHERE google_sub = 'sub-123'`)).rows[0].n;
    assert.equal(count, 1, 'must not create a duplicate on second login');
  } finally { await pool.end(); }
});

test('username collisions get a numeric suffix', { skip: SKIP }, async () => {
  const pool = await setupTestDb();
  process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
  try {
    await pool.query(`INSERT INTO users (username, password_hash, role) VALUES ('sam', 'x', 'viewer')`);
    const resolveGoogleUser = await loadResolve();
    const u = await resolveGoogleUser({ sub: 'sub-sam', email: 'sam@wilddragon.net', name: 'Sam' });
    assert.match(u.username, /^sam\d+$/, 'colliding username should get a numeric suffix');
  } finally { await pool.end(); }
});
