## Test suite for the synchronization engine
##
## This module tests the bloom filter handshake, delta object creation,
## and incremental synchronization functionality.

import std/[unittest, times, json, tables, sequtils, asyncdispatch]
import ../src/nimpak/remote/sync_engine
import ../src/nimpak/cas
import ../src/nimpak/security/event_logger

suite "Synchronization Engine Tests":

  setup:
    # Initialize test environment
    let testCasPath = "/tmp/test_cas"
    let testLogPath = "/tmp/test_security.log"

    # Create test CAS manager
    let casManager = newCasManager(testCasPath, testCasPath)

    # Create test event logger
    let eventLogger = newSecurityEventLogger(testLogPath, testCasPath)

    # Create sync engine with test configuration
    var config = getDefaultSyncEngineConfig()
    config.bloomFilterSize = 1000  # Smaller for testing
    config.syncIntervalSeconds = 10  # Faster for testing

    var syncEngine = newSyncEngine(casManager, eventLogger, config)

  test "Bloom Filter Creation and Operations":
    var filter = newBloomFilter(100, 0.01)

    # Test adding items
    filter.add("test-hash-1")
    filter.add("test-hash-2")
    filter.add("test-hash-3")

    # Test contains (should return true for added items)
    check filter.contains("test-hash-1") == true
    check filter.contains("test-hash-2") == true
    check filter.contains("test-hash-3") == true

    # Test false negatives (should not occur)
    check filter.contains("non-existent-hash") == false or true  # May be false positive

    # Test serialization
    let serialized = filter.serialize()
    check serialized.len > 0

    # Test deserialization
    let deserialized = deserializeBloomFilter(serialized)
    check deserialized.size == filter.size
    check deserialized.hashFunctions == filter.hashFunctions

  test "Bandwidth Limiter":
    var limiter = newBandwidthLimiter(1000)  # 1KB/s limit

    # Test bandwidth checking
    check limiter.checkBandwidth(500) == true  # Should allow 500 bytes
    check limiter.checkBandwidth(600) == false  # Should deny 600 more bytes (total 1100)

    # Test bandwidth reset after time window
    limiter.windowStart = now() - initDuration(seconds = 2)  # Simulate time passage
    check limiter.checkBandwidth(500) == true  # Should allow after reset

  test "Mirror Management":
    var engine = syncEngine

    # Test adding mirrors
    let result1 = engine.addMirror("test-mirror-1", "https://test1.example.com", 100)
    check result1.success == true

    let result2 = engine.addMirror("test-mirror-2", "https://test2.example.com", 50)
    check result2.success == true

    # Test duplicate mirror
    let result3 = engine.addMirror("test-mirror-1", "https://duplicate.example.com", 75)
    check result3.success == false
    check result3.errorCode == 409

    # Test mirror selection
    let bestMirror = engine.selectBestMirror()
    check bestMirror.isSome()
    check bestMirror.get().id == "test-mirror-1"  # Higher priority

    # Test removing mirror
    let removeResult = engine.removeMirror("test-mirror-2")
    check removeResult.success == true

  test "Delta Object Creation":
    let engine = syncEngine
    let testData = "This is test data for delta object creation"

    # Store test data in CAS
    let storeResult = engine.localCasManager.storeObject(testData.toOpenArrayByte(0, testData.len - 1))
    check storeResult.isOk == true

    let casObject = storeResult.get()

    # Create delta object
    let deltaResult = engine.createDeltaObject(casObject.hash)
    check deltaResult.success == true

    let delta = deltaResult.value
    check delta.objectHash == casObject.hash
    check delta.deltaType == "add"
    check delta.originalSize == testData.len.int64

  test "Sync Event Extraction":
    let engine = syncEngine

    # Create some test security events
    var testEvent = createSecurityEvent(
      EventPackageVerification,
      SeverityInfo,
      "test-source",
      "Test package verification",
      %*{"package_hash": "blake2b-test123"}
    )

    engine.eventLogger.logSecurityEvent(testEvent)

    # Extract sync events
    let syncEvents = engine.extractSyncEventsFromSecurityLog(now() - initDuration(hours = 1))

    # Should find at least one sync event
    check syncEvents.len >= 1

    # Check event properties
    if syncEvents.len > 0:
      let syncEvent = syncEvents[0]
      check syncEvent.eventType == SyncPackageUpdated
      check syncEvent.objectHash == "blake2b-test123"

  test "Bloom Filter Update from Events":
    var engine = syncEngine

    # Create test sync events
    let events = @[
      SyncEvent(
        id: "test-1",
        timestamp: now(),
        eventType: SyncPackageAdded,
        objectHash: "blake2b-hash1",
        metadata: newJObject(),
        sequenceNumber: 1
      ),
      SyncEvent(
        id: "test-2",
        timestamp: now(),
        eventType: SyncPackageUpdated,
        objectHash: "blake2b-hash2",
        metadata: newJObject(),
        sequenceNumber: 2
      )
    ]

    # Update bloom filter
    engine.updateBloomFilterFromEvents(events)

    # Check that objects were added to bloom filter and known objects
    check engine.syncState.bloomFilter.contains("blake2b-hash1") == true
    check engine.syncState.bloomFilter.contains("blake2b-hash2") == true
    check "blake2b-hash1" in engine.syncState.knownObjects
    check "blake2b-hash2" in engine.syncState.knownObjects

  test "Mirror Health Updates":
    var engine = syncEngine

    # Add test mirror
    discard engine.addMirror("health-test", "https://health.example.com", 50)

    # Update health with good metrics
    engine.updateMirrorHealth("health-test", 100.0, true)  # 100ms latency, success

    let mirror1 = engine.mirrors["health-test"]
    check mirror1.status == MirrorActive
    check mirror1.latency == 100.0
    check mirror1.reliability > 0.5

    # Update health with bad metrics
    engine.updateMirrorHealth("health-test", 6000.0, false)  # 6s latency, failure

    let mirror2 = engine.mirrors["health-test"]
    check mirror2.status == MirrorSlow or mirror2.status == MirrorUnreachable
    check mirror2.reliability < 0.9  # Should decrease

  teardown:
    # Clean up test files
    try:
      removeDir("/tmp/test_cas")
      removeFile("/tmp/test_security.log")
    except:
      discard  # Ignore cleanup errors

# Run async tests
when isMainModule:
  # Note: Async tests would need special handling in a real test suite
  # For now, we just run the synchronous tests
  discard