## Manifest Parser Tests
## Comprehensive test suite for format-agnostic manifest parsing

import std/[unittest, options, strutils]
import nip/manifest_parser
import nip/platform

suite "Semantic Versioning":
  test "Parse valid semver":
    let v = parseSemanticVersion("1.2.3")
    check v.major == 1
    check v.minor == 2
    check v.patch == 3
    check v.prerelease == ""
    check v.build == ""

  test "Parse semver with prerelease":
    let v = parseSemanticVersion("1.0.0-alpha.1")
    check v.major == 1
    check v.minor == 0
    check v.patch == 0
    check v.prerelease == "alpha.1"

  test "Parse semver with build metadata":
    let v = parseSemanticVersion("1.0.0+20130313144700")
    check v.major == 1
    check v.build == "20130313144700"

  test "Parse semver with both":
    let v = parseSemanticVersion("1.0.0-beta+exp.sha.5114f85")
    check v.prerelease == "beta"
    check v.build == "exp.sha.5114f85"

  test "Reject invalid semver":
    expect ManifestError:
      discard parseSemanticVersion("1.0")

    expect ManifestError:
      discard parseSemanticVersion("v1.2.3")

    expect ManifestError:
      discard parseSemanticVersion("1.2.x")

  test "Version comparison":
    let v1 = parseSemanticVersion("1.0.0")
    let v2 = parseSemanticVersion("1.0.1")
    let v3 = parseSemanticVersion("1.1.0")
    let v4 = parseSemanticVersion("2.0.0")

    check v1 < v2
    check v2 < v3
    check v3 < v4
    check v1 == v1
    check v4 > v1

  test "Prerelease comparison":
    let release = parseSemanticVersion("1.0.0")
    let prerelease = parseSemanticVersion("1.0.0-alpha")

    check prerelease < release  # Prerelease < release

suite "Version Constraints":
  test "Parse exact constraint":
    let c = parseVersionConstraint("1.2.3")
    check c.operator == OpExact
    check c.version.major == 1

  test "Parse >= constraint":
    let c = parseVersionConstraint(">=1.0.0")
    check c.operator == OpGreaterEq

  test "Parse ~ constraint":
    let c = parseVersionConstraint("~1.2.3")
    check c.operator == OpTilde

  test "Parse ^ constraint":
    let c = parseVersionConstraint("^2.0.0")
    check c.operator == OpCaret

  test "Parse * constraint":
    let c = parseVersionConstraint("*")
    check c.operator == OpAny

  test "Satisfy exact constraint":
    let v = parseSemanticVersion("1.2.3")
    let c = parseVersionConstraint("=1.2.3")
    check satisfiesConstraint(v, c)

    let v2 = parseSemanticVersion("1.2.4")
    check not satisfiesConstraint(v2, c)

  test "Satisfy >= constraint":
    let v1 = parseSemanticVersion("1.2.3")
    let v2 = parseSemanticVersion("1.2.4")
    let c = parseVersionConstraint(">=1.2.3")

    check satisfiesConstraint(v1, c)
    check satisfiesConstraint(v2, c)

  test "Satisfy ~ constraint":
    let c = parseVersionConstraint("~1.2.3")

    check satisfiesConstraint(parseSemanticVersion("1.2.3"), c)
    check satisfiesConstraint(parseSemanticVersion("1.2.4"), c)
    check not satisfiesConstraint(parseSemanticVersion("1.3.0"), c)
    check not satisfiesConstraint(parseSemanticVersion("2.0.0"), c)

  test "Satisfy ^ constraint":
    let c = parseVersionConstraint("^1.2.3")

    check satisfiesConstraint(parseSemanticVersion("1.2.3"), c)
    check satisfiesConstraint(parseSemanticVersion("1.3.0"), c)
    check satisfiesConstraint(parseSemanticVersion("1.9.9"), c)
    check not satisfiesConstraint(parseSemanticVersion("2.0.0"), c)

suite "Format Detection":
  test "Detect JSON format":
    let jsonContent = """{"name": "test"}"""
    check detectFormat(jsonContent) == FormatJSON

  test "Detect KDL format":
    let kdlContent = """package "test" { }"""
    check detectFormat(kdlContent) == FormatKDL

  test "Detect JSON array":
    let jsonArray = """[{"name": "test"}]"""
    check detectFormat(jsonArray) == FormatJSON

