import { Request, Response, NextFunction } from 'express';
import { RBACService } from '../services/rbac.service';

export interface AuthorizedRequest extends Request {
  user?: {
    id: string;
    email: string;
    subscription: string;
    roles: string[];
    [key: string]: any;
  };
}

/**
 * Middleware to check if user has required permission
 */
export function requirePermission(permissionId: string, projectIdParam?: string) {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const userId = req.user.id;
    let projectId: string | undefined;
    
    // Extract project ID from request parameters if specified
    if (projectIdParam) {
      projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
    }
    
    // Check if user has the required permission
    if (!RBACService.hasPermission(userId, permissionId, projectId)) {
      return res.status(403).json({ 
        error: 'Insufficient permissions',
        required: permissionId,
        projectId: projectId || 'global'
      });
    }
    
    next();
  };
}

/**
 * Middleware to check if user has any of the required permissions
 */
export function requireAnyPermission(permissionIds: string[], projectIdParam?: string) {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const userId = req.user.id;
    let projectId: string | undefined;
    
    // Extract project ID from request parameters if specified
    if (projectIdParam) {
      projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
    }
    
    // Check if user has any of the required permissions
    if (!RBACService.hasAnyPermission(userId, permissionIds, projectId)) {
      return res.status(403).json({ 
        error: 'Insufficient permissions',
        required: `Any of: ${permissionIds.join(', ')}`,
        projectId: projectId || 'global'
      });
    }
    
    next();
  };
}

/**
 * Middleware to check if user has all of the required permissions
 */
export function requireAllPermissions(permissionIds: string[], projectIdParam?: string) {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const userId = req.user.id;
    let projectId: string | undefined;
    
    // Extract project ID from request parameters if specified
    if (projectIdParam) {
      projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
    }
    
    // Check if user has all of the required permissions
    if (!RBACService.hasAllPermissions(userId, permissionIds, projectId)) {
      return res.status(403).json({ 
        error: 'Insufficient permissions',
        required: `All of: ${permissionIds.join(', ')}`,
        projectId: projectId || 'global'
      });
    }
    
    next();
  };
}

/**
 * Middleware to check if user is admin
 */
export function requireAdmin() {
  return requirePermission('admin:system');
}

/**
 * Middleware to check project access
 */
export function requireProjectAccess(action: 'read' | 'update' | 'delete' | 'share' = 'read', projectIdParam: string = 'projectId') {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const userId = req.user.id;
    const projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
    
    if (!projectId) {
      return res.status(400).json({ error: 'Project ID required' });
    }
    
    // Check if user can access the project
    if (!RBACService.canAccessProject(userId, projectId, action)) {
      return res.status(403).json({ 
        error: 'Project access denied',
        action,
        projectId
      });
    }
    
    next();
  };
}

/**
 * Middleware to check if user owns the resource or is admin
 */
export function requireOwnershipOrAdmin(ownerIdParam: string = 'userId') {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const userId = req.user.id;
    const resourceOwnerId = req.params[ownerIdParam] || req.body[ownerIdParam] || req.query[ownerIdParam] as string;
    
    // Allow if user is the owner or is admin
    if (userId === resourceOwnerId || RBACService.isAdmin(userId)) {
      return next();
    }
    
    return res.status(403).json({ 
      error: 'Access denied - ownership or admin privileges required'
    });
  };
}

/**
 * Utility function to get user permissions for response
 */
export function getUserPermissionsForResponse(userId: string, projectId?: string) {
  return {
    permissions: RBACService.getUserPermissions(userId, projectId).map(p => ({
      id: p.id,
      name: p.name,
      resource: p.resource,
      action: p.action
    })),
    roles: RBACService.getUserRoles(userId, projectId).map(ur => ({
      roleId: ur.roleId,
      projectId: ur.projectId,
      grantedAt: ur.grantedAt
    })),
    isAdmin: RBACService.isAdmin(userId)
  };
}

/**
 * Middleware to add user permissions to response
 */
export function addUserPermissions(projectIdParam?: string) {
  return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
    if (req.user) {
      const userId = req.user.id;
      let projectId: string | undefined;
      
      if (projectIdParam) {
        projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
      }
      
      // Add permissions to request for use in route handlers
      (req as any).userPermissions = getUserPermissionsForResponse(userId, projectId);
    }
    
    next();
  };
}