import std/[unittest, times, base64, json, os, tempfiles, strutils, options]
import ../src/nimpak/security/signature_verifier

suite "Signature Algorithm Detection":
  test "detect Ed25519 signature formats":
    check detectSignatureAlgorithm("ed25519-abc123") == SigEd25519
    check detectSignatureAlgorithm("a".repeat(128)) == SigEd25519  # 128 hex chars

  test "detect Dilithium signature formats":
    expect ValueError:
      discard detectSignatureAlgorithm("dilithium-abc123")  # Not implemented yet

  test "invalid signature formats":
    expect ValueError:
      discard detectSignatureAlgorithm("invalid-sig")
    expect ValueError:
      discard detectSignatureAlgorithm("abc")  # Too short

suite "Signature String Parsing":
  test "parse Ed25519 signature strings":
    let (alg1, sig1) = parseSignatureString("ed25519-abc123def456")
    check alg1 == SigEd25519
    check sig1 == "abc123def456"

    let (alg2, sig2) = parseSignatureString("a".repeat(128))
    check alg2 == SigEd25519
    check sig2 == "a".repeat(128)

  test "format signature strings":
    check formatSignatureString(SigEd25519, "abc123") == "ed25519-abc123"
    check formatSignatureString(SigDilithium, "def456") == "dilithium-def456"
    check formatSignatureString(SigRSA, "789abc") == "rsa-789abc"

suite "Ed25519 Signature Verification":
  # Test vectors for Ed25519 (RFC 8032)
  const ED25519_TEST_VECTORS = [
    (
      message: "",
      publicKey: "11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=",
      signature: "hgyY0il+MGCjP0JzlnkSPjt9HQjyOh1XBL2QX5AjXtYzOjIh2+3IfGG07fAuNqq41PvvRMzFRuV+DqbpBRlHBg==",
      valid: true
    ),
    (
      message: "test message",
      publicKey: "11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=",
      signature: "invalid-signature-data",
      valid: false
    )
  ]

  test "Ed25519 signature verification with test vectors":
    for vector in ED25519_TEST_VECTORS:
      let result = verifyEd25519Signature(vector.message, vector.signature, vector.publicKey)
      if vector.valid:
        check result == true
      else:
        check result == false

  test "Ed25519 signature with invalid inputs":
    # Invalid signature length
    check verifyEd25519Signature("test", "short", "validkey") == false

    # Invalid public key length
    check verifyEd25519Signature("test", "valid-sig", "short") == false

    # Invalid base64
    check verifyEd25519Signature("test", "invalid-base64!", "invalid-base64!") == false

suite "Signature Verifier":
  test "create signature verifier":
    let verifier = newSignatureVerifier()
    check verifier.trustedKeys.len == 0
    check verifier.requireValidTimestamp == true
    check SigEd25519 in verifier.allowedAlgorithms

  test "add trusted key":
    var verifier = newSignatureVerifier()
    let key = createPublicKey(
      SigEd25519,
      "test-key-1",
      "test-key-data",
      now().utc() - initDuration(days = 1),
      now().utc() + initDuration(days = 365)
    )

    verifier.addTrustedKey(key)
    check verifier.trustedKeys.len == 1
    check verifier.trustedKeys[0].keyId == "test-key-1"

  test "find trusted key":
    var verifier = newSignatureVerifier()
    let key = createPublicKey(
      SigEd25519,
      "findable-key",
      "test-key-data",
      now().utc() - initDuration(days = 1),
      now().utc() + initDuration(days = 365)
    )

    verifier.addTrustedKey(key)

    let foundKey = verifier.findTrustedKey("findable-key")
    check foundKey.isSome()
    check foundKey.get().keyId == "findable-key"

    let notFoundKey = verifier.findTrustedKey("nonexistent-key")
    check notFoundKey.isNone()

suite "Key Validation":
  test "key validity checks":
    let now = times.now().utc()

    # Valid key
    let validKey = createPublicKey(
      SigEd25519,
      "valid-key",
      "key-data",
      now - initDuration(days = 1),
      now + initDuration(days = 365)
    )
    check isKeyValid(validKey, now) == true

    # Expired key
    let expiredKey = createPublicKey(
      SigEd25519,
      "expired-key",
      "key-data",
      now - initDuration(days = 365),
      now - initDuration(days = 1)
    )
    check isKeyValid(expiredKey, now) == false

    # Not yet valid key
    let futureKey = createPublicKey(
      SigEd25519,
      "future-key",
      "key-data",
      now + initDuration(days = 1),
      now + initDuration(days = 365)
    )
    check isKeyValid(futureKey, now) == false

    # Revoked key
    var revokedKey = validKey
    revokedKey.revoked = true
    check isKeyValid(revokedKey, now) == false

