## Package Metadata (metadata.json) - Provenance Tracking
##
## **Purpose:**
## Defines the metadata.json format for complete provenance tracking across all package formats.
## This provides the audit trail from source to installation.
##
## **Design Principles:**
## - Complete provenance chain (source → build → installation)
## - Format-agnostic (works for NPK, NIP, NEXTER)
## - JSON format for machine readability
## - Cryptographic integrity (xxh3 for builds, Ed25519 for signatures)
##
## **Requirements:**
## - Requirement 7.1: source origin, maintainer, upstream URL, build timestamp
## - Requirement 7.2: compiler version, flags, target architecture, build hash
## - Requirement 7.3: complete chain from source to installation
## - Requirement 7.4: full audit trail
## - Requirement 7.5: xxh3 for build hashes, Ed25519 for signatures

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

type
  # ============================================================================
  # Package Metadata Types
  # ============================================================================

  PackageMetadata* = object
    ## Complete package metadata for provenance tracking
    # Format identification
    formatType*: string      ## "npk", "nip", or "nexter"
    formatVersion*: string   ## Metadata format version

    # Package identity
    name*: string
    version*: string
    description*: string
    license*: string

    # Source provenance
    source*: SourceProvenance

    # Build provenance
    build*: BuildProvenance

    # Installation provenance
    installation*: InstallationProvenance

    # Integrity hashes
    hashes*: IntegrityHashes

    # Signatures
    signatures*: seq[SignatureRecord]

    # Additional metadata
    tags*: seq[string]
    metadata*: Table[string, string]  ## Extensible metadata

  SourceProvenance* = object
    ## Source code provenance
    origin*: string          ## Source URL or repository
    sourceHash*: string      ## xxh3-128 hash of source
    upstream*: Option[string]  ## Upstream project URL
    upstreamVersion*: Option[string]  ## Upstream version
    fetchedAt*: DateTime     ## When source was fetched
    fetchMethod*: string     ## "http", "git", "local", etc.

  BuildProvenance* = object
    ## Build process provenance
    buildTimestamp*: DateTime
    builder*: string         ## Who/what built this package
    buildHost*: string       ## Hostname where built
    buildEnvironment*: BuildEnvironment
    buildDuration*: Option[int]  ## Build time in seconds

  BuildEnvironment* = object
    ## Build environment details
    compilerVersion*: string
    compilerFlags*: seq[string]
    configureFlags*: seq[string]
    targetArchitecture*: string
    libc*: string
    allocator*: string
    buildSystem*: string
    environmentVars*: Table[string, string]  ## Relevant env vars

  InstallationProvenance* = object
    ## Installation provenance
    installedAt*: DateTime
    installedBy*: string     ## User who installed
    installPath*: string     ## Installation path
    installMethod*: string   ## "nip install", "nip graft", etc.
    installHost*: string     ## Hostname where installed

  IntegrityHashes* = object
    ## Cryptographic hashes for integrity
    sourceHash*: string      ## xxh3-128 of source
    buildHash*: string       ## xxh3-128 of build configuration
    artifactHash*: string    ## xxh3-128 of final artifact
    manifestHash*: string    ## xxh3-128 of manifest.kdl

  SignatureRecord* = object
    ## Signature information
    algorithm*: string       ## "ed25519"
    keyId*: string
    signature*: string       ## Base64-encoded signature
    signedBy*: string        ## Signer identity
    signedAt*: DateTime

# ============================================================================
# JSON Generation
# ============================================================================

