## Property-Based Tests for Package Metadata (metadata.json)
##
## **Feature:** 01-nip-unified-storage-and-formats
## **Property 10:** Provenance Preservation
## **Validates:** Requirements 7.1, 7.2, 7.3
##
## **Property Statement:**
## For any package, the metadata.json SHALL accurately reflect the complete build chain
##
## **TDD Strategy:**
## Test-driven approach to ensure complete provenance tracking across all formats.

import std/[unittest, times, options, tables, json]
import nip/package_metadata
import nip/manifest_parser

# ============================================================================
# Helper: Create Maximally Populated Metadata
# ============================================================================

proc createFullMetadata*(formatType: string): PackageMetadata =
  ## Create maximally populated metadata for testing
  ## Tests all fields including optional ones

  var envVars = initTable[string, string]()
  envVars["CC"] = "gcc"
  envVars["CFLAGS"] = "-O2"
  envVars["PATH"] = "/usr/bin:/bin"

  var extensibleMeta = initTable[string, string]()
  extensibleMeta["custom_field"] = "custom_value"
  extensibleMeta["build_id"] = "12345"

  result = PackageMetadata(
    formatType: formatType,
    formatVersion: "1.0",
    name: "test-package",
    version: "1.2.3-alpha.1+build.2",
    description: "Test package for metadata validation",
    license: "MIT",
    tags: @["test", "metadata", "provenance"],

    source: SourceProvenance(
      origin: "https://github.com/test/package",
      sourceHash: "xxh3-source-abc123",
      upstream: some("https://upstream.org/package"),
      upstreamVersion: some("1.2.3"),
      fetchedAt: parse("2025-11-20T10:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
      fetchMethod: "git"
    ),

    build: BuildProvenance(
      buildTimestamp: parse("2025-11-20T11:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
      builder: "ci-bot@nexus.os",
      buildHost: "build-server-01",
      buildDuration: some(3600),
      buildEnvironment: BuildEnvironment(
        compilerVersion: "gcc-13.2.0",
        compilerFlags: @["-O3", "-march=native", "-flto"],
        configureFlags: @["--prefix=/usr", "--enable-shared"],
        targetArchitecture: "x86_64",
        libc: "musl",
        allocator: "jemalloc",
        buildSystem: "cmake",
        environmentVars: envVars
      )
    ),

    installation: InstallationProvenance(
      installedAt: parse("2025-11-20T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
      installedBy: "admin",
      installPath: "/Programs/TestPackage/1.2.3",
      installMethod: "nip install",
      installHost: "workstation-01"
    ),

    hashes: IntegrityHashes(
      sourceHash: "xxh3-source-abc123",
      buildHash: "xxh3-build-def456",
      artifactHash: "xxh3-artifact-ghi789",
      manifestHash: "xxh3-manifest-jkl012"
    ),

    signatures: @[
      SignatureRecord(
        algorithm: "ed25519",
        keyId: "builder-key-2024",
        signature: "base64-signature-1",
        signedBy: "builder@nexus.os",
        signedAt: parse("2025-11-20T11:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
      ),
      SignatureRecord(
        algorithm: "ed25519",
        keyId: "maintainer-key-2024",
        signature: "base64-signature-2",
        signedBy: "maintainer@nexus.os",
        signedAt: parse("2025-11-20T11:45:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
      )
    ],

    metadata: extensibleMeta
  )

# ============================================================================
# Property-Based Tests
# ============================================================================

suite "Package Metadata Property Tests (Task 9.1)":

  test "Property 10: JSON Generation - Verify structure":
    ## **Phase 1:** Test JSON generation in isolation

    let metadata = createFullMetadata("npk")
    let jsonString = generateMetadataJson(metadata)

    echo "\n=== Generated JSON (Phase 1) ===\n"
    echo jsonString
    echo "\n=== End JSON ===\n"

    # Verify JSON structure
    let json = parseJson(jsonString)
    check json.hasKey("format_type")
    check json.hasKey("source_provenance")
    check json.hasKey("build_provenance")
    check json.hasKey("installation_provenance")
    check json.hasKey("integrity_hashes")
    check json.hasKey("signatures")

    # Verify required fields
    check json["format_type"].getStr() == "npk"
    check json["name"].getStr() == "test-package"
    check json["source_provenance"]["source_hash"].getStr().startsWith("xxh3-")
    check json["integrity_hashes"]["build_hash"].getStr().startsWith("xxh3-")

  test "Property 10: Full Roundtrip - ALL FIELDS":
    ## **Phase 2:** Full roundtrip test
    ##
    ## **Feature: 01-nip-unified-storage-and-formats, Property 10: Provenance Preservation**
    ## **Validates: Requirements 7.1, 7.2, 7.3**

    for formatType in ["npk", "nip", "nexter"]:
      let originalMetadata = createFullMetadata(formatType)

      # Step 1: Generate JSON
      let jsonString = generateMetadataJson(originalMetadata)
      check jsonString.len > 0

      # Step 2: Parse JSON back
      let parsedMetadata = parseMetadataJson(jsonString)

      # Step 3: Verify ALL fields preserved
      check parsedMetadata.formatType == originalMetadata.formatType
      check parsedMetadata.name == originalMetadata.name
      check parsedMetadata.version == originalMetadata.version
      check parsedMetadata.hashes.buildHash == originalMetadata.hashes.buildHash
      check parsedMetadata.hashes.sourceHash == originalMetadata.hashes.sourceHash
      check parsedMetadata.build.builder == originalMetadata.build.builder
      check parsedMetadata.source.origin == originalMetadata.source.origin

      # Step 4: Verify deterministic generation
      let jsonString2 = generateMetadataJson(parsedMetadata)

      # Parse both to compare structure (JSON key order may vary)
      let json1 = parseJson(jsonString)
      let json2 = parseJson(jsonString2)
      check json1 == json2

suite "Package Metadata Validation Tests":

  test "Validate metadata with valid xxh3 hashes":
    let validMetadata = createFullMetadata("npk")
    let issues = validateMetadata(validMetadata)
    check issues.len == 0

  test "Reject metadata with invalid hash format":
    var invalidMetadata = createFullMetadata("npk")
    invalidMetadata.hashes.buildHash = "sha256-invalid"

    let issues = validateMetadata(invalidMetadata)
    check issues.len > 0
    check issues[0].contains("xxh3-128")

  test "Reject metadata with invalid signature algorithm":
    var invalidMetadata = createFullMetadata("npk")
    invalidMetadata.signatures[0].algorithm = "rsa"

    let issues = validateMetadata(invalidMetadata)
    check issues.len > 0
    check issues[0].contains("ed25519")

  test "Property 10: Determinism - Same input produces same JSON structure":
    ## Verify that generateMetadataJson is deterministic
    ## (JSON key order may vary, but structure should be identical)

    let metadata = createFullMetadata("nip")

    # Generate JSON multiple times
    let json1 = parseJson(generateMetadataJson(metadata))
    let json2 = parseJson(generateMetadataJson(metadata))
    let json3 = parseJson(generateMetadataJson(metadata))

    # All outputs should be structurally identical
    check json1 == json2
    check json2 == json3
    check json1 == json3
