/**
 * Authentication Middleware
 * Handles JWT token authentication and authorization
 */

// Import jsonwebtoken for token verification
const jwt = require('jsonwebtoken');
// Import app configuration
const appConfig = require('../config/app');
// Import response utility
const response = require('../utils/response');
// Import custom error classes
const { UnauthorizedError } = require('../utils/errors');

/**
 * Verify JWT token middleware
 * Validates JWT token from request headers
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 * @param {Function} next - Express next middleware function
 */
const authenticate = (req, res, next) => {
  try {
    // Get authorization header
    const authHeader = req.headers.authorization;
    
    // Check if authorization header exists
    if (!authHeader) {
      throw new UnauthorizedError('No token provided'); // Throw unauthorized error
    }
    
    // Extract token from "Bearer <token>" format
    const token = authHeader.split(' ')[1]; // Get token part after "Bearer "
    
    // Verify JWT token
    const decoded = jwt.verify(token, appConfig.jwt.secret);
    
    // Attach decoded user information to request object
    req.user = decoded; // Store user data in request
    
    // Proceed to next middleware
    next();
  } catch (error) {
    // Handle token verification errors
    if (error.name === 'JsonWebTokenError') {
      // Invalid token
      return response.unauthorized(res, 'Invalid token');
    }
    if (error.name === 'TokenExpiredError') {
      // Expired token
      return response.unauthorized(res, 'Token expired');
    }
    // Other errors
    return response.unauthorized(res, error.message || 'Authentication failed');
  }
};

/**
 * Optional authentication middleware
 * Authenticates if token is provided, but doesn't fail if missing
 * Useful for endpoints that work with or without authentication
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 * @param {Function} next - Express next middleware function
 */
const optionalAuth = (req, res, next) => {
  try {
    // Get authorization header
    const authHeader = req.headers.authorization;
    
    // If no authorization header, proceed without authentication
    if (!authHeader) {
      return next(); // Continue without authentication
    }
    
    // Extract token from "Bearer <token>" format
    const token = authHeader.split(' ')[1];
    
    // Verify JWT token if provided
    const decoded = jwt.verify(token, appConfig.jwt.secret);
    
    // Attach decoded user information to request object
    req.user = decoded; // Store user data in request
    
    // Proceed to next middleware
    next();
  } catch (error) {
    // If token is invalid, proceed without authentication (don't fail)
    next(); // Continue even if token is invalid
  }
};

/**
 * Require specific role middleware
 * Checks if user has required role
 * @param {string|Array} roles - Required role(s) - single role string or array of roles
 * @returns {Function} Express middleware function
 */
const requireRole = (roles) => {
  return (req, res, next) => {
    // Check if user is authenticated
    if (!req.user) {
      return response.unauthorized(res, 'Authentication required');
    }
    
    // Convert single role to array for consistency
    const requiredRoles = Array.isArray(roles) ? roles : [roles];
    
    // Get user role from request (set by authenticate middleware)
    const userRole = req.user.role;
    
    // Check if user has required role
    if (!requiredRoles.includes(userRole)) {
      return response.forbidden(res, `Access denied. Required role: ${requiredRoles.join(' or ')}`);
    }
    
    // If user has required role, proceed to next middleware
    next();
  };
};

/**
 * Require any of the specified roles middleware
 * Checks if user has at least one of the required roles
 * @param {Array} roles - Array of allowed roles
 * @returns {Function} Express middleware function
 */
const requireAnyRole = (roles) => {
  return (req, res, next) => {
    // Check if user is authenticated
    if (!req.user) {
      return response.unauthorized(res, 'Authentication required');
    }
    
    // Ensure roles is an array
    if (!Array.isArray(roles) || roles.length === 0) {
      return response.forbidden(res, 'Invalid role configuration');
    }
    
    // Get user role from request (set by authenticate middleware)
    const userRole = req.user.role;
    
    // Check if user has any of the required roles
    if (!roles.includes(userRole)) {
      return response.forbidden(res, `Access denied. Required role: ${roles.join(' or ')}`);
    }
    
    // If user has one of the required roles, proceed to next middleware
    next();
  };
};

// Export authentication middlewares
module.exports = {
  authenticate, // Required authentication middleware
  optionalAuth, // Optional authentication middleware
  requireRole, // Require specific role middleware
  requireAnyRole, // Require any of specified roles middleware
};
