## Property-Based Tests for Unified Storage
##
## Feature: 01-nip-unified-storage-and-formats
## Property 2: Chunk Integrity
## Validates: Requirements 2.2
##
## This test verifies that xxh3 hash integrity is maintained for all chunks:
## For any CAS chunk, the xxh3 hash SHALL match the chunk content after decompression

import std/[unittest, os, random, strutils]
import ../src/nip/unified_storage
import ../src/nip/xxhash

# Simple property-based testing framework
type
  TestCase = object
    data: string
    description: string

proc generateRandomData(size: int): string =
  ## Generate random data for testing
  result = newString(size)
  for i in 0..<size:
    result[i] = char(rand(255))

proc generateTestCases(count: int): seq[TestCase] =
  ## Generate test cases with various data patterns
  result = @[]

  # Edge cases
  result.add(TestCase(data: "", description: "empty string"))
  result.add(TestCase(data: "a", description: "single character"))
  result.add(TestCase(data: "Hello, World!", description: "simple text"))

  # Random data of various sizes
  for i in 0..<count:
    let size = rand(1..10000)
    result.add(TestCase(
      data: generateRandomData(size),
      description: "random data size=" & $size
    ))

  # Repeated patterns
  result.add(TestCase(data: "a".repeat(1000), description: "repeated character"))
  result.add(TestCase(data: "ab".repeat(500), description: "repeated pattern"))

  # Binary data
  var binaryData = ""
  for i in 0..255:
    binaryData.add(char(i))
  result.add(TestCase(data: binaryData, description: "all byte values"))

suite "Unified Storage - Property Tests":

  setup:
    # Create temporary test directory
    let testRoot = getTempDir() / "nip_test_" & $rand(100000)
    let storageRoot = initStorageRoot(testRoot)
    discard createStorageStructure(storageRoot)

  teardown:
    # Clean up test directory
    try:
      removeDir(testRoot)
    except:
      discard

  test "Property 2: Chunk Integrity - Hash matches content after storage":
    ## **Feature: 01-nip-unified-storage-and-formats, Property 2: Chunk Integrity**
    ## **Validates: Requirements 2.2**
    ##
    ## For any chunk data, calculating the hash and then verifying it
    ## should always succeed. This ensures hash integrity is maintained.

    let testCases = generateTestCases(100)
    var passCount = 0
    var failCount = 0

    for tc in testCases:
      # Calculate hash
      let hash = calculateXXH3(tc.data)

      # Verify hash matches original data
      let verified = verifyXXH3(tc.data, hash)

      if verified:
        passCount.inc
      else:
        failCount.inc
        echo "FAILED: ", tc.description
        echo "  Data length: ", tc.data.len
        echo "  Hash: ", $hash

    echo "\nProperty Test Results:"
    echo "  Total cases: ", testCases.len
    echo "  Passed: ", passCount
    echo "  Failed: ", failCount

    check failCount == 0
    check passCount == testCases.len

  test "Property 2: Chunk Integrity - Hash is deterministic":
    ## Hashing the same data multiple times should produce the same hash

    let testData = "NexusOS unified storage test data"
    var hashes: seq[XXH3Hash] = @[]

    # Calculate hash 100 times
    for i in 0..<100:
      hashes.add(calculateXXH3(testData))

    # All hashes should be identical
    let firstHash = hashes[0]
    for h in hashes:
      check h == firstHash

  test "Property 2: Chunk Integrity - Different data produces different hashes":
    ## Different data should produce different hashes (collision resistance)

    var hashes: seq[XXH3Hash] = @[]
    var dataSet: seq[string] = @[]

    # Generate 1000 different data samples
    for i in 0..<1000:
      let data = generateRandomData(rand(10..1000))
      dataSet.add(data)
      hashes.add(calculateXXH3(data))

    # Check for collisions
    var collisions = 0
    for i in 0..<hashes.len:
      for j in (i+1)..<hashes.len:
        if hashes[i] == hashes[j] and dataSet[i] != dataSet[j]:
          collisions.inc
          echo "COLLISION DETECTED:"
          echo "  Data 1 length: ", dataSet[i].len
          echo "  Data 2 length: ", dataSet[j].len
          echo "  Hash: ", $hashes[i]

    echo "\nCollision Test Results:"
    echo "  Total comparisons: ", (hashes.len * (hashes.len - 1)) div 2
    echo "  Collisions found: ", collisions

    check collisions == 0

  test "Property 2: Chunk Integrity - Byte sequence hashing":
    ## Hashing byte sequences should work correctly

    let testCases = generateTestCases(50)

    for tc in testCases:
      # Convert string to byte sequence
      var bytes: seq[byte] = @[]
      for c in tc.data:
        bytes.add(byte(c))

      # Hash both string and bytes
      let stringHash = calculateXXH3(tc.data)
      let bytesHash = calculateXXH3(bytes)

      # They should produce the same hash
      check stringHash == bytesHash

  test "Storage Structure Creation":
    ## Verify that storage structure is created correctly

    let root = initStorageRoot()
    check createStorageStructure(root)
    check verifyStorageStructure(root)

    # Verify all directories exist
    check dirExists(root.basePath)
    check dirExists(root.casPath)
    check dirExists(root.casPath / "chunks")
    check dirExists(root.casPath / "refs")
    check dirExists(root.casPath / "refs" / "npks")
    check dirExists(root.casPath / "refs" / "nips")
    check dirExists(root.casPath / "refs" / "nexters")
    check dirExists(root.npksPath)
    check dirExists(root.nipsPath)
    check dirExists(root.nextersPath)
    check fileExists(root.auditLogPath)

  test "CAS Store Initialization":
    ## Verify CAS store can be initialized

    let root = initStorageRoot()
    discard createStorageStructure(root)

    let store = initCASStore(root.casPath)
    check store.rootPath == root.casPath
    check store.chunksPath == root.casPath / "chunks"
    check store.refsPath == root.casPath / "refs"
    check store.auditLog == root.auditLogPath

  test "Audit Log Writing":
    ## Verify audit log entries can be written

    let root = initStorageRoot()
    discard createStorageStructure(root)

    let store = initCASStore(root.casPath)
    store.logAuditEntry("TEST", "Test audit entry")

    # Verify log file was updated
    check fileExists(store.auditLog)
    let logContent = readFile(store.auditLog)
    check "TEST" in logContent
    check "Test audit entry" in logContent

when isMainModule:
  # Run tests with random seed for reproducibility
  randomize(12345)
  echo "Running Unified Storage Property Tests..."
  echo "Random seed: 12345"
  echo ""
