/**
 * HSM Enclave Module - Hardware Security Module Integration
 * 
 * Provides secure enclave integration for PQC key storage following
 * FIPS 140-3 Level 3 standards with PKCS#11 interface simulation.
 * 
 * In production: Integrates with AWS Nitro Enclaves or ARM TrustZone
 * In demo: Simulates HSM operations with secure key isolation
 * 
 * Patent Value: "Hardware-Locked Sovereignty" - Keys cannot be extracted
 * even if physical device is stolen.
 */

import crypto from 'crypto';
import { EventEmitter } from 'events';
import { logger } from './lib/logger';

const hsmLogger = logger.child('hsm');

export interface HSMConfig {
  enclaveId: string;
  // HONESTY (CF-1 2026-07-02): this is a SOFTWARE key-management module, NOT a FIPS-validated
  // hardware HSM. securityLevel must not assert a FIPS 140-3 hardware level it does not hold.
  securityLevel: string;
  keyWrapAlgorithm: 'AES-256-GCM' | 'AES-256-KWP';
  attestationEnabled: boolean;
  autoZeroize: boolean;
  zeroizeOnTamper: boolean;
}

export interface HSMKeyHandle {
  keyId: string;
  keyType: 'PQC_ML_KEM_768' | 'AES_256' | 'ECDSA_P384' | 'ED25519';
  createdAt: Date;
  lastAccessed: Date;
  accessCount: number;
  enclaveLocked: boolean;
  attestationHash: string;
}

export interface HSMAttestation {
  enclaveId: string;
  timestamp: Date;
  pcr0: string; // Platform Configuration Register 0
  pcr1: string;
  pcr2: string;
  nonce: string;
  signature: string;
  valid: boolean;
}

export interface PKCS11Session {
  sessionId: string;
  state: 'OPEN' | 'AUTHENTICATED' | 'CLOSED';
  userType: 'USER' | 'SO' | 'CONTEXT_SPECIFIC';
  openedAt: Date;
  lastActivity: Date;
  operations: number;
}

export interface HSMMetrics {
  totalOperations: number;
  encryptOperations: number;
  decryptOperations: number;
  signOperations: number;
  verifyOperations: number;
  keyGenerations: number;
  attestationRequests: number;
  failedOperations: number;
  avgLatencyMs: number;
  keysStored: number;
  sessionsActive: number;
  lastTamperCheck: Date;
  tamperDetected: boolean;
}

/**
 * Hardware Security Module Enclave
 * 
 * Simulates PKCS#11 HSM operations for secure key management.
 * Designed to wrap PQC operations in a hardware root of trust.
 */
export class HSMEnclave extends EventEmitter {
  private config: HSMConfig;
  private keys: Map<string, { 
    handle: HSMKeyHandle; 
    wrappedKey: Buffer;
    masterKeyId: string;
  }> = new Map();
  private sessions: Map<string, PKCS11Session> = new Map();
  private masterKeys: Map<string, Buffer> = new Map();
  private metrics: HSMMetrics;
  private initialized: boolean = false;
  private tamperState: boolean = false;

  constructor(config: Partial<HSMConfig> = {}) {
    super();
    
    this.config = {
      enclaveId: config.enclaveId || `SW-KEYSTORE-${crypto.randomBytes(4).toString('hex').toUpperCase()}`,
      securityLevel: config.securityLevel || 'SOFTWARE-SIM (not FIPS-validated)',
      keyWrapAlgorithm: config.keyWrapAlgorithm || 'AES-256-GCM',
      attestationEnabled: config.attestationEnabled ?? true,
      autoZeroize: config.autoZeroize ?? true,
      zeroizeOnTamper: config.zeroizeOnTamper ?? true
    };

    this.metrics = {
      totalOperations: 0,
      encryptOperations: 0,
      decryptOperations: 0,
      signOperations: 0,
      verifyOperations: 0,
      keyGenerations: 0,
      attestationRequests: 0,
      failedOperations: 0,
      avgLatencyMs: 0,
      keysStored: 0,
      sessionsActive: 0,
      lastTamperCheck: new Date(),
      tamperDetected: false
    };

    hsmLogger.info('Enclave created', {
      metadata: { enclaveId: this.config.enclaveId, securityLevel: this.config.securityLevel }
    });
  }

