const jwt = require('jsonwebtoken');
const db = require('../db/database');
const { hashToken } = require('../utils/sessions');
const { isMobileClient } = require('../utils/platform');

// Sliding idle timeout — web only. Mobile stays persistent (no idle check,
// 365-day JWT in Keychain/Keystore — user only logs out on manual action,
// password change, admin revoke, or app uninstall).
const WEB_IDLE_TIMEOUT_MS = 24 * 60 * 60 * 1000;    // 24h since last request
const ACTIVITY_THROTTLE_MS = 10 * 60 * 1000;        // throttle DB writes to ≤ 1 per 10 min / user
const COOKIE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000; // cookie hard expiry — session table drives actual session

if (!process.env.JWT_SECRET) {
  if (process.env.NODE_ENV === 'production' || process.env.APP_URL) {
    console.error('[FATAL] JWT_SECRET is not set. Refusing to start in production.');
    process.exit(1);
  }
  console.warn('[WARN] JWT_SECRET not set — using development fallback. DO NOT use in production.');
}
const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-change-in-production';

async function authMiddleware(req, res, next) {
  var token = null;

  var authHeader = req.headers.authorization;
  if (authHeader && authHeader.startsWith('Bearer ')) {
    token = authHeader.substring(7) || null;
  }
  if (!token && req.cookies && req.cookies.ped_auth) {
    token = req.cookies.ped_auth;
  }

  if (!token) {
    return res.status(401).json({ error: 'Authentication required' });
  }

  try {
    var decoded = jwt.verify(token, JWT_SECRET);
    var user = await db.get('SELECT id, email, name, role, totp_enabled, disabled FROM users WHERE id = ?', [decoded.userId]);

    if (!user) {
      return res.status(401).json({ error: 'User not found' });
    }

    if (user.disabled) {
      return res.status(403).json({ error: 'Account disabled. Contact administrator.' });
    }

    req.user = user;

    // Session validation — allows revocation + sliding idle timeout
    try {
      var tHash = hashToken(token);
      var session = await db.get('SELECT id, last_activity FROM user_sessions WHERE token_hash = ?', [tHash]);
      if (!session) {
        // Pre-migration user? If they have no sessions at all, allow through
        var sessCount = await db.get('SELECT COUNT(*) as count FROM user_sessions WHERE user_id = ?', [user.id]);
        if (parseInt(sessCount.count) > 0) {
          return res.status(401).json({ error: 'Session revoked' });
        }
      } else {
        req.sessionId = session.id;
        var now = Date.now();
        var lastAct = new Date(session.last_activity).getTime();
        var idleMs = now - lastAct;
        var mobile = isMobileClient(req);

        // Sliding idle timeout — web only. Mobile stays persistent.
        if (!mobile && idleMs > WEB_IDLE_TIMEOUT_MS) {
          var idleMinutes = Math.round(idleMs / 60000);
          console.warn('[Auth] session expired (idle ' + idleMinutes + ' min) user=' + user.id + ' ip=' + req.ip);
          db.run(
            'INSERT INTO audit_log (user_id, action, category, details, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?)',
            [user.id, 'session_idle_timeout', 'auth', 'Idle ' + idleMinutes + ' minutes', req.ip, (req.headers['user-agent'] || '').substring(0, 255)]
          ).catch(function(){});
          db.run('DELETE FROM user_sessions WHERE id = ?', [session.id]).catch(function(){});
          res.clearCookie('ped_auth', { path: '/' });
          return res.status(401).json({ error: 'Session expired due to inactivity', idleTimeout: true });
        }

        // Only state-changing HTTP methods count as "user activity" for
        // sliding-idle purposes. GET requests are often automated (poll
        // loops, dashboards, /api/auth/me heartbeats) and would otherwise
        // keep a session alive forever, defeating the 24h idle policy.
        var isWriteMethod = req.method === 'POST' || req.method === 'PUT'
          || req.method === 'DELETE' || req.method === 'PATCH';
        if (isWriteMethod && idleMs > ACTIVITY_THROTTLE_MS) {
          db.run('UPDATE user_sessions SET last_activity = NOW() WHERE id = ?', [session.id]).catch(function() {});
          // Sliding cookie refresh (web only) — extend browser-side lifetime too
          if (!mobile && req.cookies && req.cookies.ped_auth) {
            var isProd = process.env.NODE_ENV === 'production' || !!process.env.APP_URL;
            res.cookie('ped_auth', token, {
              httpOnly: true,
              secure: !!isProd,
              sameSite: 'lax',
              maxAge: COOKIE_MAX_AGE_MS,
              path: '/'
            });
          }
        }
      }
    } catch (sessErr) {
      // Fail open — table may not exist yet during migration
    }

    next();
  } catch (err) {
    return res.status(401).json({ error: 'Invalid or expired token' });
  }
}

// Admin-only middleware — use after authMiddleware
async function adminMiddleware(req, res, next) {
  if (!req.user || req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  next();
}

// Moderator or Admin middleware — for Learning Hub CMS
async function moderatorMiddleware(req, res, next) {
  if (!req.user || (req.user.role !== 'admin' && req.user.role !== 'moderator')) {
    return res.status(403).json({ error: 'Moderator or admin access required' });
  }
  next();
}

module.exports = { authMiddleware, adminMiddleware, moderatorMiddleware, JWT_SECRET };
