## tests/test_security_event_logging.nim
## Comprehensive tests for security event logging system (Task 11.1d)

import std/[unittest, times, json, os, strutils, options]
import ../src/nimpak/security/event_logger
import ../src/nimpak/security/revocation_manager
import ../src/nimpak/cli/audit_commands

suite "Security Event Logging System":

  let testLogPath = "/tmp/nip_test_security.log"
  let testCasPath = "/tmp/nip_test_cas"
  let testCrlPath = "/tmp/nip_test_crl"

  setup:
    # Clean up test files
    if fileExists(testLogPath):
      removeFile(testLogPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if dirExists(testCrlPath):
      removeDir(testCrlPath)

    createDir(testCasPath)
    createDir(testCrlPath)

  teardown:
    # Clean up test files
    if fileExists(testLogPath):
      removeFile(testLogPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if dirExists(testCrlPath):
      removeDir(testCrlPath)

  test "Security Event Logger Creation":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)

    check:
      logger.logPath == testLogPath
      logger.casStore == testCasPath
      logger.signingKey.isNone()
      logger.lastEventHash == ""
      logger.eventCounter == 0

  test "Security Event Creation":
    let event = createSecurityEvent(
      EventKeyRevocation,
      SeverityCritical,
      "test-source",
      "Test revocation event",
      %*{"key_id": "test-key-123"}
    )

    check:
      event.eventType == EventKeyRevocation
      event.severity == SeverityCritical
      event.source == "test-source"
      event.message == "Test revocation event"
      event.metadata["key_id"].getStr() == "test-key-123"
      event.hashChainPrev == ""
      event.hashChainCurrent == ""
      event.signature.isNone()

  test "Event Hash Calculation":
    let event = createSecurityEvent(
      EventSignatureVerification,
      SeverityInfo,
      "verifier",
      "Signature verified",
      %*{"package": "test-package"}
    )

    let hash = calculateEventHash(event)

    check:
      hash.startsWith("blake3-")
      hash.len > 10

  test "Security Event Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)
    var event = createSecurityEvent(
      EventPackageVerification,
      SeverityInfo,
      "package-manager",
      "Package verified successfully",
      %*{"package": "htop", "version": "3.2.1"}
    )

    logger.logSecurityEvent(event)

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1
      logger.lastEventHash == event.hashChainCurrent
      event.hashChainPrev == ""  # First event has no previous hash
      event.hashChainCurrent != ""

  test "Hash Chain Continuity":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    # Log first event
    var event1 = createSecurityEvent(EventKeyGeneration, SeverityInfo, "key-manager", "Key generated")
    logger.logSecurityEvent(event1)

    # Log second event
    var event2 = createSecurityEvent(EventKeyRevocation, SeverityWarning, "key-manager", "Key revoked")
    logger.logSecurityEvent(event2)

    check:
      event1.hashChainPrev == ""
      event2.hashChainPrev == event1.hashChainCurrent
      event2.hashChainCurrent != event1.hashChainCurrent
      logger.eventCounter == 2

  test "Revocation Event Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    let revocation = RevocationEvent(
      keyId: "test-key-456",
      reason: ReasonKeyCompromise,
      reasonText: "Key compromised in security incident",
      revocationDate: now().utc(),
      supersededBy: some("test-key-789"),
      affectedPackages: @["package1", "package2"],
      emergencyRevocation: true,
      responseActions: @["immediate_crl_update", "package_re_signing"]
    )

    logger.logKeyRevocation(revocation)

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

  test "Emergency Revocation Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    logger.logEmergencyRevocation("emergency-key-123", "Suspected compromise", @["critical-package"])

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

  test "Key Rollover Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    let rollover = RolloverEvent(
      oldKeyId: "old-key-123",
      newKeyId: "new-key-456",
      rolloverType: "scheduled",
      overlapPeriod: "30d",
      affectedRepositories: @["stable", "testing"],
      validationResults: %*{"packages_re_signed": 150, "errors": []}
    )

    logger.logKeyRollover(rollover)

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

  test "Signature Verification Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    # Log successful verification
    logger.logSignatureVerification("test-package", "key-123", true)

    # Log failed verification
    logger.logSignatureVerification("bad-package", "key-456", false, "Invalid signature")

    check:
      fileExists(testLogPath)
      logger.eventCounter == 2

  test "Trust Violation Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    logger.logTrustViolation("suspicious-package", "Untrusted key used", "untrusted-key-789")

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

  test "CRL Update Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    logger.logCRLUpdate("https://crl.example.com/nexus.crl", @["revoked-key-1", "revoked-key-2"], true)

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

  test "Security Incident Logging":
    var logger = newSecurityEventLogger(testLogPath, testCasPath)

    logger.logSecurityIncident(
      "key_compromise",
      "Multiple keys compromised in coordinated attack",
      @["repository-server", "signing-server"],
      @["revoke_all_keys", "regenerate_infrastructure", "notify_users"]
    )

    check:
      fileExists(testLogPath)
      logger.eventCounter == 1