  /**
   * Initialize the HSM enclave with master key generation
   */
  async initialize(): Promise<boolean> {
    const startTime = Date.now();
    
    try {
      // Generate enclave master key (in production: derived from TEE)
      const masterKeyId = `MK-${this.config.enclaveId}`;
      const masterKey = crypto.randomBytes(32);
      this.masterKeys.set(masterKeyId, masterKey);

      // Generate backup master key for redundancy
      const backupKeyId = `MK-BACKUP-${this.config.enclaveId}`;
      const backupKey = crypto.randomBytes(32);
      this.masterKeys.set(backupKeyId, backupKey);

      this.initialized = true;
      this.updateMetrics(Date.now() - startTime);

      hsmLogger.info('Enclave initialized', { metadata: { masterKeyCount: this.masterKeys.size } });
      this.emit('initialized', { enclaveId: this.config.enclaveId });
      
      return true;
    } catch (error) {
      this.metrics.failedOperations++;
      hsmLogger.error('Initialization failed', {
        error: error instanceof Error ? error : new Error(String(error))
      });
      return false;
    }
  }

  /**
   * Open a PKCS#11 session with the HSM
   */
  openSession(userType: 'USER' | 'SO' = 'USER'): PKCS11Session {
    if (!this.initialized) {
      throw new Error('HSM not initialized');
    }

    if (this.tamperState && this.config.zeroizeOnTamper) {
      throw new Error('HSM tampered - operations locked');
    }

    const sessionId = `SES-${crypto.randomBytes(8).toString('hex').toUpperCase()}`;
    const session: PKCS11Session = {
      sessionId,
      state: 'OPEN',
      userType,
      openedAt: new Date(),
      lastActivity: new Date(),
      operations: 0
    };

    this.sessions.set(sessionId, session);
    this.metrics.sessionsActive = this.sessions.size;

    hsmLogger.debug('Session opened', { metadata: { sessionId, userType } });
    return session;
  }

  /**
   * Authenticate a PKCS#11 session with PIN
   */
  authenticateSession(sessionId: string, pin: string): boolean {
    const session = this.sessions.get(sessionId);
    if (!session) {
      throw new Error('Invalid session');
    }

    // In production: Verify against secure PIN storage
    // Demo: Accept any 4+ digit PIN
    if (pin.length >= 4) {
      session.state = 'AUTHENTICATED';
      session.lastActivity = new Date();
      hsmLogger.debug('Session authenticated', { metadata: { sessionId } });
      return true;
    }

    this.metrics.failedOperations++;
    return false;
  }

  /**
   * Close a PKCS#11 session
   */
  closeSession(sessionId: string): void {
    const session = this.sessions.get(sessionId);
    if (session) {
      session.state = 'CLOSED';
      this.sessions.delete(sessionId);
      this.metrics.sessionsActive = this.sessions.size;
      hsmLogger.debug('Session closed', { metadata: { sessionId } });
    }
  }

