import { Request, Response, NextFunction } from 'express';
import { Participant } from '@models/Participant';
import { WorkflowRequest } from '@models/WorkflowRequest';
import { Op } from 'sequelize';

type AllowedType = 'INITIATOR' | 'APPROVER' | 'SPECTATOR';

// Helper to check if identifier is UUID or requestNumber
function isUuid(identifier: string): boolean {
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return uuidRegex.test(identifier);
}

// Helper to find workflow by either requestId or requestNumber
async function findWorkflowByIdentifier(identifier: string): Promise<WorkflowRequest | null> {
  if (isUuid(identifier)) {
    return await WorkflowRequest.findByPk(identifier);
  } else {
    return await WorkflowRequest.findOne({
      where: { requestNumber: identifier }
    });
  }
}

export function requireParticipantTypes(allowed: AllowedType[]) {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      const userId: string | undefined = (req as any).user?.userId || (req as any).user?.id;
      const requestIdentifier: string | undefined = (req.params as any)?.id;
      if (!userId || !requestIdentifier) {
        return res.status(403).json({ success: false, error: 'Forbidden' });
      }

      // Resolve requestIdentifier to actual requestId (UUID)
      const workflow = await findWorkflowByIdentifier(requestIdentifier);
      if (!workflow) {
        return res.status(404).json({ success: false, error: 'Workflow not found' });
      }
      const actualRequestId = (workflow as any).requestId;

      // Check initiator
      if (allowed.includes('INITIATOR')) {
        if ((workflow as any).initiatorId === userId) {
          return next();
        }
      }

      // Check participants table for SPECTATOR
      if (allowed.includes('SPECTATOR')) {
        const participant = await Participant.findOne({
          where: {
            requestId: actualRequestId,
            userId,
            participantType: 'SPECTATOR',
            isActive: true
          },
        });
        if (participant) {
          return next();
        }
      }

      // For APPROVER role, check ApprovalLevel table
      // This is the primary source of truth for approvers
      if (allowed.includes('APPROVER')) {
        const { ApprovalLevel } = await import('@models/ApprovalLevel');
        const approvalLevel = await ApprovalLevel.findOne({
          where: {
            requestId: actualRequestId,
            approverId: userId,
            status: { [Op.in]: ['PENDING', 'IN_PROGRESS'] as any }
          }
        });
        if (approvalLevel) {
          return next();
        }

        // Fallback: also check Participants table (some approvers might be added there)
        const participant = await Participant.findOne({
          where: {
            requestId: actualRequestId,
            userId,
            participantType: 'APPROVER',
            isActive: true
          },
        });
        if (participant) {
          return next();
        }
      }

      return res.status(403).json({ success: false, error: 'Insufficient permissions' });
    } catch (err) {
      console.error('❌ Authorization check failed:', err);
      return res.status(500).json({ success: false, error: 'Authorization check failed' });
    }
  };
}

/**
 * Role-Based Access Control (RBAC) Middleware
 * 
 * Roles:
 * - USER: Default role - can create/view own requests, participate in assigned workflows
 * - MANAGEMENT: Read access to all requests, enhanced dashboard visibility
 * - ADMIN: Full system access - configuration, user management, all workflows
 */

/**
 * Middleware: requireAdmin
 * 
 * Purpose: Restrict access to ADMIN role only
 * 
 * Use Cases:
 * - System configuration management
 * - User role assignment
 * - Holiday calendar management
 * - Email/notification settings
 * - Audit log access
 * 
 * Returns: 403 Forbidden if user is not ADMIN
 */
export function requireAdmin(req: Request, res: Response, next: NextFunction): void {
  try {
    const userRole = req.user?.role;

    if (userRole !== 'ADMIN') {
      res.status(403).json({
        success: false,
        error: 'Admin access required. Only administrators can perform this action.'
      });
      return;
    }

    next();
  } catch (error) {
    console.error('❌ Admin authorization failed:', error);
    res.status(500).json({
      success: false,
      error: 'Authorization check failed'
    });
  }
}

/**
 * Middleware: requireManagement
 * 
 * Purpose: Restrict access to MANAGEMENT and ADMIN roles
 * 
 * Use Cases:
 * - View all requests (read-only)
 * - Access comprehensive dashboards
 * - Export reports
 * - View analytics across all departments
 * 
 * Permissions:
 * - MANAGEMENT: Read access to all data
 * - ADMIN: Read + Write access
 * 
 * Returns: 403 Forbidden if user is only USER role
 */
export function requireManagement(req: Request, res: Response, next: NextFunction): void {
  try {
    const userRole = req.user?.role;

    if (userRole !== 'MANAGEMENT' && userRole !== 'ADMIN') {
      res.status(403).json({
        success: false,
        error: 'Management access required. This feature is available to management and admin users only.'
      });
      return;
    }

    next();
  } catch (error) {
    console.error('❌ Management authorization failed:', error);
    res.status(500).json({
      success: false,
      error: 'Authorization check failed'
    });
  }
}

/**
 * Middleware: requireRole
 * 
 * Purpose: Flexible role checking - accepts multiple allowed roles
 * 
 * Example Usage:
 * - requireRole(['ADMIN']) - Admin only
 * - requireRole(['MANAGEMENT', 'ADMIN']) - Management or Admin
 * - requireRole(['USER', 'MANAGEMENT', 'ADMIN']) - Any authenticated user
 * 
 * @param allowedRoles - Array of allowed role strings
 * @returns Express middleware function
 */
export function requireRole(allowedRoles: ('USER' | 'MANAGEMENT' | 'ADMIN')[]) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      const userRole = req.user?.role;

      if (!userRole || !allowedRoles.includes(userRole as any)) {
        res.status(403).json({
          success: false,
          error: `Access denied. Required roles: ${allowedRoles.join(' or ')}`
        });
        return;
      }

      next();
    } catch (error) {
      console.error('❌ Role authorization failed:', error);
      res.status(500).json({
        success: false,
        error: 'Authorization check failed'
      });
    }
  };
}

/**
 * Helper: Check if user has specific role
 * 
 * Purpose: Programmatic role checking within controllers
 * 
 * @param user - Express req.user object
 * @param role - Role to check
 * @returns boolean
 */
export function hasRole(user: any, role: 'USER' | 'MANAGEMENT' | 'ADMIN'): boolean {
  return user?.role === role;
}

/**
 * Helper: Check if user has management or admin access
 * 
 * Purpose: Quick check for enhanced permissions
 * 
 * @param user - Express req.user object
 * @returns boolean
 */
export function hasManagementAccess(user: any): boolean {
  return user?.role === 'MANAGEMENT' || user?.role === 'ADMIN';
}

/**
 * Helper: Check if user has admin access
 * 
 * Purpose: Quick check for admin-only permissions
 * 
 * @param user - Express req.user object
 * @returns boolean
 */
export function hasAdminAccess(user: any): boolean {
  return user?.role === 'ADMIN';
}