suite "Digital Signature Objects":
  test "create digital signature":
    let timestamp = now().utc()
    let sig = createDigitalSignature(SigEd25519, "test-key", "signature-data", timestamp)

    check sig.algorithm == SigEd25519
    check sig.keyId == "test-key"
    check sig.signature == "signature-data"
    check sig.timestamp == timestamp

  test "create public key":
    let validFrom = now().utc()
    let validUntil = validFrom + initDuration(days = 365)
    let key = createPublicKey(SigEd25519, "test-key", "key-data", validFrom, validUntil)

    check key.algorithm == SigEd25519
    check key.keyId == "test-key"
    check key.keyData == "key-data"
    check key.validFrom == validFrom
    check key.validUntil == validUntil
    check key.revoked == false

  test "signature validation":
    let validSig = createDigitalSignature(SigEd25519, "key-1", "sig-data")
    check isSignatureValid(validSig) == true

    let invalidSig = createDigitalSignature(SigEd25519, "", "sig-data")
    check isSignatureValid(invalidSig) == false

    let invalidSig2 = createDigitalSignature(SigEd25519, "key-1", "")
    check isSignatureValid(invalidSig2) == false

suite "Signature Policy":
  test "create signature policy":
    let policy = newSignaturePolicy()
    check policy.requireSignatures == true
    check SigEd25519 in policy.allowedAlgorithms
    check policy.minimumKeySize == 256
    check policy.maxSignatureAge == 86400
    check policy.requireTimestamp == true
    check policy.allowSelfSigned == false

  test "validate signature against policy":
    let policy = newSignaturePolicy()

    # Valid signature
    let validSig = createDigitalSignature(SigEd25519, "key-1", "sig-data", now().utc())
    check validateSignaturePolicy(validSig, policy) == true

    # Invalid algorithm
    let invalidAlgSig = createDigitalSignature(SigRSA, "key-1", "sig-data", now().utc())
    check validateSignaturePolicy(invalidAlgSig, policy) == false

    # Too old signature
    let oldSig = createDigitalSignature(SigEd25519, "key-1", "sig-data",
                                       now().utc() - initDuration(days = 2))
    check validateSignaturePolicy(oldSig, policy) == false

suite "File Signature Verification":
  test "verify file signature":
    # Create a temporary file
    let (tempFile, tempPath) = createTempFile("nimpak_sig_test_", ".txt")
    tempFile.write("Test file content for signature verification")
    tempFile.close()

    try:
      # This test would require actual Ed25519 key generation and signing
      # For now, we test the error handling
      expect IOError:
        let verifier = newSignatureVerifier()
        let sig = createDigitalSignature(SigEd25519, "test-key", "fake-signature")
        discard verifier.verifyFileSignature("/nonexistent/file.txt", sig)

    finally:
      removeFile(tempPath)

suite "Hybrid Signature Support":
  test "create hybrid signature":
    let classicalSig = createDigitalSignature(SigEd25519, "classical-key", "classical-sig")
    let quantumSig = createDigitalSignature(SigDilithium, "quantum-key", "quantum-sig")

    let hybridSig = HybridSignature(
      classicalSig: classicalSig,
      quantumSig: some(quantumSig),
      requireBoth: true
    )

    check hybridSig.classicalSig.algorithm == SigEd25519
    check hybridSig.quantumSig.isSome()
    check hybridSig.quantumSig.get().algorithm == SigDilithium
    check hybridSig.requireBoth == true

suite "Signature Information":
  test "get signature info":
    let timestamp = now().utc()
    let sig = createDigitalSignature(SigEd25519, "info-key", "info-sig", timestamp)
    let info = getSignatureInfo(sig)

    check "ed25519" in info.toLower()
    check "info-key" in info
    check $timestamp in info

suite "Error Handling":
  test "signature verification errors":
    let verifier = newSignatureVerifier()
    let sig = createDigitalSignature(SigEd25519, "nonexistent-key", "fake-signature")

    expect SignatureVerificationError:
      discard verifier.verifySignature("test message", sig)

  test "unsupported algorithm errors":
    expect ValueError:
      discard verifyDilithiumSignature("test", "sig", "key")

    expect ValueError:
      discard verifyRSASignature("test", "sig", "key")

suite "Integration with Hash Verifier":
  test "verify hash signature":
    let verifier = newSignatureVerifier()
    let hash = "blake2b-abc123def456"
    let sig = createDigitalSignature(SigEd25519, "hash-key", "hash-signature")

    # This would fail because we don't have the trusted key
    expect SignatureVerificationError:
      discard verifier.verifyHashSignature(hash, sig)

suite "Performance and Statistics":
  test "signature verification timing":
    # Create a mock signature that will fail verification
    let verifier = newSignatureVerifier()
    let sig = createDigitalSignature(SigEd25519, "timing-key", "timing-signature")

    expect SignatureVerificationError:
      let result = verifier.verifySignature("timing test", sig)
      # Even failed verification should have timing information
      check result.verificationTime >= 0.0