// Zero-Trust Authentication Middleware

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { sovereignIdentityFabric } from '@/sovereign/identity/sovereign-identity-fabric.service';
import { hsmService } from '@/integration/hsm/hsm.service';
import { DbisError, ErrorCode, JwtPayload } from '@/shared/types';
import { logger } from '@/infrastructure/monitoring/logger';
import { getEnv } from '@/shared/config/env-validator';

export interface AuthenticatedRequest extends Request {
  sovereignBankId?: string;
  employeeId?: string;
  email?: string;
  name?: string;
  roleName?: string;
  permissions?: string[];
  sessionType?: 'portal' | 'service';
  portalSurface?: 'admin' | 'member' | 'core';
  identityType?: string;
  apiRole?: string;
}

function isPortalSession(payload: JwtPayload): boolean {
  return payload.sessionType === 'portal' || payload.identityType === 'WEB_PORTAL';
}

/**
 * Extract Sovereign Identity Token (SIT) from Authorization header
 */
export function extractSitToken(req: Request): string | null {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('SOV-TOKEN ')) {
    return null;
  }
  return authHeader.substring(10);
}

/**
 * Verify request signature (X-SOV-SIGNATURE)
 */
export async function verifyRequestSignature(
  req: Request,
  sovereignBankId: string,
  identityType: string
): Promise<boolean> {
  const signature = req.headers['x-sov-signature'] as string;
  const timestamp = req.headers['x-sov-timestamp'] as string;
  const nonce = req.headers['x-sov-nonce'] as string;

  if (!signature || !timestamp || !nonce) {
    logger.warn('Missing request signature headers', {
      sovereignBankId,
      identityType,
      hasSignature: !!signature,
      hasTimestamp: !!timestamp,
      hasNonce: !!nonce,
    });
    return false;
  }

  // Validate timestamp (prevent replay attacks)
  const requestTime = parseInt(timestamp, 10);
  const currentTime = Date.now();
  const timeDiff = Math.abs(currentTime - requestTime);
  const maxTimeDiff = 5 * 60 * 1000; // 5 minutes

  if (isNaN(requestTime) || timeDiff > maxTimeDiff) {
    logger.warn('Invalid or expired request timestamp', {
      sovereignBankId,
      timestamp,
      timeDiff: `${timeDiff}ms`,
    });
    return false;
  }

  // Create payload for signing (must match client-side signing)
  const payload = JSON.stringify({
    method: req.method,
    path: req.path,
    body: req.body,
    timestamp,
    nonce,
  });

  try {
    // Get sovereign identity to retrieve HSM key ID
    const identity = await sovereignIdentityFabric.getIdentity(sovereignBankId, identityType);
    if (!identity || !identity.hsmKeyId) {
      logger.error('Sovereign identity not found or missing HSM key', {
        sovereignBankId,
        identityType,
      });
      return false;
    }

    // Verify signature using HSM
    const isValid = await hsmService.verify(payload, signature, identity.hsmKeyId);
    
    if (!isValid) {
      logger.warn('Invalid request signature', {
        sovereignBankId,
        identityType,
        keyId: identity.hsmKeyId,
      });
    }

    return isValid;
  } catch (error) {
    logger.error('Error verifying request signature', {
      sovereignBankId,
      identityType,
      error: error instanceof Error ? error.message : 'Unknown error',
    });
    return false;
  }
}

/**
 * Zero-Trust Authentication Middleware
 * Enforces authentication and authorization for all requests
 */