suite "JSON Manifest Parsing":
  test "Parse minimal valid manifest":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT"
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)
    check manifest.name == "test-package"
    check manifest.version.major == 1
    check manifest.license == "MIT"

  test "Parse manifest with dependencies":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "dependencies": [
    {"name": "dep1", "version": ">=1.0.0"},
    {"name": "dep2", "version": "~2.0.0", "optional": true}
  ]
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)
    check manifest.dependencies.len == 2
    check manifest.dependencies[0].name == "dep1"
    check manifest.dependencies[1].optional == true

  test "Parse manifest with platform constraints":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "supported_os": ["linux", "freebsd"],
  "supported_architectures": ["x86_64", "aarch64"]
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)
    check manifest.supportedOS.len == 2
    check "linux" in manifest.supportedOS
    check manifest.supportedArchitectures.len == 2
    check "x86_64" in manifest.supportedArchitectures

  test "Reject missing required field":
    let jsonContent = """
{
  "name": "test-package",
  "license": "MIT"
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Reject invalid semver":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0",
  "license": "MIT"
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Reject unknown field in strict mode":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "unknown_field": "contamination"
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Accept unknown field in lenient mode":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "unknown_field": "allowed"
}
"""
    # Should not raise in lenient mode
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationLenient)
    check manifest.name == "test-package"

suite "KDL Manifest Parsing":
  test "Parse minimal KDL manifest":
    let kdlContent = """
package "test-package" {
  version "1.0.0"
  license "MIT"
}
"""
    let manifest = parseManifest(kdlContent, NPK, FormatKDL, ValidationStrict)
    check manifest.name == "test-package"
    check manifest.version.major == 1
    check manifest.license == "MIT"

  test "Parse KDL manifest with dependencies":
    let kdlContent = """
package "test-package" {
  version "1.0.0"
  license "MIT"

  dependencies {
    "dep1" version=">=1.0.0"
    "dep2" version="~2.0.0" optional=true
  }
}
"""
    let manifest = parseManifest(kdlContent, NPK, FormatKDL, ValidationStrict)
    check manifest.dependencies.len == 2
    check manifest.dependencies[0].name == "dep1"
    check manifest.dependencies[1].optional == true

suite "Platform Validation":
  test "Validate valid OS":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "supported_os": ["linux", "freebsd"]
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)
    check manifest.supportedOS.len == 2

  test "Reject invalid OS in strict mode":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "supported_os": ["invalid_os"]
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Reject invalid architecture in strict mode":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "supported_architectures": ["invalid_arch"]
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Check platform compatibility":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "supported_os": ["linux"],
  "supported_architectures": ["x86_64"]
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

    # Create mock platform capabilities
    var caps = PlatformCapabilities(
      osType: Linux,
      hasUserNamespaces: true,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "6.1.0",
      isEmbedded: false,
      memoryTotal: 8589934592,
      cpuCount: 8
    )

    check checkPlatformCompatibility(manifest, caps)

    # Change to incompatible platform
    caps.osType = FreeBSD
    check not checkPlatformCompatibility(manifest, caps)

suite "Serialization":
  test "Serialize to JSON":
    let manifest = PackageManifest(
      format: NPK,
      name: "test-package",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      description: some("A test package"),
      supportedOS: @["linux", "freebsd"],
      supportedArchitectures: @["x86_64"]
    )

    let jsonStr = serializeManifestToJSON(manifest)
    check jsonStr.contains("test-package")
    check jsonStr.contains("1.0.0")
    check jsonStr.contains("MIT")

  test "Serialize to KDL":
    let manifest = PackageManifest(
      format: NPK,
      name: "test-package",
      version: parseSemanticVersion("1.0.0"),
      license: "MIT",
      description: some("A test package")
    )

    let kdlStr = serializeManifestToKDL(manifest)
    check kdlStr.contains("package \"test-package\"")
    check kdlStr.contains("version \"1.0.0\"")
    check kdlStr.contains("license \"MIT\"")

  test "Round-trip JSON":
    let original = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "description": "A test package"
}
"""
    let manifest = parseManifest(original, NPK, FormatJSON, ValidationStrict)
    let serialized = serializeManifestToJSON(manifest)
    let reparsed = parseManifest(serialized, NPK, FormatJSON, ValidationStrict)

    check reparsed.name == manifest.name
    check reparsed.version == manifest.version
    check reparsed.license == manifest.license

suite "Validation Rules":
  test "Required fields rule":
    let jsonContent = """
{
  "version": "1.0.0",
  "license": "MIT"
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

  test "Dependency validation":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "dependencies": [
    {"name": "dep1", "version": "invalid"}
  ]
}
"""
    expect ManifestError:
      discard parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)

suite "Format-Specific Fields":
  test "NPK-specific fields allowed":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "files": ["file1", "file2"]
}
"""
    let manifest = parseManifest(jsonContent, NPK, FormatJSON, ValidationStrict)
    check manifest.name == "test-package"

  test "NIP-specific fields allowed":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "desktop": "test.desktop"
}
"""
    let manifest = parseManifest(jsonContent, NIP, FormatJSON, ValidationStrict)
    check manifest.name == "test-package"

  test "NEXTER-specific fields allowed":
    let jsonContent = """
{
  "name": "test-package",
  "version": "1.0.0",
  "license": "MIT",
  "container": "test-container"
}
"""
    let manifest = parseManifest(jsonContent, NEXTER, FormatJSON, ValidationStrict)
    check manifest.name == "test-package"

when isMainModule:
  echo "Running manifest parser tests..."