  /**
   * Generate and store a PQC key pair in the HSM
   * Keys never leave the enclave in plaintext
   */
  generatePQCKey(sessionId: string, keyLabel: string): HSMKeyHandle {
    const startTime = Date.now();
    this.validateSession(sessionId);

    // Generate ML-KEM-768 key material (simulated)
    // In production: Uses liboqs within TEE
    const publicKey = crypto.randomBytes(1184); // ML-KEM-768 public key size
    const secretKey = crypto.randomBytes(2400); // ML-KEM-768 secret key size

    // Wrap the secret key with master key (never stored plaintext)
    const masterKeyId = `MK-${this.config.enclaveId}`;
    const masterKey = this.masterKeys.get(masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const wrappedKey = this.wrapKey(secretKey, masterKey);
    
    const keyId = `KEY-${crypto.randomBytes(4).toString('hex').toUpperCase()}`;
    const handle: HSMKeyHandle = {
      keyId,
      keyType: 'PQC_ML_KEM_768',
      createdAt: new Date(),
      lastAccessed: new Date(),
      accessCount: 0,
      enclaveLocked: true,
      attestationHash: this.computeAttestation(keyId)
    };

    this.keys.set(keyId, {
      handle,
      wrappedKey,
      masterKeyId
    });

    this.metrics.keyGenerations++;
    this.metrics.keysStored = this.keys.size;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.info('PQC key generated', { metadata: { keyId, keyLabel } });
    this.emit('keyGenerated', { keyId, keyType: 'PQC_ML_KEM_768' });

    return handle;
  }

  /**
   * Perform PQC encapsulation within the HSM enclave
   * Shared secret never leaves the enclave
   */
  pqcEncapsulate(sessionId: string, keyId: string): { 
    ciphertext: Buffer; 
    sharedSecretHandle: string;
  } {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    // Simulate ML-KEM-768 encapsulation
    const ciphertext = crypto.randomBytes(1088); // ML-KEM-768 ciphertext size
    const sharedSecret = crypto.randomBytes(32); // Shared secret size

    // Store shared secret as a derived key (never exported)
    const sharedSecretId = `SS-${crypto.randomBytes(4).toString('hex').toUpperCase()}`;
    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const wrappedSecret = this.wrapKey(sharedSecret, masterKey);
    
    const secretHandle: HSMKeyHandle = {
      keyId: sharedSecretId,
      keyType: 'AES_256',
      createdAt: new Date(),
      lastAccessed: new Date(),
      accessCount: 0,
      enclaveLocked: true,
      attestationHash: this.computeAttestation(sharedSecretId)
    };

    this.keys.set(sharedSecretId, {
      handle: secretHandle,
      wrappedKey: wrappedSecret,
      masterKeyId: keyData.masterKeyId
    });

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.encryptOperations++;
    this.metrics.keysStored = this.keys.size;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('PQC encapsulation', { metadata: { keyId, sharedSecretId } });
    
    return { ciphertext, sharedSecretHandle: sharedSecretId };
  }

  /**
   * Perform PQC decapsulation within the HSM enclave
   */
  pqcDecapsulate(sessionId: string, keyId: string, ciphertext: Buffer): string {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    // Simulate ML-KEM-768 decapsulation
    const sharedSecret = crypto.randomBytes(32);

    // Store as derived key
    const sharedSecretId = `SS-${crypto.randomBytes(4).toString('hex').toUpperCase()}`;
    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const wrappedSecret = this.wrapKey(sharedSecret, masterKey);
    
    const secretHandle: HSMKeyHandle = {
      keyId: sharedSecretId,
      keyType: 'AES_256',
      createdAt: new Date(),
      lastAccessed: new Date(),
      accessCount: 0,
      enclaveLocked: true,
      attestationHash: this.computeAttestation(sharedSecretId)
    };

    this.keys.set(sharedSecretId, {
      handle: secretHandle,
      wrappedKey: wrappedSecret,
      masterKeyId: keyData.masterKeyId
    });

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.decryptOperations++;
    this.metrics.keysStored = this.keys.size;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('PQC decapsulation', { metadata: { keyId, sharedSecretId } });
    
    return sharedSecretId;
  }

  /**
   * Sign data using a key stored in the HSM
   */
  sign(sessionId: string, keyId: string, data: Buffer): Buffer {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    // Unwrap key temporarily for signing (in TEE only)
    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const unwrappedKey = this.unwrapKey(keyData.wrappedKey, masterKey);
    
    // Sign with unwrapped key
    const signature = crypto
      .createHmac('sha256', unwrappedKey)
      .update(data)
      .digest();

    // Zero-out unwrapped key immediately
    unwrappedKey.fill(0);

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.signOperations++;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('Signed', { metadata: { keyId } });
    return signature;
  }

  /**
   * Verify a signature using a key stored in the HSM
   */
  verify(sessionId: string, keyId: string, data: Buffer, signature: Buffer): boolean {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const unwrappedKey = this.unwrapKey(keyData.wrappedKey, masterKey);
    
    const expectedSig = crypto
      .createHmac('sha256', unwrappedKey)
      .update(data)
      .digest();

    // Zero-out unwrapped key immediately
    unwrappedKey.fill(0);

    const valid = signature.length === expectedSig.length && 
      crypto.timingSafeEqual(signature, expectedSig);

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.verifyOperations++;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('Verified', { metadata: { keyId, valid } });
    return valid;
  }

  /**
   * Encrypt data using a key stored in the HSM (AES-256-GCM)
   */
  encrypt(sessionId: string, keyId: string, plaintext: Buffer): { ciphertext: Buffer; iv: Buffer; tag: Buffer } {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const unwrappedKey = this.unwrapKey(keyData.wrappedKey, masterKey);
    
    // AES-256-GCM encryption
    const iv = crypto.randomBytes(12);
    const cipher = crypto.createCipheriv('aes-256-gcm', unwrappedKey.slice(0, 32), iv);
    
    const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
    const tag = cipher.getAuthTag();

    // Zero-out unwrapped key immediately
    unwrappedKey.fill(0);

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.encryptOperations++;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('Encrypted', { metadata: { keyId, plaintextBytes: plaintext.length } });
    return { ciphertext: encrypted, iv, tag };
  }

  /**
   * Decrypt data using a key stored in the HSM
   */
  decrypt(sessionId: string, keyId: string, ciphertext: Buffer, iv: Buffer, tag: Buffer): Buffer {
    const startTime = Date.now();
    this.validateSession(sessionId);

    const keyData = this.keys.get(keyId);
    if (!keyData) {
      throw new Error('Key not found');
    }

    const masterKey = this.masterKeys.get(keyData.masterKeyId);
    if (!masterKey) {
      throw new Error('Master key not found');
    }

    const unwrappedKey = this.unwrapKey(keyData.wrappedKey, masterKey);
    
    // AES-256-GCM decryption
    const decipher = crypto.createDecipheriv('aes-256-gcm', unwrappedKey.slice(0, 32), iv, { authTagLength: 16 });
    decipher.setAuthTag(tag);
    
    const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);

    // Zero-out unwrapped key immediately
    unwrappedKey.fill(0);

    keyData.handle.lastAccessed = new Date();
    keyData.handle.accessCount++;

    this.metrics.decryptOperations++;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.debug('Decrypted', { metadata: { keyId, ciphertextBytes: ciphertext.length } });
    return decrypted;
  }