export async function zeroTrustAuthMiddleware(
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    // Extract SIT token
    const token = extractSitToken(req);
    if (!token) {
      throw new DbisError(
        ErrorCode.UNAUTHORIZED,
        'Missing or invalid Sovereign Identity Token'
      );
    }

    // Verify JWT token
    const jwtSecret = getEnv('JWT_SECRET');
    if (!jwtSecret) {
      logger.error('JWT_SECRET environment variable is not set');
      throw new DbisError(
        ErrorCode.INTERNAL_ERROR,
        'Server configuration error: JWT secret not configured'
      );
    }

    let decoded: JwtPayload;
    try {
      decoded = jwt.verify(token, jwtSecret) as JwtPayload;
    } catch (error) {
      logger.warn('JWT verification failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid or expired token');
    }

    // Extract claims shared by service and portal sessions
    req.sovereignBankId = decoded.sovereignBankId;
    req.employeeId = typeof decoded.employeeId === 'string' ? decoded.employeeId : undefined;
    req.email = typeof decoded.email === 'string' ? decoded.email : undefined;
    req.name = typeof decoded.name === 'string' ? decoded.name : undefined;
    req.roleName = typeof decoded.roleName === 'string' ? decoded.roleName : undefined;
    req.permissions = Array.isArray(decoded.permissions)
      ? decoded.permissions.filter((permission): permission is string => typeof permission === 'string')
      : undefined;
    req.sessionType = decoded.sessionType === 'portal' ? 'portal' : 'service';
    req.portalSurface =
      decoded.portalSurface === 'admin' ||
      decoded.portalSurface === 'member' ||
      decoded.portalSurface === 'core'
        ? decoded.portalSurface
        : undefined;
    req.identityType = decoded.identityType;
    req.apiRole = decoded.apiRole;

    if (isPortalSession(decoded)) {
      if (!req.employeeId && !req.email) {
        throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid portal token payload');
      }
    } else {
      if (!req.sovereignBankId) {
        throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid token payload');
      }

      // Verify request signature
      const signatureValid = await verifyRequestSignature(
        req,
        req.sovereignBankId,
        req.identityType || ''
      );
      if (!signatureValid) {
        throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid request signature');
      }
    }

    // Check token expiration
    if (decoded.exp && decoded.exp < Date.now() / 1000) {
      throw new DbisError(ErrorCode.UNAUTHORIZED, 'Token expired');
    }

    next();
  } catch (error) {
    if (error instanceof DbisError) {
      res.status(401).json({
        success: false,
        error: {
          code: error.code,
          message: error.message,
        },
        timestamp: new Date(),
      });
    } else {
      res.status(500).json({
        success: false,
        error: {
          code: ErrorCode.INTERNAL_ERROR,
          message: 'Authentication error',
        },
        timestamp: new Date(),
      });
    }
  }
}

/**
 * Optional authentication middleware (for public endpoints)
 */
export function optionalAuthMiddleware(
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): void {
  const token = extractSitToken(req);
  if (token) {
    try {
      const jwtSecret = getEnv('JWT_SECRET');
      if (jwtSecret) {
        const decoded = jwt.verify(token, jwtSecret) as JwtPayload;
        req.sovereignBankId = decoded.sovereignBankId;
        req.employeeId = typeof decoded.employeeId === 'string' ? decoded.employeeId : undefined;
        req.email = typeof decoded.email === 'string' ? decoded.email : undefined;
        req.name = typeof decoded.name === 'string' ? decoded.name : undefined;
        req.roleName = typeof decoded.roleName === 'string' ? decoded.roleName : undefined;
        req.permissions = Array.isArray(decoded.permissions)
          ? decoded.permissions.filter((permission): permission is string => typeof permission === 'string')
          : undefined;
        req.sessionType = decoded.sessionType === 'portal' ? 'portal' : 'service';
        req.portalSurface =
          decoded.portalSurface === 'admin' ||
          decoded.portalSurface === 'member' ||
          decoded.portalSurface === 'core'
            ? decoded.portalSurface
            : undefined;
        req.identityType = decoded.identityType;
        req.apiRole = decoded.apiRole;
      }
    } catch (error) {
      // Ignore auth errors for optional auth
      logger.debug('Optional auth failed (ignored)', {
        error: error instanceof Error ? error.message : 'Unknown error',
      });
    }
  }
  next();
}
