// Single-use ledger for WebAuthn challenges (data/webauthn-replay.db, better-sqlite3).
//
// The web flow already gets single-use semantics from the challenge cookie being deleted on
// consume. The native app carries the signed challenge token in the request BODY instead (it
// gets no cookies), so a captured login-verify body could be replayed within the 5-minute TTL.
// Every consumed challenge is recorded here and rejected on a second sight — shared across PM2
// workers because SQLite lives on disk. Same lazy-open / fail-soft pattern as fcmDb.ts: if the
// DB cannot be opened the check degrades to "not seen" and logs a warning instead of breaking login.

import Database from 'better-sqlite3'
import path from 'path'
import fs from 'fs'

let db: any = null
let initFailed = false

const getDb = () => {
  if (db) return db
  if (initFailed) return null
  try {
    const dbPath = path.join(process.cwd(), 'data', 'webauthn-replay.db')
    const dataDir = path.dirname(dbPath)
    if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
    const _db = new Database(dbPath)
    _db.pragma('journal_mode = WAL')
    _db.exec(`CREATE TABLE IF NOT EXISTS used_challenges (
      challenge TEXT PRIMARY KEY,
      kind TEXT NOT NULL,
      used_at INTEGER NOT NULL,
      expires_at INTEGER NOT NULL
    );
    CREATE INDEX IF NOT EXISTS idx_used_challenges_exp ON used_challenges(expires_at);`)
    db = _db
    return db
  } catch (e: any) {
    initFailed = true
    console.warn('[webauthnReplayDb] open failed — replay protection degraded:', e?.message)
    return null
  }
}

/**
 * Marks a challenge as used. Returns false when it was already recorded (replay).
 * `expiresAt` lets the row be pruned once the challenge could not verify anyway.
 */
export const markChallengeUsed = (challenge: string, kind: string, expiresAt: number): boolean => {
  const d = getDb()
  if (!d || !challenge) return true
  try {
    const now = Date.now()
    d.prepare('DELETE FROM used_challenges WHERE expires_at < ?').run(now - 60_000)
    const r = d.prepare(
      'INSERT OR IGNORE INTO used_challenges (challenge, kind, used_at, expires_at) VALUES (?, ?, ?, ?)'
    ).run(challenge, kind, now, expiresAt)
    return r.changes === 1
  } catch (e: any) {
    console.warn('[webauthnReplayDb] mark failed:', e?.message)
    return true
  }
}
