/**
 * TASKMATE - Auth Middleware
 * ==========================
 * JWT-basierte Authentifizierung
 */

const jwt = require('jsonwebtoken');
const logger = require('../utils/logger');

const JWT_SECRET = process.env.JWT_SECRET || 'UNSICHER_BITTE_AENDERN';
const SESSION_TIMEOUT = parseInt(process.env.SESSION_TIMEOUT) || 30; // Minuten

/**
 * JWT-Token generieren
 */
function generateToken(user) {
  // Permissions parsen falls als String gespeichert
  let permissions = user.permissions || [];
  if (typeof permissions === 'string') {
    try {
      permissions = JSON.parse(permissions);
    } catch (e) {
      permissions = [];
    }
  }

  return jwt.sign(
    {
      id: user.id,
      username: user.username,
      displayName: user.display_name,
      color: user.color,
      role: user.role || 'user',
      permissions: permissions
    },
    JWT_SECRET,
    { expiresIn: `${SESSION_TIMEOUT}m` }
  );
}

/**
 * JWT-Token verifizieren
 */
function verifyToken(token) {
  try {
    return jwt.verify(token, JWT_SECRET);
  } catch (error) {
    return null;
  }
}

/**
 * Express Middleware: Token aus Header, Cookie oder Query-Parameter prüfen
 */
function authenticateToken(req, res, next) {
  // Token aus Authorization Header, Cookie oder Query-Parameter (für img src etc.)
  let token = null;

  const authHeader = req.headers['authorization'];
  if (authHeader && authHeader.startsWith('Bearer ')) {
    token = authHeader.substring(7);
  } else if (req.cookies && req.cookies.token) {
    token = req.cookies.token;
  } else if (req.query && req.query.token) {
    // Token aus Query-Parameter (für Ressourcen die in img/video tags geladen werden)
    token = req.query.token;
  }

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

  const user = verifyToken(token);
  if (!user) {
    return res.status(401).json({ error: 'Token ungültig oder abgelaufen' });
  }

  // User-Info an Request anhängen
  req.user = user;

  // Token-Refresh: Wenn Token bald ablaeuft, neuen ausstellen
  const tokenExp = user.exp * 1000; // exp ist in Sekunden
  const now = Date.now();
  const refreshThreshold = 5 * 60 * 1000; // 5 Minuten vor Ablauf

  if (tokenExp - now < refreshThreshold) {
    const newToken = generateToken({
      id: user.id,
      username: user.username,
      display_name: user.displayName,
      color: user.color,
      role: user.role,
      permissions: user.permissions
    });
    res.setHeader('X-New-Token', newToken);
  }

  next();
}

/**
 * Middleware: Nur Admins erlauben
 */
function requireAdmin(req, res, next) {
  if (!req.user) {
    return res.status(401).json({ error: 'Nicht authentifiziert' });
  }

  if (req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Keine Admin-Berechtigung' });
  }

  next();
}

/**
 * Middleware: Nur regulaere User erlauben (blockiert Admins)
 */
function requireRegularUser(req, res, next) {
  if (!req.user) {
    return res.status(401).json({ error: 'Nicht authentifiziert' });
  }

  if (req.user.role === 'admin') {
    return res.status(403).json({ error: 'Admin hat keinen Zugang zur regulaeren App' });
  }

  next();
}

/**
 * Middleware-Factory: Bestimmte Berechtigung pruefen
 */
function checkPermission(permission) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Nicht authentifiziert' });
    }

    const permissions = req.user.permissions || [];
    if (!permissions.includes(permission)) {
      return res.status(403).json({ error: `Berechtigung "${permission}" fehlt` });
    }

    next();
  };
}

/**
 * Socket.io Middleware: Token prüfen
 */
function authenticateSocket(socket, next) {
  const token = socket.handshake.auth.token ||
    socket.handshake.headers.authorization?.replace('Bearer ', '');

  if (!token) {
    return next(new Error('Nicht authentifiziert'));
  }

  const user = verifyToken(token);
  if (!user) {
    return next(new Error('Token ungültig oder abgelaufen'));
  }

  // User-Info an Socket anhängen
  socket.user = user;
  next();
}

/**
 * CSRF-Token generieren (für Forms)
 */
function generateCsrfToken() {
  const { randomBytes } = require('crypto');
  return randomBytes(32).toString('hex');
}

module.exports = {
  generateToken,
  verifyToken,
  authenticateToken,
  authenticateSocket,
  generateCsrfToken,
  requireAdmin,
  requireRegularUser,
  checkPermission,
  JWT_SECRET,
  SESSION_TIMEOUT
};
