import rateLimit from 'express-rate-limit'
import qrcode from 'qrcode'
import { parseCookies } from '../helpers.js'
import { APP_VERSION, GIT_COMMIT } from '../config.js'
import {
  isAdminPasswordConfigured,
  isValidAdminSession,
  requireAdminAuth,
  setAdminSessionCookie,
  clearAdminSessionCookie,
  createAdminSession,
  deleteAdminSession,
  isAdminPasswordValid,
  isTotpEnabled,
  loadTotpState,
  saveTotpState,
  generateTotpSecret,
  getTotpUri,
  verifyTotpCode,
  generateRecoveryCodes,
  hashRecoveryCode,
  consumeRecoveryCode,
  createPendingSession,
  consumePendingSession,
} from '../auth.js'

const ADMIN_SESSION_COOKIE = 'vbn_admin_session'

const loginRateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 10,
  standardHeaders: true,
  legacyHeaders: false,
  message: { message: 'Too many login attempts. Please wait 15 minutes and try again.' },
  skipSuccessfulRequests: true,
})

export function register(app) {
  app.get('/api/admin-auth/status', async (req, res) => {
    res.json({
      authenticated: isValidAdminSession(req),
      configured: isAdminPasswordConfigured(),
      totpEnabled: await isTotpEnabled(),
      version: APP_VERSION,
      commit: GIT_COMMIT,
    })
  })

  app.post('/api/admin-auth/login', loginRateLimiter, async (req, res) => {
    const password = typeof req.body?.password === 'string' ? req.body.password : ''

    if (!isAdminPasswordConfigured()) {
      res.status(503).json({ message: 'ADMIN_PASSWORD is not configured on the server.' })
      return
    }

    if (!isAdminPasswordValid(password)) {
      res.status(401).json({ message: 'Invalid password.' })
      return
    }

    const totpOn = await isTotpEnabled()
    if (totpOn) {
      const pendingToken = createPendingSession()
      res.json({ totpRequired: true, pendingToken })
      return
    }

    const sessionToken = createAdminSession()
    setAdminSessionCookie(res, sessionToken)
    res.json({ ok: true })
  })

  app.post('/api/admin-auth/totp-verify', loginRateLimiter, async (req, res) => {
    const { pendingToken, code } = req.body ?? {}

    if (!consumePendingSession(pendingToken)) {
      res.status(401).json({ message: 'Session expired or invalid. Please sign in again.' })
      return
    }

    const totpState = await loadTotpState()
    if (!totpState?.secret || !totpState?.verified) {
      res.status(400).json({ message: 'TOTP is not configured.' })
      return
    }

    const codeStr = typeof code === 'string' ? code.trim() : ''

    if (verifyTotpCode(totpState.secret, codeStr)) {
      const sessionToken = createAdminSession()
      setAdminSessionCookie(res, sessionToken)
      res.json({ ok: true })
      return
    }

    if (consumeRecoveryCode(totpState, codeStr)) {
      await saveTotpState(totpState)
      const sessionToken = createAdminSession()
      setAdminSessionCookie(res, sessionToken)
      res.json({ ok: true, usedRecoveryCode: true, remainingRecoveryCodes: totpState.hashedRecoveryCodes.length })
      return
    }

    res.status(401).json({ message: 'Invalid code. Try again or use a recovery code.' })
  })

  app.post('/api/admin-auth/totp-setup-init', requireAdminAuth, async (req, res) => {
    const secret = generateTotpSecret()
    const uri = getTotpUri(secret)
    const qrDataUrl = await qrcode.toDataURL(uri)
    const existing = await loadTotpState()
    await saveTotpState({ ...existing, secret, verified: false })
    res.json({ qrDataUrl, secret })
  })

  app.post('/api/admin-auth/totp-setup-confirm', requireAdminAuth, async (req, res) => {
    const { code } = req.body ?? {}
    const totpState = await loadTotpState()

    if (!totpState?.secret) {
      res.status(400).json({ message: 'No TOTP setup in progress. Call /totp-setup-init first.' })
      return
    }

    if (!verifyTotpCode(totpState.secret, typeof code === 'string' ? code.trim() : '')) {
      res.status(401).json({ message: 'Code incorrect. Scan the QR code again and try once more.' })
      return
    }

    const recoveryCodes = generateRecoveryCodes()
    await saveTotpState({
      secret: totpState.secret,
      verified: true,
      hashedRecoveryCodes: recoveryCodes.map(hashRecoveryCode),
      enabledAt: new Date().toISOString(),
    })

    res.json({ ok: true, recoveryCodes })
  })

  app.post('/api/admin-auth/totp-disable', requireAdminAuth, async (req, res) => {
    await saveTotpState({ secret: null, verified: false, hashedRecoveryCodes: [], disabledAt: new Date().toISOString() })
    res.json({ ok: true })
  })

  app.post('/api/admin-auth/totp-regen-recovery', requireAdminAuth, async (req, res) => {
    const totpState = await loadTotpState()
    if (!totpState?.secret || !totpState?.verified) {
      res.status(400).json({ message: 'TOTP is not enabled.' })
      return
    }
    const recoveryCodes = generateRecoveryCodes()
    await saveTotpState({ ...totpState, hashedRecoveryCodes: recoveryCodes.map(hashRecoveryCode) })
    res.json({ ok: true, recoveryCodes })
  })

  app.post('/api/admin-auth/logout', (req, res) => {
    const cookies = parseCookies(req.headers.cookie)
    const sessionToken = cookies[ADMIN_SESSION_COOKIE]
    deleteAdminSession(sessionToken)
    clearAdminSessionCookie(res)
    res.json({ ok: true })
  })
}
