// src/routes/admin.js
const express = require('express');
const db = require('../db');
const { hashPassword, verifyPassword, checkPasswordStrength } = require('../auth');
const requireAdmin = require('../middleware/requireAdmin');
const rateLimit = require('../middleware/rateLimit');
const { logAudit, clientIp } = require('../middleware/audit');

const router = express.Router();

// ---------------------------------------------------------------------------
// Escalating lockout ("punishment") for repeated failed sign-ins.
// The more an account fails, the longer it is locked — this beats a flat
// rate limit because it survives an attacker rotating IPs, while still
// letting a genuine admin who mistyped a password once try again quickly.
// ---------------------------------------------------------------------------
function lockoutMsFor(failedAttempts) {
  if (failedAttempts >= 12) return 60 * 60_000; // 1 hour
  if (failedAttempts >= 8) return 10 * 60_000;  // 10 minutes
  if (failedAttempts >= 5) return 2 * 60_000;   // 2 minutes
  if (failedAttempts >= 3) return 30_000;       // 30 seconds
  return 0;
}

function isLocked(user) {
  if (!user.locked_until) return false;
  return new Date(user.locked_until).getTime() > Date.now();
}

// LOGIN
router.post('/login', rateLimit({ windowMs: 5 * 60_000, max: 15 }), (req, res) => {
  const { username, password } = req.body || {};
  const ip = clientIp(req);
  if (!username || !password) return res.status(400).json({ error: 'Username and password are required.' });

  const cleanUsername = String(username).trim();
  const user = db.prepare('SELECT * FROM admin_users WHERE username = ?').get(cleanUsername);

  // Account currently locked out from previous failures.
  if (user && isLocked(user)) {
    const secondsLeft = Math.ceil((new Date(user.locked_until).getTime() - Date.now()) / 1000);
    db.prepare(`INSERT INTO login_attempts (username, ip, success) VALUES (?, ?, 0)`).run(cleanUsername, ip);
    return res.status(423).json({
      error: `Too many failed attempts. This account is temporarily locked. Try again in ${secondsLeft > 90 ? Math.ceil(secondsLeft / 60) + ' min' : secondsLeft + 's'}.`,
      lockedForSeconds: secondsLeft
    });
  }

  const valid = user && verifyPassword(password, user.password_hash);

  if (!valid) {
    db.prepare(`INSERT INTO login_attempts (username, ip, success) VALUES (?, ?, 0)`).run(cleanUsername, ip);
    if (user) {
      const failedAttempts = (user.failed_attempts || 0) + 1;
      const lockMs = lockoutMsFor(failedAttempts);
      const lockedUntil = lockMs ? new Date(Date.now() + lockMs).toISOString() : null;
      db.prepare('UPDATE admin_users SET failed_attempts = ?, locked_until = ? WHERE id = ?')
        .run(failedAttempts, lockedUntil, user.id);
      logAudit(req, 'login_failed', 'session', user.id, `${failedAttempts} consecutive failed attempt(s)`, cleanUsername);
      if (lockedUntil) {
        return res.status(423).json({
          error: `Invalid credentials. Account locked for ${Math.round(lockMs / 1000)}s after repeated failures.`,
          lockedForSeconds: Math.round(lockMs / 1000)
        });
      }
    } else {
      logAudit(req, 'login_failed', 'session', null, `Unknown username attempted: ${cleanUsername}`, cleanUsername);
    }
    return res.status(401).json({ error: 'Invalid username or password.' });
  }

  // Success — clear the slate.
  db.prepare('UPDATE admin_users SET failed_attempts = 0, locked_until = NULL, last_login_at = datetime(\'now\'), last_login_ip = ? WHERE id = ?')
    .run(ip, user.id);
  db.prepare(`INSERT INTO login_attempts (username, ip, success) VALUES (?, ?, 1)`).run(cleanUsername, ip);

  req.session.regenerate((err) => {
    if (err) return res.status(500).json({ error: 'Could not start session.' });
    req.session.adminId = user.id;
    req.session.username = user.username;
    req.session.lastActivity = Date.now();
    logAudit(req, 'login', 'session', user.id, null);
    res.json({ ok: true, username: user.username, role: user.role || 'admin' });
  });
});

