/**
 * Authentication Middleware for Redirect Intelligence v2
 * 
 * Provides JWT token authentication and user context injection
 */

import { Request, Response, NextFunction } from 'express';
import { AuthService, AuthUser } from '../services/auth.service';
import { logger } from '../lib/logger';

// Extend Request interface to include user
export interface AuthenticatedRequest extends Request {
  user?: AuthUser;
}

export class AuthMiddleware {
  private authService = new AuthService();

  /**
   * Extract token from request headers or cookies
   */
  private extractToken(req: Request): string | null {
    // Try Authorization header first (Bearer token)
    const authHeader = req.headers.authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.substring(7);
    }

    // Try cookie as fallback
    const cookieToken = req.cookies?.auth_token;
    if (cookieToken) {
      return cookieToken;
    }

    return null;
  }

  /**
   * Required authentication middleware
   * Returns 401 if user is not authenticated
   */
  requireAuth = async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    try {
      const token = this.extractToken(req);

      if (!token) {
        logger.warn('Authentication required but no token provided');
        return res.status(401).json({
          success: false,
          error: 'Authentication required',
          message: 'Please provide a valid authentication token'
        });
      }

      // Verify token
      const decoded = this.authService.verifyToken(token);

      // Get user details
      const user = await this.authService.getUserById(decoded.userId);

      if (!user) {
        logger.warn(`Token valid but user not found: ${decoded.userId}`);
        return res.status(401).json({
          success: false,
          error: 'User not found',
          message: 'The authenticated user no longer exists'
        });
      }

      // Attach user to request
      req.user = user;
      
      logger.debug(`User authenticated: ${user.email}`);
      next();

    } catch (error) {
      logger.warn('Authentication failed:', error);
      return res.status(401).json({
        success: false,
        error: 'Invalid token',
        message: 'Please provide a valid authentication token'
      });
    }
  };

  /**
   * Optional authentication middleware
   * Attaches user if token is valid, but doesn't require it
   */
  optionalAuth = async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    try {
      const token = this.extractToken(req);

      if (!token) {
        // No token provided, continue without authentication
        return next();
      }

      // Verify token
      const decoded = this.authService.verifyToken(token);

      // Get user details
      const user = await this.authService.getUserById(decoded.userId);

      if (user) {
        req.user = user;
        logger.debug(`Optional auth successful: ${user.email}`);
      }

    } catch (error) {
      // Token invalid, but that's okay for optional auth
      logger.debug('Optional auth failed (continuing):', error);
    }

    next();
  };

  /**
   * Require specific role in organization
   */
  requireRole = (orgIdParam: string, requiredRole: string) => {
    return async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
      if (!req.user) {
        return res.status(401).json({
          success: false,
          error: 'Authentication required'
        });
      }

      // Get organization ID from request params
      const orgId = req.params[orgIdParam] || req.body[orgIdParam];

      if (!orgId) {
        return res.status(400).json({
          success: false,
          error: 'Organization ID required'
        });
      }

      // Check if user has the required role
      const userRole = await this.authService.getUserRole(req.user.id, orgId);

      if (!userRole) {
        return res.status(403).json({
          success: false,
          error: 'Access denied',
          message: 'You are not a member of this organization'
        });
      }

      // Define role hierarchy
      const roleHierarchy = {
        'MEMBER': 1,
        'ADMIN': 2,
        'OWNER': 3,
      };

      const userRoleLevel = roleHierarchy[userRole as keyof typeof roleHierarchy] || 0;
      const requiredRoleLevel = roleHierarchy[requiredRole as keyof typeof roleHierarchy] || 999;

      if (userRoleLevel < requiredRoleLevel) {
        return res.status(403).json({
          success: false,
          error: 'Insufficient permissions',
          message: `This action requires ${requiredRole} role or higher`
        });
      }

      next();
    };
  };

  /**
   * Check organization access
   */
  requireOrgAccess = (orgIdParam: string) => {
    return async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
      if (!req.user) {
        return res.status(401).json({
          success: false,
          error: 'Authentication required'
        });
      }

      const orgId = req.params[orgIdParam] || req.body[orgIdParam];

      if (!orgId) {
        return res.status(400).json({
          success: false,
          error: 'Organization ID required'
        });
      }

      const hasAccess = await this.authService.hasOrgAccess(req.user.id, orgId);

      if (!hasAccess) {
        return res.status(403).json({
          success: false,
          error: 'Access denied',
          message: 'You do not have access to this organization'
        });
      }

      next();
    };
  };
}

// Export singleton instance
export const authMiddleware = new AuthMiddleware();

// Export individual middleware functions for convenience
export const requireAuth = authMiddleware.requireAuth;
export const optionalAuth = authMiddleware.optionalAuth;
export const requireRole = authMiddleware.requireRole;
export const requireOrgAccess = authMiddleware.requireOrgAccess;