proc toJson*(metadata: PackageMetadata): JsonNode =
  ## Convert PackageMetadata to JSON
  ##
  ## **Requirements:**
  ## - Requirement 7.1: Include source origin, maintainer, upstream URL, build timestamp
  ## - Requirement 7.2: Include compiler version, flags, target architecture, build hash
  ## - Requirement 7.3: Record complete chain from source to installation

  result = %* {
    "format_type": metadata.formatType,
    "format_version": metadata.formatVersion,
    "name": metadata.name,
    "version": metadata.version,
    "description": metadata.description,
    "license": metadata.license,
    "tags": metadata.tags,

    "source_provenance": {
      "origin": metadata.source.origin,
      "source_hash": metadata.source.sourceHash,
      "fetched_at": metadata.source.fetchedAt.format("yyyy-MM-dd'T'HH:mm:ss'Z'"),
      "fetch_method": metadata.source.fetchMethod
    },

    "build_provenance": {
      "build_timestamp": metadata.build.buildTimestamp.format("yyyy-MM-dd'T'HH:mm:ss'Z'"),
      "builder": metadata.build.builder,
      "build_host": metadata.build.buildHost,
      "build_environment": {
        "compiler_version": metadata.build.buildEnvironment.compilerVersion,
        "compiler_flags": metadata.build.buildEnvironment.compilerFlags,
        "configure_flags": metadata.build.buildEnvironment.configureFlags,
        "target_architecture": metadata.build.buildEnvironment.targetArchitecture,
        "libc": metadata.build.buildEnvironment.libc,
        "allocator": metadata.build.buildEnvironment.allocator,
        "build_system": metadata.build.buildEnvironment.buildSystem
      }
    },

    "installation_provenance": {
      "installed_at": metadata.installation.installedAt.format("yyyy-MM-dd'T'HH:mm:ss'Z'"),
      "installed_by": metadata.installation.installedBy,
      "install_path": metadata.installation.installPath,
      "install_method": metadata.installation.installMethod,
      "install_host": metadata.installation.installHost
    },

    "integrity_hashes": {
      "source_hash": metadata.hashes.sourceHash,
      "build_hash": metadata.hashes.buildHash,
      "artifact_hash": metadata.hashes.artifactHash,
      "manifest_hash": metadata.hashes.manifestHash
    },

    "signatures": newJArray()
  }

  # Add optional fields
  if metadata.source.upstream.isSome:
    result["source_provenance"]["upstream"] = %metadata.source.upstream.get()
  if metadata.source.upstreamVersion.isSome:
    result["source_provenance"]["upstream_version"] = %metadata.source.upstreamVersion.get()
  if metadata.build.buildDuration.isSome:
    result["build_provenance"]["build_duration_seconds"] = %metadata.build.buildDuration.get()

  # Add environment variables if present
  if metadata.build.buildEnvironment.environmentVars.len > 0:
    result["build_provenance"]["build_environment"]["environment_vars"] = newJObject()
    for key, val in metadata.build.buildEnvironment.environmentVars:
      result["build_provenance"]["build_environment"]["environment_vars"][key] = %val

  # Add signatures
  for sig in metadata.signatures:
    result["signatures"].add(%* {
      "algorithm": sig.algorithm,
      "key_id": sig.keyId,
      "signature": sig.signature,
      "signed_by": sig.signedBy,
      "signed_at": sig.signedAt.format("yyyy-MM-dd'T'HH:mm:ss'Z'")
    })

  # Add extensible metadata
  if metadata.metadata.len > 0:
    result["metadata"] = newJObject()
    for key, val in metadata.metadata:
      result["metadata"][key] = %val

proc generateMetadataJson*(metadata: PackageMetadata): string =
  ## Generate JSON string from PackageMetadata
  ##
  ## **Requirements:**
  ## - Requirement 7.4: Provide full audit trail
  ## - Requirement 7.5: Use xxh3 for build hashes, Ed25519 for signatures

  let jsonNode = metadata.toJson()
  result = jsonNode.pretty(indent = 2)

# ============================================================================
# JSON Parsing
# ============================================================================

