import std/[unittest, os, tempfiles, options, strutils]
import nip/system_integration
import nip/manifest_parser
import nip/cas

suite "System Integration Tests":
  
  setup:
    let tempDir = createTempDir("nip_test_", "")
    let casRoot = tempDir / "cas"
    let programsRoot = tempDir / "Programs"
    let systemIndexRoot = tempDir / "System/Index"
    
    createDir(casRoot)
    createDir(programsRoot)
    createDir(systemIndexRoot)
    
    # Initialize CAS
    discard initCasManager(casRoot, casRoot)
    
    let si = newSystemIntegrator(casRoot, programsRoot, systemIndexRoot)

  teardown:
    removeDir(tempDir)

  test "Install package with file reconstruction":
    # 1. Prepare CAS content
    let fileContent = "echo 'Hello World'"
    let casObj = storeObject(fileContent, casRoot)
    let fileHash = string(casObj.hash)
    
    # 2. Create Manifest
    var manifest = PackageManifest(
      name: "hello-world",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    
    manifest.files.add(FileSpec(
      path: "bin/hello",
      hash: fileHash,
      size: fileContent.len,
      permissions: "755"
    ))
    
    # 3. Install
    si.installPackage(manifest)
    
    # 4. Verify
    let installPath = programsRoot / "hello-world/1.0.0/hash123"
    let binPath = installPath / "bin/hello"
    
    check fileExists(binPath)
    check readFile(binPath) == fileContent
    
    # Verify permissions (basic check)
    let perms = getFilePermissions(binPath)
    check fpUserExec in perms

  test "Create symlinks":
    # 1. Prepare installed state (simulate previous step)
    let installPath = programsRoot / "hello-world/1.0.0/hash123"
    createDir(installPath / "bin")
    writeFile(installPath / "bin/hello", "binary")
    
    var manifest = PackageManifest(
      name: "hello-world",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    
    # 2. Run symlink creation (via installPackage or directly)
    # We'll run installPackage which calls createSymlinks
    # But we need files in manifest to avoid error in reconstructFiles?
    # No, reconstructFiles iterates manifest.files. If empty, it does nothing.
    # But we want to test symlinks, so we need the file to exist in installPath.
    # We already created it manually.
    # But reconstructFiles might overwrite it or fail if hash not in CAS.
    # So we should add file to manifest AND CAS.
    
    let fileContent = "binary"
    let casObj = storeObject(fileContent, casRoot)
    manifest.files.add(FileSpec(
      path: "bin/hello",
      hash: string(casObj.hash),
      size: fileContent.len,
      permissions: "755"
    ))
    
    si.installPackage(manifest)
    
    # 3. Verify Symlinks
    let currentLink = programsRoot / "hello-world/Current"
    check symlinkExists(currentLink)
    check expandSymlink(currentLink) == installPath
    
    let binLink = systemIndexRoot / "bin/hello"
    check symlinkExists(binLink)
    # The link should point to .../Current/bin/hello
    check expandSymlink(binLink).contains("Current")

  test "Service creation":
    var manifest = PackageManifest(
      name: "myservice",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    
    manifest.services.add(ServiceSpec(
      name: "myservice",
      content: "[Unit]\nDescription=My Service",
      enabled: true
    ))
    
    # We need to run as root for systemctl, so this part might fail or need mocking.
    # But writeFile should work if we have permissions on tempDir.
    # execCmd("systemctl") will fail.
    # We should probably mock execCmd or check for root/systemd.
    # For this test, we just check file creation.
    # But `manageServices` calls `execCmd`.
    # We can't easily mock `execCmd` in Nim without dependency injection or mixins.
    # However, `execCmd` failure just logs error in our implementation?
    # No, `discard execCmd` ignores result.
    
    si.installPackage(manifest)
    
    let serviceFile = systemIndexRoot / "lib/systemd/system/myservice.service"
    check fileExists(serviceFile)
    check readFile(serviceFile) == "[Unit]\nDescription=My Service"

  test "Remove package":
    # 1. Setup: Install a package
    let fileContent = "binary"
    let casObj = storeObject(fileContent, casRoot)
    
    var manifest = PackageManifest(
      name: "removable-pkg",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    
    manifest.files.add(FileSpec(
      path: "bin/removable",
      hash: string(casObj.hash),
      size: fileContent.len,
      permissions: "755"
    ))
    
    si.installPackage(manifest)
    
    # Verify installation
    let installPath = programsRoot / "removable-pkg/1.0.0/hash123"
    check fileExists(installPath / "bin/removable")
    check symlinkExists(programsRoot / "removable-pkg/Current")
    check symlinkExists(systemIndexRoot / "bin/removable")
    
    # Verify CAS reference
    check hasReferences(casRoot, casObj.hash)
    
    # 2. Remove package
    si.removePackage(manifest)
    
    # 3. Verify removal
    check not fileExists(installPath / "bin/removable")
    check not dirExists(installPath)
    check not symlinkExists(programsRoot / "removable-pkg/Current")
    check not symlinkExists(systemIndexRoot / "bin/removable")
    
    # Verify CAS reference removal
    check not hasReferences(casRoot, casObj.hash)
    
    # Verify package dir removal (since it was empty)
    check not dirExists(programsRoot / "removable-pkg")
