// Set before requiring auth.js (reads JWT_SECRET at load) and db.js (builds the
// pool at load). Pointing the DB at a closed port stops the pool from eagerly
// opening idle connections that would keep this test process alive — none of
// these tests touch the database.
process.env.JWT_SECRET = process.env.JWT_SECRET || 'test-secret'
process.env.DB_HOST = '127.0.0.1'
process.env.DB_PORT = '59999'

const { test, after } = require('node:test')
const assert = require('node:assert/strict')
const speakeasy = require('speakeasy')

const totp = require('../src/utils/totp')
const { needsTotp } = require('../src/router/v1/auth/auth.controller')
const { signTotpChallenge, verifyTotpChallenge, getUserFromRequest } = require('../src/utils/auth')
const db = require('../src/utils/db')

after(() => db.close())

test('needsTotp: disabled user does not require a second factor', () => {
  assert.equal(needsTotp({ id: 1, totp_enabled: 0 }), false)
  assert.equal(needsTotp({ id: 1 }), false)
})

test('needsTotp: enabled user requires a second factor', () => {
  assert.equal(needsTotp({ id: 1, totp_enabled: 1 }), true)
})

test('verifyCode accepts a current code and rejects a wrong/absent one', () => {
  const { base32 } = totp.generateSecret('alice')
  const good = speakeasy.totp({ secret: base32, encoding: 'base32' })
  assert.equal(totp.verifyCode(base32, good), true)
  assert.equal(totp.verifyCode(base32, '000000'), false)
  assert.equal(totp.verifyCode(base32, ''), false)
  assert.equal(totp.verifyCode(null, good), false)
})

test('generateSecret yields a base32 secret and an otpauth URL', () => {
  const s = totp.generateSecret('bob')
  assert.ok(s.base32 && s.base32.length >= 16)
  assert.match(s.otpauthUrl, /^otpauth:\/\/totp\//)
})

test('qrDataUrl renders the otpauth URL to a PNG data URL', async () => {
  const s = totp.generateSecret('carol')
  const dataUrl = await totp.qrDataUrl(s.otpauthUrl)
  assert.match(dataUrl, /^data:image\/png;base64,/)
})

// The password-verified challenge must never work as a real session token.
test('TOTP challenge token is not accepted as a session', () => {
  const token = signTotpChallenge({ id: 42 })
  // Valid as a challenge...
  const challenge = verifyTotpChallenge(token)
  assert.equal(challenge.id, 42)
  // ...but rejected as a session (stage-tagged) when presented as a cookie/bearer.
  const req = { cookies: {}, headers: { authorization: `Bearer ${token}` } }
  assert.equal(getUserFromRequest(req), null)
})

test('a normal session token is not accepted as a TOTP challenge', () => {
  const { signToken } = require('../src/utils/auth')
  const session = signToken({ id: 7, username: 'x', role: 'admin' })
  assert.equal(verifyTotpChallenge(session), null)
})
