/**
 * MFA Enforcement Middleware
 * 
 * Enforces MFA requirements per DoD/MilSpec standards:
 * - NIST SP 800-53: IA-2 (Identification and Authentication)
 * - NIST SP 800-63B: Digital Identity Guidelines
 * 
 * Requires MFA for:
 * - All privileged operations
 * - Access to classified data
 * - Administrative actions
 * - Security-sensitive operations
 */

import { FastifyRequest, FastifyReply } from 'fastify'
import { hasMFAEnabled, verifyMFAChallenge } from '../services/mfa'
import { getDb } from '../db'
import { logger } from '../lib/logger'

/**
 * Operations that require MFA
 */
const MFA_REQUIRED_OPERATIONS = [
  // Administrative operations
  'createTenant',
  'updateTenant',
  'deleteTenant',
  'suspendTenant',
  'activateTenant',
  
  // Security operations
  'createUser',
  'updateUser',
  'deleteUser',
  'changePassword',
  'updateRole',
  'grantPermission',
  'revokePermission',
  
  // Resource management
  'createResource',
  'updateResource',
  'deleteResource',
  'provisionVM',
  'destroyVM',
  
  // Billing operations
  'createInvoice',
  'updateBillingAccount',
  'processPayment',
  
  // Compliance operations
  'exportAuditLog',
  'generateComplianceReport',
  'updateSecurityPolicy',
]

/**
 * Check if an operation requires MFA
 */
function requiresMFA(operation: string): boolean {
  return MFA_REQUIRED_OPERATIONS.includes(operation)
}

/**
 * Get operation name from GraphQL request
 */
function getOperationName(request: FastifyRequest): string | null {
  if (request.body && typeof request.body === 'object') {
    const body = request.body as any
    if (body.operationName) {
      return body.operationName
    }
    if (body.query) {
      // Parse GraphQL query to extract operation name
      const match = body.query.match(/(?:mutation|query)\s+(\w+)/)
      if (match) {
        return match[1]
      }
    }
  }
  return null
}

/**
 * MFA Enforcement Middleware
 * Checks if MFA is required and verified for the current request
 */
export async function mfaEnforcementMiddleware(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  // Skip MFA check for health endpoints and WebSocket upgrades
  if (request.url === '/health' || request.url === '/graphql-ws') {
    return
  }

  // Get user from request context (set by auth middleware)
  const user = (request as any).user
  if (!user) {
    // Not authenticated - auth middleware will handle
    return
  }

  // Get operation name
  const operation = getOperationName(request)
  if (!operation || !requiresMFA(operation)) {
    // Operation doesn't require MFA
    return
  }

  // Check if user has MFA enabled
  const mfaEnabled = await hasMFAEnabled(user.id)
  if (!mfaEnabled) {
    logger.warn('MFA required but not enabled', { userId: user.id, operation })
    reply.code(403).send({
      error: 'MFA_REQUIRED',
      message: 'Multi-factor authentication is required for this operation. Please enable MFA in your account settings.',
      operation,
    })
    return
  }

  // Check for MFA challenge verification
  const mfaChallengeId = request.headers['x-mfa-challenge-id'] as string
  const mfaToken = request.headers['x-mfa-token'] as string

  if (!mfaChallengeId || !mfaToken) {
    logger.warn('MFA challenge missing', { userId: user.id, operation })
    reply.code(403).send({
      error: 'MFA_CHALLENGE_REQUIRED',
      message: 'MFA challenge verification required for this operation',
      operation,
    })
    return
  }

  // Verify MFA challenge
  const verified = await verifyMFAChallenge(mfaChallengeId, user.id, mfaToken)
  if (!verified) {
    logger.warn('MFA challenge verification failed', { userId: user.id, operation, challengeId: mfaChallengeId })
    reply.code(403).send({
      error: 'MFA_VERIFICATION_FAILED',
      message: 'MFA verification failed. Please try again.',
      operation,
    })
    return
  }

  // MFA verified - allow request to proceed
  logger.info('MFA verified for operation', { userId: user.id, operation })
}

/**
 * Check if MFA is required for a specific operation
 * Can be used in GraphQL resolvers
 */
export function checkMFARequired(operation: string, userId: string): Promise<boolean> {
  return requiresMFA(operation) ? hasMFAEnabled(userId) : Promise.resolve(false)
}