suite "Revocation Manager":

  let testCrlPath = "/tmp/nip_test_crl"
  let testCasPath = "/tmp/nip_test_cas"
  let testLogPath = "/tmp/nip_test_security.log"

  setup:
    if dirExists(testCrlPath):
      removeDir(testCrlPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if fileExists(testLogPath):
      removeFile(testLogPath)

    createDir(testCrlPath)
    createDir(testCasPath)

  teardown:
    if dirExists(testCrlPath):
      removeDir(testCrlPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if fileExists(testLogPath):
      removeFile(testLogPath)

  test "Revocation Manager Creation":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)
    let manager = newRevocationManager(testC, testCasPath, logger)

    check:
      manager.crlPath == testCrlPath
      manager.casStore == testCasPath
      manager.distributionUrls.len == 0
      manager.policies.len == 0

  test "Default Rollover Policies":
    let policies = getDefaultPolicies()

    check:
      policies.hasKey("ed25519")
      policies.hasKey("dilithium")
      policies["ed25519"].algorithm == "ed25519"
      policies["ed25519"].quantumResistant == false
      policies["dilithium"].quantumResistant == true

  test "Emergency Revocation":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)
    var manager = newRevocationManager(testCrlPath, testCasPath, logger)

    let result = manager.emergencyRevocation(
      "compromised-key-123",
      "Key compromised in security breach",
      @["critical-package-1", "critical-package-2"]
    )

    check:
      result.isOk()
      fileExists(testCrlPath / "revocation_list.nexus")

  test "Scheduled Key Rollover":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)
    var manager = newRevocationManager(testCrlPath, testCasPath, logger)

    # Set up rollover policy
    let policy = RolloverPolicy(
      algorithm: "ed25519",
      keySize: 256,
      overlapPeriod: initDuration(days = 30),
      gracePeriod: initDuration(days = 7),
      autoRolloverInterval: initDuration(days = 365),
      emergencyRolloverEnabled: true,
      quantumResistant: false
    )
    manager.setRolloverPolicy("ed25519", policy)

    let result = manager.scheduleKeyRollover("old-key-123", "ed25519", @["stable", "testing"])

    check:
      result.isOk()
      result.get().rolloverType == "scheduled"
      result.get().overlapPeriod == initDuration(days = 30)

  test "Quantum Transition Planning":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)
    var manager = newRevocationManager(testCrlPath, testCasPath, logger)

    let result = manager.planQuantumTransition("classical-key-123", "dilithium")

    check:
      result.isOk()
      result.get().rolloverType == "quantum-transition"
      result.get().overlapPeriod == initDuration(days = 60)
      result.get().affectedRepositories == @["all"]

  test "Offline Revocation Package":
    let logger = newSecurityEventLogger(testLogPath, testCasPath)
    let manager = newRevocationManager(testCrlPath, testCasPath, logger)

    let result = manager.createOfflineRevocationPackage(@["offline-key-1", "offline-key-2"])

    check:
      result.isOk()
      fileExists(result.get())