// LOGOUT
router.post('/logout', (req, res) => {
  if (req.session?.adminId) logAudit(req, 'logout', 'session', req.session.adminId, null);
  req.session.destroy(() => res.json({ ok: true }));
});

// CURRENT SESSION
router.get('/me', requireAdmin, (req, res) => {
  const user = db.prepare('SELECT username, role, last_login_at, last_login_ip FROM admin_users WHERE id = ?').get(req.session.adminId);
  res.json({ username: req.session.username, role: user?.role || 'admin', lastLoginAt: user?.last_login_at, lastLoginIp: user?.last_login_ip });
});

// CHANGE PASSWORD
router.post('/change-password', requireAdmin, (req, res) => {
  const { currentPassword, newPassword } = req.body || {};
  const strength = checkPasswordStrength(newPassword);
  if (!strength.ok) {
    return res.status(400).json({ error: strength.reasons.join(' ') });
  }
  const user = db.prepare('SELECT * FROM admin_users WHERE id = ?').get(req.session.adminId);
  if (!user || !verifyPassword(currentPassword || '', user.password_hash)) {
    logAudit(req, 'login_failed', 'admin_users', user?.id, 'Incorrect current password on change-password attempt');
    return res.status(401).json({ error: 'Current password is incorrect.' });
  }
  db.prepare('UPDATE admin_users SET password_hash = ? WHERE id = ?').run(hashPassword(newPassword), user.id);
  logAudit(req, 'update', 'admin_users', user.id, 'Password changed');
  res.json({ ok: true });
});

// DASHBOARD STATS
router.get('/stats', requireAdmin, (req, res) => {
  const count = (table, where = '1=1') => db.prepare(`SELECT COUNT(*) AS n FROM ${table} WHERE ${where}`).get().n;
  res.json({
    newMessages: count('messages', "status = 'new'"),
    totalMessages: count('messages'),
    subscribers: count('subscribers'),
    stories: count('stories'),
    insights: count('insights'),
    press: count('press'),
    media: count('media'),
    upcomingEvents: count('events', "date(event_date) >= date('now')"),
    chatSessions: db.prepare('SELECT COUNT(DISTINCT session_id) AS n FROM chat_logs').get().n,
    pageViewsToday: count('page_views', "date(created_at) = date('now')"),
    pageViewsTotal: count('page_views'),
    failedLoginsToday: count('login_attempts', "success = 0 AND date(created_at) = date('now')"),
    lockedAccounts: count('admin_users', "locked_until IS NOT NULL AND datetime(locked_until) > datetime('now')"),
    draftInvoices: count('invoices', "status = 'draft'")
  });
});

// ---------------------------------------------------------------------------
// SECURITY — audit trail + login attempts + manual unlock. This is the
// visible half of the "punishment" system: admins can see who tried, and
// who is currently locked out, and lift a lockout early if it was them.
// ---------------------------------------------------------------------------
router.get('/audit-log', requireAdmin, (req, res) => {
  const limit = Math.min(Number(req.query.limit) || 60, 200);
  const rows = db.prepare('SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT ?').all(limit);
  res.json(rows);
});

router.get('/security/login-attempts', requireAdmin, (req, res) => {
  const limit = Math.min(Number(req.query.limit) || 60, 200);
  const rows = db.prepare('SELECT * FROM login_attempts ORDER BY created_at DESC LIMIT ?').all(limit);
  res.json(rows);
});

router.get('/security/accounts', requireAdmin, (req, res) => {
  const rows = db.prepare(`
    SELECT id, username, role, failed_attempts, locked_until, last_login_at, last_login_ip, created_at
    FROM admin_users ORDER BY id ASC
  `).all();
  res.json(rows.map((u) => ({ ...u, isLocked: !!(u.locked_until && new Date(u.locked_until).getTime() > Date.now()) })));
});

router.post('/security/unlock/:id', requireAdmin, (req, res) => {
  const user = db.prepare('SELECT * FROM admin_users WHERE id = ?').get(req.params.id);
  if (!user) return res.status(404).json({ error: 'Account not found.' });
  db.prepare('UPDATE admin_users SET failed_attempts = 0, locked_until = NULL WHERE id = ?').run(user.id);
  logAudit(req, 'unlock', 'admin_users', user.id, `Lockout cleared by ${req.session.username}`);
  res.json({ ok: true });
});

module.exports = router;