  /**
   * Generate attestation report for the enclave
   */
  getAttestation(nonce: string): HSMAttestation {
    const startTime = Date.now();
    
    // Compute PCR values (Platform Configuration Registers)
    const pcr0 = crypto.createHash('sha256')
      .update(`${this.config.enclaveId}-firmware`)
      .digest('hex');
    const pcr1 = crypto.createHash('sha256')
      .update(`${this.config.enclaveId}-config`)
      .digest('hex');
    const pcr2 = crypto.createHash('sha256')
      .update(`${this.config.enclaveId}-keys-${this.keys.size}`)
      .digest('hex');

    // Sign attestation
    const attestationData = `${pcr0}:${pcr1}:${pcr2}:${nonce}:${Date.now()}`;
    const signature = crypto.createHash('sha256')
      .update(attestationData)
      .digest('hex');

    const attestation: HSMAttestation = {
      enclaveId: this.config.enclaveId,
      timestamp: new Date(),
      pcr0,
      pcr1,
      pcr2,
      nonce,
      signature,
      valid: true
    };

    this.metrics.attestationRequests++;
    this.updateMetrics(Date.now() - startTime);

    hsmLogger.info('Attestation generated', { metadata: { enclaveId: this.config.enclaveId } });
    return attestation;
  }

  /**
   * Perform tamper check on the enclave
   */
  tamperCheck(): { passed: boolean; findings: string[] } {
    const findings: string[] = [];
    let passed = true;

    // Check master key integrity
    Array.from(this.masterKeys.entries()).forEach(([keyId, key]) => {
      if (key.length !== 32) {
        findings.push(`Master key ${keyId} has invalid length`);
        passed = false;
      }
    });

    // Check for unauthorized key access patterns
    Array.from(this.keys.entries()).forEach(([keyId, keyData]) => {
      if (keyData.handle.accessCount > 10000) {
        findings.push(`Key ${keyId} has suspicious access count: ${keyData.handle.accessCount}`);
      }
    });

    // Check session integrity
    Array.from(this.sessions.entries()).forEach(([sessionId, session]) => {
      const sessionAge = Date.now() - session.openedAt.getTime();
      if (sessionAge > 24 * 60 * 60 * 1000) { // 24 hours
        findings.push(`Session ${sessionId} exceeded maximum lifetime`);
      }
    });

    this.metrics.lastTamperCheck = new Date();
    this.metrics.tamperDetected = !passed;
    this.tamperState = !passed;

    if (!passed && this.config.zeroizeOnTamper) {
      this.zeroize();
    }

    if (passed) {
      hsmLogger.info('Tamper check passed');
    } else {
      hsmLogger.error('Tamper check failed', { metadata: { findings } });
    }
    return { passed, findings };
  }

