// AS4 Security Service
// Handles mTLS, message signing, encryption, and anti-replay protection

import { createHash, createSign, createVerify } from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import prisma from '@/shared/database/prisma';
import { memberDirectoryService } from '../as4-settlement/member-directory/member-directory.service';
import { certificateManagerService } from '../as4-settlement/member-directory/certificate-manager.service';
import { logger } from '@/infrastructure/monitoring/logger';

export interface MessageSignature {
  algorithm: string;
  signature: string;
  certificateFingerprint: string;
  signedAt: Date;
}

export interface MessageEncryption {
  algorithm: string;
  encryptedData: string;
  keyEncryption: string;
  certificateFingerprint: string;
}

export class As4SecurityService {
  /**
   * Validate replay nonce
   */
  async validateReplayNonce(memberId: string, nonce: string, timeWindowMinutes: number = 5): Promise<boolean> {
    // Check if nonce already used
    const existing = await prisma.as4ReplayNonce.findUnique({
      where: { nonce },
    });

    if (existing) {
      logger.warn('Replay nonce already used', { memberId, nonce });
      return false;
    }

    // Store nonce with expiration
    const expiresAt = new Date();
    expiresAt.setMinutes(expiresAt.getMinutes() + timeWindowMinutes);

    try {
      await prisma.as4ReplayNonce.create({
        data: {
          id: uuidv4(),
          nonce,
          memberId,
          expiresAt,
        },
      });
    } catch (error) {
      // Nonce might have been inserted concurrently
      logger.warn('Failed to store replay nonce', { memberId, nonce, error });
      return false;
    }

    // Clean up expired nonces (async)
    this.cleanupExpiredNonces().catch((err) => {
      logger.error('Failed to cleanup expired nonces', { error: err });
    });

    return true;
  }

  /**
   * Clean up expired nonces
   */
  private async cleanupExpiredNonces(): Promise<void> {
    await prisma.as4ReplayNonce.deleteMany({
      where: {
        expiresAt: {
          lt: new Date(),
        },
      },
    });
  }

  /**
   * Generate replay nonce
   */
  generateReplayNonce(): string {
    // Use UUIDv7-like format with timestamp for better ordering
    return uuidv4();
  }

  /**
   * Sign message (XMLDSig or JWS)
   */
  async signMessage(
    messagePayload: string,
    memberId: string,
    algorithm: 'RSA-SHA256' | 'ECDSA-SHA256' = 'RSA-SHA256'
  ): Promise<MessageSignature> {
    // Get member's signing certificate
    const member = await memberDirectoryService.getMember(memberId);
    if (!member || !member.signingCertFingerprint) {
      throw new Error(`Member ${memberId} not found or no signing certificate`);
    }

    // TODO: Implement actual signing with HSM or certificate
    // For now, create a placeholder signature
    const sign = createSign(algorithm === 'RSA-SHA256' ? 'RSA-SHA256' : 'sha256');
    sign.update(messagePayload);
    sign.end();

    // In production, this would use HSM or certificate private key
    // For now, return a hash-based signature
    const signature = createHash('sha256')
      .update(messagePayload + member.signingCertFingerprint)
      .digest('hex');

    return {
      algorithm,
      signature,
      certificateFingerprint: member.signingCertFingerprint,
      signedAt: new Date(),
    };
  }

  /**
   * Verify message signature
   */
  async verifyMessageSignature(
    messagePayload: string,
    signature: MessageSignature,
    memberId: string
  ): Promise<boolean> {
    // Get member's signing certificate
    const member = await memberDirectoryService.getMember(memberId);
    if (!member || !member.signingCertFingerprint) {
      logger.warn('Member not found or no signing certificate', { memberId });
      return false;
    }

    // Verify fingerprint matches
    if (signature.certificateFingerprint.toUpperCase() !== member.signingCertFingerprint.toUpperCase()) {
      logger.warn('Certificate fingerprint mismatch', {
        memberId,
        expected: member.signingCertFingerprint,
        received: signature.certificateFingerprint,
      });
      return false;
    }

    // TODO: Implement actual signature verification
    // For now, verify hash-based signature
    const expectedSignature = createHash('sha256')
      .update(messagePayload + member.signingCertFingerprint)
      .digest('hex');

    return expectedSignature === signature.signature;
  }

  /**
   * Encrypt message (XML Encryption or JWE)
   */
  async encryptMessage(
    messagePayload: string,
    recipientMemberId: string,
    algorithm: 'AES-256-GCM' | 'ChaCha20-Poly1305' = 'AES-256-GCM'
  ): Promise<MessageEncryption> {
    // Get recipient's encryption certificate
    const member = await memberDirectoryService.getMember(recipientMemberId);
    if (!member || !member.encryptionCertFingerprint) {
      throw new Error(`Recipient ${recipientMemberId} not found or no encryption certificate`);
    }

    // TODO: Implement actual encryption with recipient's public key
    // For now, return placeholder
    const encryptedData = Buffer.from(messagePayload).toString('base64');

    return {
      algorithm,
      encryptedData,
      keyEncryption: 'RSA-OAEP', // Key encryption algorithm
      certificateFingerprint: member.encryptionCertFingerprint,
    };
  }

  /**
   * Decrypt message
   */
  async decryptMessage(
    encryptedMessage: MessageEncryption,
    recipientMemberId: string
  ): Promise<string> {
    // Get recipient's encryption certificate
    const member = await memberDirectoryService.getMember(recipientMemberId);
    if (!member || !member.encryptionCertFingerprint) {
      throw new Error(`Recipient ${recipientMemberId} not found or no encryption certificate`);
    }

    // Verify certificate fingerprint
    if (
      encryptedMessage.certificateFingerprint.toUpperCase() !==
      member.encryptionCertFingerprint.toUpperCase()
    ) {
      throw new Error('Certificate fingerprint mismatch');
    }

    // TODO: Implement actual decryption with private key
    // For now, decode base64
    return Buffer.from(encryptedMessage.encryptedData, 'base64').toString('utf-8');
  }

  /**
   * Validate TLS certificate fingerprint
   */
  async validateTlsCertificate(memberId: string, certificateFingerprint: string): Promise<boolean> {
    return certificateManagerService.validateCertificateFingerprint(
      memberId,
      certificateFingerprint,
      'TLS'
    );
  }

  /**
   * Calculate payload hash
   */
  calculatePayloadHash(payload: string, algorithm: 'SHA256' | 'SHA512' = 'SHA256'): string {
    const hash = createHash(algorithm.toLowerCase());
    hash.update(payload);
    return hash.digest('hex').toUpperCase();
  }
}

export const as4SecurityService = new As4SecurityService();
