## Test suite for Nippels Merkle Tree Integration (Task 8.4)
##
## Tests the integration of Merkle Tree with NippelManager

import std/[unittest, os, times, strutils, tables, json]
import ../src/nimpak/nippels
import ../src/nimpak/merkle_tree
import ../src/nimpak/nippel_types
import ../src/nimpak/utils/resultutils

suite "Nippels Merkle Tree Integration (Task 8.4)":
  var manager: NippelManager
  let testRoot = getTempDir() / "nippels_merkle_test_" & $epochTime().int

  setup:
    # Create test directory
    createDir(testRoot)
    manager = newNippelManager(testRoot)

  teardown:
    # Deactivate all active Nippels
    for name in manager.getActiveNippels():
      discard manager.deactivateNippel(name)

    # Clean up test directory
    if dirExists(testRoot):
      removeDir(testRoot)

  test "Create Nippel builds initial merkle tree":
    let result = manager.createNippel("test-merkle", Homestation)
    check result.isOk

    if result.isOk:
      let nippel = result.value

      # Verify merkle root is set
      check nippel.merkleRoot.len > 0
      check nippel.merkleRoot.startsWith("xxh3-")

      # Verify merkle tree is stored in manager
      check manager.merkleTrees.hasKey("test-merkle")

      let tree = manager.merkleTrees["test-merkle"]
      check tree.hashAlgorithm == "xxh3"
      check tree.leafCount >= 0  # Empty tree initially

      echo "   ✅ Initial merkle tree created with root: ", nippel.merkleRoot

  test "Merkle root is stored in cell configuration":
    let result = manager.createNippel("test-config", Homestation)
    check result.isOk

    if result.isOk:
      let nippel = result.value
      let configPath = nippel.cellRoot / "cell.json"
      check fileExists(configPath)

      # Read and verify configuration contains merkle root
      let config = parseJson(readFile(configPath))
      check config.hasKey("storage")
      check config["storage"].hasKey("merkle_root")
      check config["storage"]["merkle_root"].getStr() == nippel.merkleRoot

      echo "   ✅ Merkle root stored in configuration"

  test "Multiple Nippels have independent merkle trees":
    let result1 = manager.createNippel("test-multi-1", Homestation)
    let result2 = manager.createNippel("test-multi-2", Workstation)
    let result3 = manager.createNippel("test-multi-3", Server)

    check result1.isOk
    check result2.isOk
    check result3.isOk

    if result1.isOk and result2.isOk and result3.isOk:
      # Verify each has its own merkle tree
      check manager.merkleTrees.hasKey("test-multi-1")
      check manager.merkleTrees.hasKey("test-multi-2")
      check manager.merkleTrees.hasKey("test-multi-3")

      # Verify merkle roots (should all be the same for empty trees)
      let nippel1 = result1.value
      let nippel2 = result2.value
      let nippel3 = result3.value

      check nippel1.merkleRoot.len > 0
      check nippel2.merkleRoot.len > 0
      check nippel3.merkleRoot.len > 0

      echo "   ✅ Multiple independent merkle trees created"

  test "Merkle tree uses xxh3 algorithm":
    let result = manager.createNippel("test-algorithm", Homestation)
    check result.isOk

    if result.isOk:
      let tree = manager.merkleTrees["test-algorithm"]
      check tree.hashAlgorithm == "xxh3"
      check tree.root.hash.startsWith("xxh3-")

      echo "   ✅ Merkle tree uses xxh3 algorithm"

  test "Empty merkle tree has valid root hash":
    let result = manager.createNippel("test-empty", Homestation)
    check result.isOk

    if result.isOk:
      let nippel = result.value
      let tree = manager.merkleTrees["test-empty"]

      # Verify tree structure
      check tree.root != nil
      check tree.root.hash.len > 0
      check tree.leafCount >= 0

      # Verify root hash matches
      check getRootHash(tree) == nippel.merkleRoot

      echo "   ✅ Empty merkle tree has valid root hash"

  test "Merkle tree can be verified":
    let result = manager.createNippel("test-verify", Homestation)
    check result.isOk

    if result.isOk:
      let tree = manager.merkleTrees["test-verify"]

      # Verify the tree
      let verifyResult = verifyTree(tree)
      check verifyResult.isOk

      if verifyResult.isOk:
        check verifyResult.get() == true
        echo "   ✅ Merkle tree verification passed"

  test "Merkle tree can be updated with new files":
    let result = manager.createNippel("test-update", Homestation)
    check result.isOk

    if result.isOk:
      var tree = manager.merkleTrees["test-update"]
      let oldRoot = getRootHash(tree)

      # Add a file to the tree
      let addResult = addFile(tree, "test.txt", "xxh3-abc123", 100)
      check addResult.isOk

      if addResult.isOk:
        let newRoot = addResult.get()
        check newRoot != oldRoot
        check newRoot.len > 0

        # Update the tree in manager
        manager.merkleTrees["test-update"] = tree

        echo "   ✅ Merkle tree updated with new file"
        echo "      Old root: ", oldRoot
        echo "      New root: ", newRoot

  test "Merkle tree tracks file additions":
    let result = manager.createNippel("test-additions", Homestation)
    check result.isOk

    if result.isOk:
      var tree = manager.merkleTrees["test-additions"]

      # Add multiple files
      discard addFile(tree, "file1.txt", "xxh3-hash1", 100)
      discard addFile(tree, "file2.txt", "xxh3-hash2", 200)
      discard addFile(tree, "file3.txt", "xxh3-hash3", 300)

      # Verify tree has the files
      let leaves = getAllLeaves(tree)
      check leaves.len == 3

      # Verify we can find each file
      check findLeafInTree(tree, "file1.txt").isSome
      check findLeafInTree(tree, "file2.txt").isSome
      check findLeafInTree(tree, "file3.txt").isSome

      echo "   ✅ Merkle tree tracks file additions"

  test "Merkle tree can diff between states":
    let result = manager.createNippel("test-diff", Homestation)
    check result.isOk

    if result.isOk:
      var tree1 = manager.merkleTrees["test-diff"]

      # Add files to tree1
      discard addFile(tree1, "file1.txt", "xxh3-hash1", 100)
      discard addFile(tree1, "file2.txt", "xxh3-hash2", 200)

      # Create tree2 with different files
      var tree2 = tree1
      discard addFile(tree2, "file3.txt", "xxh3-hash3", 300)
      discard removeFile(tree2, "file1.txt")

      # Diff the trees
      let diffResult = diffTrees(tree1, tree2)
      check diffResult.isOk

      if diffResult.isOk:
        let diffs = diffResult.get()
        check diffs.len > 0
        echo "   ✅ Merkle tree diffing works"
        echo "      Found ", diffs.len, " differences"

  test "Merkle tree root changes when content changes":
    let result = manager.createNippel("test-changes", Homestation)
    check result.isOk

    if result.isOk:
      var tree = manager.merkleTrees["test-changes"]
      let root1 = getRootHash(tree)

      # Add a file
      discard addFile(tree, "file.txt", "xxh3-hash1", 100)
      let root2 = getRootHash(tree)

      # Modify the file
      discard updateFile(tree, "file.txt", "xxh3-hash2", 150)
      let root3 = getRootHash(tree)

      # Remove the file
      discard removeFile(tree, "file.txt")
      let root4 = getRootHash(tree)

      # All roots should be different
      check root1 != root2
      check root2 != root3
      check root3 != root4
      check root1 == root4  # Back to empty tree

      echo "   ✅ Merkle root changes with content"

  test "Merkle tree verification detects tampering":
    let result = manager.createNippel("test-tamper", Homestation)
    check result.isOk

    if result.isOk:
      var tree = manager.merkleTrees["test-tamper"]

      # Add files
      discard addFile(tree, "file1.txt", "xxh3-hash1", 100)
      discard addFile(tree, "file2.txt", "xxh3-hash2", 200)

      # Verify tree is valid
      let verifyResult1 = verifyTree(tree)
      check verifyResult1.isOk
      check verifyResult1.get() == true

      # Tamper with a node (if we had access to internal nodes)
      # For now, just verify that verification works
      echo "   ✅ Merkle tree verification works"

  test "Merkle tree performance is acceptable":
    let result = manager.createNippel("test-perf", Homestation)
    check result.isOk

    if result.isOk:
      var tree = manager.merkleTrees["test-perf"]

      # Add many files and measure time
      let startTime = cpuTime()

      for i in 1..100:
        discard addFile(tree, "file" & $i & ".txt", "xxh3-hash" & $i, int64(i * 100))

      let endTime = cpuTime()
      let duration = (endTime - startTime) * 1000.0  # Convert to ms

      echo "   ✅ Added 100 files in ", duration.formatFloat(ffDecimal, 2), " ms"

      # Verify performance is reasonable (< 100ms for 100 files)
      check duration < 100.0

echo "✅ All Task 8.4 tests completed"
