/**
 * Security Middleware
 * Implements security headers and protections per DoD/MilSpec standards
 * 
 * Complies with:
 * - DISA STIG: Web Server Security
 * - NIST SP 800-53: SI-4 (Information System Monitoring)
 * - NIST SP 800-171: 3.13.1 (Cryptographic Protection in Transit)
 */

import { FastifyRequest, FastifyReply } from 'fastify'
import { randomBytes } from 'crypto'

/**
 * Add security headers to responses per DoD/MilSpec requirements
 * 
 * Implements comprehensive security headers as required by:
 * - DISA STIG for Web Servers
 * - OWASP Secure Headers Project
 * - DoD Security Technical Implementation Guides
 */
export async function securityHeadersMiddleware(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  // Prevent MIME type sniffing (DISA STIG requirement)
  reply.header('X-Content-Type-Options', 'nosniff')
  
  // Prevent clickjacking attacks (DISA STIG requirement)
  reply.header('X-Frame-Options', 'DENY')
  
  // Legacy XSS protection (deprecated but still recommended for older browsers)
  reply.header('X-XSS-Protection', '1; mode=block')
  
  // HTTP Strict Transport Security (HSTS) with preload
  // max-age: 1 year (31536000 seconds)
  // includeSubDomains: Apply to all subdomains
  // preload: Allow inclusion in HSTS preload lists
  const hstsMaxAge = 31536000 // 1 year
  reply.header('Strict-Transport-Security', `max-age=${hstsMaxAge}; includeSubDomains; preload`)
  
  // Content Security Policy (CSP) per STIG requirements
  // Strict CSP to prevent XSS and injection attacks
  // Generate nonce for inline scripts/styles to avoid unsafe-inline
  const nonce = randomBytes(16).toString('base64')
  // Store nonce in request for use in templates
  ;(request as any).cspNonce = nonce
  
  const csp = [
    "default-src 'self'",
    `script-src 'self' 'nonce-${nonce}'`, // Use nonce instead of unsafe-inline
    `style-src 'self' 'nonce-${nonce}'`, // Use nonce instead of unsafe-inline
    "img-src 'self' data: https:",
    "font-src 'self' data:",
    "connect-src 'self'",
    "frame-ancestors 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "upgrade-insecure-requests",
  ].join('; ')
  reply.header('Content-Security-Policy', csp)
  
  // Referrer Policy - control referrer information leakage
  reply.header('Referrer-Policy', 'strict-origin-when-cross-origin')
  
  // Permissions Policy (formerly Feature Policy) - disable unnecessary features
  reply.header('Permissions-Policy', [
    'geolocation=()',
    'microphone=()',
    'camera=()',
    'payment=()',
    'usb=()',
    'magnetometer=()',
    'gyroscope=()',
    'accelerometer=()',
  ].join(', '))
  
  // X-Permitted-Cross-Domain-Policies - restrict cross-domain policies
  reply.header('X-Permitted-Cross-Domain-Policies', 'none')
  
  // Expect-CT - Certificate Transparency (deprecated but still used)
  // Note: This header is deprecated but may still be required for some compliance
  // reply.header('Expect-CT', 'max-age=86400, enforce')
  
  // Cross-Origin-Embedder-Policy - prevent cross-origin data leakage
  reply.header('Cross-Origin-Embedder-Policy', 'require-corp')
  
  // Cross-Origin-Opener-Policy - isolate browsing context
  reply.header('Cross-Origin-Opener-Policy', 'same-origin')
  
  // Cross-Origin-Resource-Policy - control resource loading
  reply.header('Cross-Origin-Resource-Policy', 'same-origin')
  
  // Remove server information disclosure
  // Note: Fastify doesn't expose server header by default, but we ensure it's not set
  reply.removeHeader('Server')
  reply.removeHeader('X-Powered-By')
}

/**
 * Input sanitization helper
 */
export function sanitizeInput(input: unknown): unknown {
  if (typeof input === 'string') {
    // Remove potentially dangerous characters
    return input
      .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
      .replace(/javascript:/gi, '')
      .replace(/on\w+\s*=/gi, '')
      .trim()
  }

  if (Array.isArray(input)) {
    return input.map(sanitizeInput)
  }

  if (input && typeof input === 'object' && !Array.isArray(input)) {
    const sanitized: Record<string, unknown> = {}
    for (const key in input) {
      if (Object.prototype.hasOwnProperty.call(input, key)) {
        sanitized[key] = sanitizeInput((input as Record<string, unknown>)[key])
      }
    }
    return sanitized
  }

  return input
}

/**
 * Validate and sanitize request body
 */
export async function sanitizeBodyMiddleware(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  if (request.body) {
    request.body = sanitizeInput(request.body)
  }
}

