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

suite "NIP Installer Tests":
  
  setup:
    let tempDir = createTempDir("nip_test_installer_", "")
    let casRoot = tempDir / "cas"
    
    # Mock home directory structure
    let mockHome = tempDir / "home"
    createDir(mockHome / ".local/share/nexus/nips")
    createDir(mockHome / ".local/share/applications")
    createDir(casRoot)
    
    discard initCasManager(casRoot, casRoot)
    
    # We need to override getHomeDir or pass paths manually.
    # nip_installer uses getHomeDir(). We should probably allow overriding paths in constructor.
    # Wait, newNipInstaller takes casRoot. It calculates others from getHomeDir().
    # I should modify newNipInstaller to accept overrides or use environment variable.
    # Or just subclass/mock? Nim doesn't do classes like that easily.
    # Let's modify nip_installer to allow path overrides for testing.
    
    # For now, I'll manually construct the object since fields are exported?
    # No, fields are exported (*).
    
    var ni = NipInstaller(
      casRoot: casRoot,
      installRoot: mockHome / ".local/share/nexus/nips",
      appsRoot: mockHome / ".local/share/applications",
      dryRun: false
    )

  teardown:
    removeDir(tempDir)

  test "Install NIP with Desktop Integration":
    # 1. Prepare CAS
    let fileContent = "binary data"
    let casObj = storeObject(fileContent, ni.casRoot)
    
    # 2. Create Manifest
    var manifest = PackageManifest(
      name: "test-app",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    
    manifest.files.add(FileSpec(
      path: "bin/app",
      hash: string(casObj.hash),
      size: fileContent.len,
      permissions: "755"
    ))
    
    manifest.desktop = some(DesktopIntegration(
      displayName: "Test App",
      categories: @["Utility"],
      terminal: true
    ))
    
    # 3. Install
    ni.installNip(manifest)
    
    # 4. Verify Files
    let installPath = ni.installRoot / "test-app/1.0.0/hash123"
    check fileExists(installPath / "bin/app")
    check readFile(installPath / "bin/app") == fileContent
    
    # 5. Verify Symlink
    let currentLink = ni.installRoot / "test-app/Current"
    check symlinkExists(currentLink)
    check expandSymlink(currentLink) == installPath
    
    # 6. Verify Desktop File
    let desktopFile = ni.appsRoot / "test-app.desktop"
    check fileExists(desktopFile)
    let content = readFile(desktopFile)
    check content.contains("Name=Test App")
    check content.contains("Exec=nip run test-app")
    check content.contains("Categories=Utility;")
    check content.contains("Terminal=true")
    
    # 7. Verify CAS Reference
    check hasReferences(ni.casRoot, casObj.hash)

  test "Remove NIP":
    # 1. Setup (Install first)
    let fileContent = "data"
    let casObj = storeObject(fileContent, ni.casRoot)
    var manifest = PackageManifest(
      name: "remove-me",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      artifactHash: "hash123"
    )
    manifest.files.add(FileSpec(path: "file", hash: string(casObj.hash)))
    manifest.desktop = some(DesktopIntegration(displayName: "Remove Me"))
    
    ni.installNip(manifest)
    check fileExists(ni.appsRoot / "remove-me.desktop")
    
    # 2. Remove
    ni.removeNip(manifest)
    
    # 3. Verify
    check not fileExists(ni.appsRoot / "remove-me.desktop")
    check not dirExists(ni.installRoot / "remove-me")
    check not hasReferences(ni.casRoot, casObj.hash)