  /**
   * Zeroize all key material (emergency key destruction)
   */
  zeroize(): void {
    hsmLogger.error('ZEROIZING all key material');

    // Zero all master keys
    Array.from(this.masterKeys.values()).forEach(key => {
      key.fill(0);
    });
    this.masterKeys.clear();

    // Zero all wrapped keys
    Array.from(this.keys.values()).forEach(keyData => {
      keyData.wrappedKey.fill(0);
    });
    this.keys.clear();

    // Close all sessions
    this.sessions.clear();

    this.metrics.keysStored = 0;
    this.metrics.sessionsActive = 0;
    this.initialized = false;

    this.emit('zeroized', { enclaveId: this.config.enclaveId });
    hsmLogger.warn('Zeroization complete');
  }

  /**
   * Get HSM metrics
   */
  getMetrics(): HSMMetrics {
    return { ...this.metrics };
  }

  /**
   * Get all key handles (metadata only, no key material)
   */
  listKeys(sessionId: string): HSMKeyHandle[] {
    this.validateSession(sessionId);
    return Array.from(this.keys.values()).map(k => ({ ...k.handle }));
  }

  /**
   * Get enclave configuration
   */
  getConfig(): HSMConfig {
    return { ...this.config };
  }

  /**
   * Check if HSM is initialized
   */
  isInitialized(): boolean {
    return this.initialized;
  }

  // Private helper methods

  private validateSession(sessionId: string): void {
    const session = this.sessions.get(sessionId);
    if (!session) {
      throw new Error('Invalid session');
    }
    if (session.state !== 'AUTHENTICATED') {
      throw new Error('Session not authenticated');
    }
    if (this.tamperState && this.config.zeroizeOnTamper) {
      throw new Error('HSM tampered - operations locked');
    }
    session.lastActivity = new Date();
    session.operations++;
  }

  private wrapKey(key: Buffer, masterKey: Buffer): Buffer {
    const iv = crypto.randomBytes(12);
    const cipher = crypto.createCipheriv('aes-256-gcm', masterKey, iv);
    const encrypted = Buffer.concat([cipher.update(key), cipher.final()]);
    const tag = cipher.getAuthTag();
    
    // Format: IV (12) + Tag (16) + Encrypted Key
    return Buffer.concat([iv, tag, encrypted]);
  }

  private unwrapKey(wrappedKey: Buffer, masterKey: Buffer): Buffer {
    const iv = wrappedKey.slice(0, 12);
    const tag = wrappedKey.slice(12, 28);
    const encrypted = wrappedKey.slice(28);
    
    const decipher = crypto.createDecipheriv('aes-256-gcm', masterKey, iv, { authTagLength: 16 });
    decipher.setAuthTag(tag);
    
    return Buffer.concat([decipher.update(encrypted), decipher.final()]);
  }

  private computeAttestation(keyId: string): string {
    return crypto.createHash('sha256')
      .update(`${this.config.enclaveId}:${keyId}:${Date.now()}`)
      .digest('hex').substring(0, 16);
  }

  private updateMetrics(latencyMs: number): void {
    this.metrics.totalOperations++;
    // Exponential moving average for latency
    this.metrics.avgLatencyMs = this.metrics.avgLatencyMs * 0.9 + latencyMs * 0.1;
  }
}

// Singleton instance for global HSM access
let globalHSM: HSMEnclave | null = null;

export function getGlobalHSM(): HSMEnclave {
  if (!globalHSM) {
    globalHSM = new HSMEnclave();
  }
  return globalHSM;
}

export function initializeGlobalHSM(config?: Partial<HSMConfig>): HSMEnclave {
  globalHSM = new HSMEnclave(config);
  return globalHSM;
}