suite "CLI Audit Commands":

  test "Audit Command Parsing - Log":
    let result = parseAuditCommand(@["log", "--follow", "--format", "json"])

    check:
      result.isOk()
      result.get().command == AuditLog
      result.get().follow == true
      result.get().format == "json"

  test "Audit Command Parsing - Keys":
    let result = parseAuditCommand(@["keys", "--format", "table", "--verbose"])

    check:
      result.isOk()
      result.get().command == AuditKeys
      result.get().format == "table"
      result.get().verbose == true

  test "Audit Command Parsing - Packages":
    let result = parseAuditCommand(@["packages", "--package", "htop", "--severity", "error"])

    check:
      result.isOk()
      result.get().command == AuditPackages
      result.get().packageName.isSome()
      result.get().packageName.get() == "htop"
      result.get().severity.isSome()
      result.get().severity.get() == SeverityError

  test "Audit Command Parsing - Integrity":
    let result = parseAuditCommand(@["integrity", "--output", "/tmp/integrity_report.json"])

    check:
      result.isOk()
      result.get().command == AuditIntegrity
      result.get().outputFile.isSome()
      result.get().outputFile.get() == "/tmp/integrity_report.json"

  test "Invalid Audit Command":
    let result = parseAuditCommand(@["invalid-command"])

    check:
      result.isErr()
      result.errValue.contains("Unknown audit command")

  test "Missing Required Arguments":
    let result = parseAuditCommand(@["log", "--format"])

    check:
      result.isErr()
      result.errValue.contains("--format requires a value")

suite "Integration Tests":

  let testLogPath = "/tmp/nip_integration_security.log"
  let testCasPath = "/tmp/nip_integration_cas"
  let testCrlPath = "/tmp/nip_integration_crl"

  setup:
    if fileExists(testLogPath):
      removeFile(testLogPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if dirExists(testCrlPath):
      removeDir(testCrlPath)

    createDir(testCasPath)
    createDir(testCrlPath)

  teardown:
    if fileExists(testLogPath):
      removeFile(testLogPath)
    if dirExists(testCasPath):
      removeDir(testCasPath)
    if dirExists(testCrlPath):
      removeDir(testCrlPath)

  test "Complete Security Workflow":
    # Initialize components
    var logger = newSecurityEventLogger(testLogPath, testCasPath)
    var manager = newRevocationManager(testCrlPath, testCasPath, logger)

    # Set up policies
    let policies = getDefaultPolicies()
    for algorithm, policy in policies:
      manager.setRolloverPolicy(algorithm, policy)

    # Simulate security events
    logger.logSignatureVerification("package-1", "key-123", true)
    logger.logSignatureVerification("package-2", "key-456", false, "Invalid signature")

    # Perform emergency revocation
    let revocationResult = manager.emergencyRevocation("key-456", "Compromised key", @["package-2"])
    check revocationResult.isOk()

    # Schedule rollover
    let rolloverResult = manager.scheduleKeyRollover("key-123", "ed25519", @["stable"])
    check rolloverResult.isOk()

    # Verify log integrity
    let integrityResult = logger.verifyLogIntegrity()
    check integrityResult.valid

    # Check that all events were logged
    check:
      logger.eventCounter >= 4  # At least 4 events logged
      fileExists(testLogPath)
      fileExists(testCrlPath / "revocation_list.nexus")

  test "CLI Integration":
    # Set up test environment
    putEnv("NIP_SECURITY_LOG", testLogPath)
    putEnv("NIP_CAS_STORE", testCasPath)
    putEnv("NIP_CRL_PATH", testCrlPath)

    # Initialize and log some events
    var logger = newSecurityEventLogger(testLogPath, testCasPath)
    logger.logSignatureVerification("test-package", "test-key", true)
    logger.logTrustViolation("bad-package", "Untrusted source", "bad-key")

    # Test CLI commands
    let logResult = executeAuditCommand(@["log", "--format", "json"])
    check logResult.isOk()

    let integrityResult = executeAuditCommand(@["integrity", "--format", "table"])
    check integrityResult.isOk()

when isMainModule:
  # Run the tests
  echo "Running Security Event Logging System Tests..."
  echo "=" .repeat(50)