import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { rbacService, Permission, UserContext } from '../services/rbac.service';

// Extend Express Request to include user context
declare global {
  namespace Express {
    interface Request {
      user?: UserContext;
      userId?: string;
    }
  }
}

/**
 * JWT Payload Interface
 */
interface JWTPayload {
  id: string;
  email: string;
  role?: string;
  iat?: number;
  exp?: number;
}

/**
 * Extract and verify JWT token
 */
export const authenticateToken = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Get token from Authorization header
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

    if (!token) {
      res.status(401).json({
        success: false,
        error: 'Access token required'
      });
      return;
    }

    // Verify token
    const secret = process.env.JWT_SECRET || 'default-secret-change-in-production';
    const payload = jwt.verify(token, secret) as JWTPayload;

    // Get user context from RBAC service
    const userContext = await rbacService.getUserContext(payload.id);
    
    // Attach user context to request
    req.user = userContext;
    req.userId = payload.id;

    next();
  } catch (error: any) {
    if (error.name === 'TokenExpiredError') {
      res.status(401).json({
        success: false,
        error: 'Token expired'
      });
      return;
    }
    
    if (error.name === 'JsonWebTokenError') {
      res.status(401).json({
        success: false,
        error: 'Invalid token'
      });
      return;
    }

    res.status(500).json({
      success: false,
      error: 'Authentication failed'
    });
  }
};

/**
 * Require specific permission
 */
export const requirePermission = (permission: Permission) => {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      // Ensure user is authenticated
      if (!req.user || !req.userId) {
        res.status(401).json({
          success: false,
          error: 'Authentication required'
        });
        return;
      }

      // Check if user has the required permission
      const hasPermission = await rbacService.hasPermission(req.userId, permission);

      if (!hasPermission) {
        res.status(403).json({
          success: false,
          error: `Permission denied: ${permission} required`
        });
        return;
      }

      next();
    } catch (error) {
      res.status(500).json({
        success: false,
        error: 'Permission check failed'
      });
    }
  };
};

/**
 * Require any of the specified permissions
 */
export const requireAnyPermission = (permissions: Permission[]) => {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      // Ensure user is authenticated
      if (!req.user || !req.userId) {
        res.status(401).json({
          success: false,
          error: 'Authentication required'
        });
        return;
      }

      // Check if user has any of the required permissions
      const hasPermission = await rbacService.hasAnyPermission(req.userId, permissions);

      if (!hasPermission) {
        res.status(403).json({
          success: false,
          error: `Permission denied: One of [${permissions.join(', ')}] required`
        });
        return;
      }

      next();
    } catch (error) {
      res.status(500).json({
        success: false,
        error: 'Permission check failed'
      });
    }
  };
};

/**
 * Require all of the specified permissions
 */
export const requireAllPermissions = (permissions: Permission[]) => {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      // Ensure user is authenticated
      if (!req.user || !req.userId) {
        res.status(401).json({
          success: false,
          error: 'Authentication required'
        });
        return;
      }

      // Check if user has all of the required permissions
      const hasPermission = await rbacService.hasAllPermissions(req.userId, permissions);

      if (!hasPermission) {
        res.status(403).json({
          success: false,
          error: `Permission denied: All of [${permissions.join(', ')}] required`
        });
        return;
      }

      next();
    } catch (error) {
      res.status(500).json({
        success: false,
        error: 'Permission check failed'
      });
    }
  };
};

/**
 * Check task access permission
 */
export const requireTaskAccess = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Ensure user is authenticated
    if (!req.user || !req.userId) {
      res.status(401).json({
        success: false,
        error: 'Authentication required'
      });
      return;
    }

    // Get task ID from params or body
    const taskId = req.params.id || req.params.taskId || req.body.taskId;

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

    // Check if user can access the task
    const canAccess = await rbacService.canAccessTask(req.userId, taskId);

    if (!canAccess) {
      res.status(403).json({
        success: false,
        error: 'Access denied to this task'
      });
      return;
    }

    next();
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Task access check failed'
    });
  }
};

/**
 * Check task modification permission
 */
export const requireTaskModify = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Ensure user is authenticated
    if (!req.user || !req.userId) {
      res.status(401).json({
        success: false,
        error: 'Authentication required'
      });
      return;
    }

    // Get task ID from params or body
    const taskId = req.params.id || req.params.taskId || req.body.taskId;

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

    // Check if user can modify the task
    const canModify = await rbacService.canModifyTask(req.userId, taskId);

    if (!canModify) {
      res.status(403).json({
        success: false,
        error: 'Permission denied to modify this task'
      });
      return;
    }

    next();
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Task modification check failed'
    });
  }
};

/**
 * Optional authentication - doesn't fail if no token
 */
export const optionalAuth = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Get token from Authorization header
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1];

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

    // Verify token
    const secret = process.env.JWT_SECRET || 'default-secret-change-in-production';
    const payload = jwt.verify(token, secret) as JWTPayload;

    // Get user context from RBAC service
    const userContext = await rbacService.getUserContext(payload.id);
    
    // Attach user context to request
    req.user = userContext;
    req.userId = payload.id;

    next();
  } catch (error) {
    // Token verification failed, continue without user context
    next();
  }
};