proc parseMetadataJson*(jsonStr: string): PackageMetadata =
  ## Parse metadata.json from JSON string
  ##
  ## **Requirements:**
  ## - Requirement 7.3: Parse complete chain from source to installation

  let json = parseJson(jsonStr)

  # Parse source provenance
  let sourceProv = json["source_provenance"]
  var source = SourceProvenance(
    origin: sourceProv["origin"].getStr(),
    sourceHash: sourceProv["source_hash"].getStr(),
    fetchedAt: parse(sourceProv["fetched_at"].getStr(), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
    fetchMethod: sourceProv["fetch_method"].getStr()
  )
  if sourceProv.hasKey("upstream"):
    source.upstream = some(sourceProv["upstream"].getStr())
  if sourceProv.hasKey("upstream_version"):
    source.upstreamVersion = some(sourceProv["upstream_version"].getStr())

  # Parse build provenance
  let buildProv = json["build_provenance"]
  let buildEnv = buildProv["build_environment"]

  var envVars = initTable[string, string]()
  if buildEnv.hasKey("environment_vars"):
    for key, val in buildEnv["environment_vars"]:
      envVars[key] = val.getStr()

  var build = BuildProvenance(
    buildTimestamp: parse(buildProv["build_timestamp"].getStr(), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
    builder: buildProv["builder"].getStr(),
    buildHost: buildProv["build_host"].getStr(),
    buildEnvironment: BuildEnvironment(
      compilerVersion: buildEnv["compiler_version"].getStr(),
      compilerFlags: buildEnv["compiler_flags"].to(seq[string]),
      configureFlags: buildEnv["configure_flags"].to(seq[string]),
      targetArchitecture: buildEnv["target_architecture"].getStr(),
      libc: buildEnv["libc"].getStr(),
      allocator: buildEnv["allocator"].getStr(),
      buildSystem: buildEnv["build_system"].getStr(),
      environmentVars: envVars
    )
  )
  if buildProv.hasKey("build_duration_seconds"):
    build.buildDuration = some(buildProv["build_duration_seconds"].getInt())

  # Parse installation provenance
  let installProv = json["installation_provenance"]
  let installation = InstallationProvenance(
    installedAt: parse(installProv["installed_at"].getStr(), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
    installedBy: installProv["installed_by"].getStr(),
    installPath: installProv["install_path"].getStr(),
    installMethod: installProv["install_method"].getStr(),
    installHost: installProv["install_host"].getStr()
  )

  # Parse integrity hashes
  let hashesJson = json["integrity_hashes"]
  let hashes = IntegrityHashes(
    sourceHash: hashesJson["source_hash"].getStr(),
    buildHash: hashesJson["build_hash"].getStr(),
    artifactHash: hashesJson["artifact_hash"].getStr(),
    manifestHash: hashesJson["manifest_hash"].getStr()
  )

  # Parse signatures
  var signatures: seq[SignatureRecord] = @[]
  for sigJson in json["signatures"]:
    signatures.add(SignatureRecord(
      algorithm: sigJson["algorithm"].getStr(),
      keyId: sigJson["key_id"].getStr(),
      signature: sigJson["signature"].getStr(),
      signedBy: sigJson["signed_by"].getStr(),
      signedAt: parse(sigJson["signed_at"].getStr(), "yyyy-MM-dd'T'HH:mm:ss'Z'")
    ))

  # Parse extensible metadata
  var metadataTable = initTable[string, string]()
  if json.hasKey("metadata"):
    for key, val in json["metadata"]:
      metadataTable[key] = val.getStr()

  result = PackageMetadata(
    formatType: json["format_type"].getStr(),
    formatVersion: json["format_version"].getStr(),
    name: json["name"].getStr(),
    version: json["version"].getStr(),
    description: json["description"].getStr(),
    license: json["license"].getStr(),
    tags: json["tags"].to(seq[string]),
    source: source,
    build: build,
    installation: installation,
    hashes: hashes,
    signatures: signatures,
    metadata: metadataTable
  )

# ============================================================================
# Validation
# ============================================================================

proc validateMetadata*(metadata: PackageMetadata): seq[string] =
  ## Validate package metadata and return list of issues
  ##
  ## **Requirements:**
  ## - Requirement 7.5: Validate xxh3 for build hashes, Ed25519 for signatures

  result = @[]

  # Validate format type
  if metadata.formatType notin ["npk", "nip", "nexter"]:
    result.add("Format type must be 'npk', 'nip', or 'nexter'")

  # Validate hashes use xxh3
  if not metadata.hashes.sourceHash.startsWith("xxh3-"):
    result.add("Source hash must use xxh3-128 format")
  if not metadata.hashes.buildHash.startsWith("xxh3-"):
    result.add("Build hash must use xxh3-128 format")
  if not metadata.hashes.artifactHash.startsWith("xxh3-"):
    result.add("Artifact hash must use xxh3-128 format")
  if not metadata.hashes.manifestHash.startsWith("xxh3-"):
    result.add("Manifest hash must use xxh3-128 format")

  # Validate signatures use Ed25519
  for sig in metadata.signatures:
    if sig.algorithm != "ed25519":
      result.add("Signature algorithm must be 'ed25519'")

# ============================================================================
# Convenience Functions
# ============================================================================

proc `$`*(metadata: PackageMetadata): string =
  ## Convert metadata to human-readable string
  result = "Package: " & metadata.name & " v" & metadata.version & "\n"
  result.add("Format: " & metadata.formatType & "\n")
  result.add("Source: " & metadata.source.origin & "\n")
  result.add("Built by: " & metadata.build.builder & " on " & metadata.build.buildHost & "\n")
  result.add("Installed: " & metadata.installation.installPath & "\n")
