## nimpak/cli/track_commands.nim
## Enhanced track command implementations for NimPak CLI
##
## This module implements the nip track command with comprehensive
## provenance tracking, trust scoring, and structured output support.

import std/[os, strutils, times, json, sequtils, strformat, algorithm, tables, options]
import core, ../security/[provenance_tracker, keyring_manager, event_logger]

type
  TrackCommandOptions* = object
    packageName*: string            # Package to track
    version*: string               # Specific version (optional)
    showTrustScore*: bool          # Show detailed trust scoring
    showVerification*: bool        # Show verification details
    showSteps*: bool              # Show individual provenance steps
    outputFormat*: OutputFormat   # Output format
    verbose*: bool                # Verbose output
    validateChain*: bool          # Perform chain validation

  TrustScoreBreakdown* = object
    overallScore*: float
    stepScores*: seq[tuple[stepType: string, score: float]]
    sourceBonus*: float
    completenessBonus*: float
    verificationPenalty*: float
    ageFactors*: seq[tuple[step: string, ageDays: int, penalty: float]]

# =============================================================================
# Track Command Options Parsing
# =============================================================================

proc parseTrackCommandOptions*(args: seq[string]): TrackCommandOptions =
  ## Parse nip track command arguments
  var options = TrackCommandOptions(
    packageName: "",
    version: "",
    showTrustScore: false,
    showVerification: false,
    showSteps: true,
    outputFormat: OutputHuman,
    verbose: false,
    validateChain: true
  )

  if args.len == 0:
    raise newException(ValueError, "Usage: nip track <package> [options]")

  options.packageName = args[0]

  var i = 1
  while i < args.len:
    case args[i]:
    of "--version":
      if i + 1 < args.len:
        options.version = args[i + 1]
        i += 1
      else:
        raise newException(ValueError, "--version requires a value")

    of "--trust-score":
      options.showTrustScore = true

    of "--verification":
      options.showVerification = true

    of "--no-steps":
      options.showSteps = false

    of "--no-validation":
      options.validateChain = false

    of "--verbose", "-v":
      options.verbose = true

    of "--output":
      if i + 1 < args.len:
        case args[i + 1].toLower():
        of "json": options.outputFormat = OutputJson
        of "yaml": options.outputFormat = OutputYaml
        of "kdl": options.outputFormat = OutputKdl
        else: options.outputFormat = OutputHuman
        i += 1
      else:
        raise newException(ValueError, "--output requires a value")

    else:
      raise newException(ValueError, fmt"Unknown option: {args[i]}")

    i += 1

  return options

# =============================================================================
# Trust Score Analysis
# =============================================================================

proc analyzeTrustScore*(chain: ProvenanceChain, calculator: TrustCalculator): TrustScoreBreakdown =
  ## Analyze trust score breakdown for detailed reporting
  var breakdown = TrustScoreBreakdown(
    overallScore: chain.trustScore,
    stepScores: @[],
    sourceBonus: 0.0,
    completenessBonus: 0.0,
    verificationPenalty: 0.0,
    ageFactors: @[]
  )

  # Calculate individual step scores
  for step in chain.steps:
    let stepScore = calculateStepTrust(calculator, step)
    breakdown.stepScores.add((step.stepType, stepScore))

    # Calculate age factors
    let age = (now().utc() - step.timestamp).inDays
    var agePenalty = 0.0
    if age > calculator.policy.maxProvenanceAge:
      agePenalty = 0.2
    elif age > (calculator.policy.maxProvenanceAge div 2):
      agePenalty = 0.1

    if agePenalty > 0.0:
      breakdown.ageFactors.add((step.stepType, age.int, agePenalty))

  # Calculate source bonus
  case chain.source:
  of SourceOriginal: breakdown.sourceBonus = 0.1
  of SourceGrafted: breakdown.sourceBonus = -0.05
  of SourceConverted: breakdown.sourceBonus = -0.02
  of SourceRebuilt: breakdown.sourceBonus = 0.05
  of SourceMirrored: breakdown.sourceBonus = -0.1

  # Calculate completeness bonus
  let hasSource = chain.steps.anyIt(it.stepType == "source")
  let hasBuild = chain.steps.anyIt(it.stepType == "build")
  let hasSign = chain.steps.anyIt(it.stepType == "sign")

  if hasSource and hasBuild and hasSign:
    breakdown.completenessBonus = 0.1
  elif hasSource and hasBuild:
    breakdown.completenessBonus = 0.05

  # Calculate verification penalty
  if chain.verificationErrors.len > 0:
    breakdown.verificationPenalty = chain.verificationErrors.len.float * 0.1

  return breakdown

# =============================================================================
# Human-Readable Display Functions
# =============================================================================

proc displayProvenanceChain*(chain: ProvenanceChain, options: TrackCommandOptions,
                            breakdown: TrustScoreBreakdown) =
  ## Display provenance chain in human-readable format
  echo bold("🔍 Package Provenance Report")
  echo "=".repeat(50)
  echo fmt"Package: {bold(chain.packageId)} v{chain.version}"
  echo fmt"Source Type: {chain.source}"

  if chain.originalUrl.isSome():
    echo fmt"Original URL: {chain.originalUrl.get()}"

  echo fmt"Trust Score: {chain.trustScore:.2f}"
  let lastVerifiedStr = if chain.lastVerified != default(times.DateTime): $chain.lastVerified else: "Never"
  echo fmt"Last Verified: {lastVerifiedStr}"
  echo ""

  # Show trust score breakdown if requested
  if options.showTrustScore:
    # TODO: Implement displayTrustScoreBreakdown function
    echo "Trust Score Breakdown:"
    echo fmt"  Overall Score: {breakdown.overallScore:.2f}"
    echo fmt"  Source Bonus: {breakdown.sourceBonus:.2f}"
    echo fmt"  Completeness Bonus: {breakdown.completenessBonus:.2f}"

  # Show verification status if requested
  if options.showVerification:
    # TODO: Implement displayVerificationStatus function
    echo "Verification Status:"
    echo fmt"  Verified Steps: {chain.steps.countIt(it.verified)}/{chain.steps.len}"

  # Show provenance steps
  if options.showSteps:
    # TODO: Implement displayProvenanceSteps function
    echo "Provenance Steps:"
    for i, step in chain.steps:
      echo fmt"  {i+1}. {step.stepType} - {step.actor}"

  # Show verification errors if any
  if chain.verificationErrors.len > 0:
    echo bold("⚠️  Verification Issues:")
    for error in chain.verificationErrors:
      echo fmt"  • {error}"
    echo ""

proc formatTrustScore*(score: float): string =
  ## Format trust score with color coding
  let scoreStr = fmt"{score:.3f}"
  if score >= 0.8:
    return success(scoreStr & " (High)")
  elif score >= 0.6:
    return warning(scoreStr & " (Medium)")
  else:
    return error(scoreStr & " (Low)")

proc displayTrustScoreBreakdown*(breakdown: TrustScoreBreakdown) =
  ## Display detailed trust score breakdown
  echo bold("📊 Trust Score Breakdown:")
  echo fmt"Overall Score: {formatTrustScore(breakdown.overallScore)}"
  echo ""

  echo "Step Scores:"
  for (stepType, score) in breakdown.stepScores:
    let scoreStr = fmt"{score:.3f}"
    let coloredScore = if score >= 0.7: success(scoreStr) elif score >= 0.5: warning(scoreStr) else: error(scoreStr)
    echo fmt"  • {stepType.capitalizeAscii()}: {coloredScore}"

  if breakdown.sourceBonus != 0.0:
    let bonusStr = if breakdown.sourceBonus > 0: success(fmt"+{breakdown.sourceBonus:.3f}") else: error(fmt"{breakdown.sourceBonus:.3f}")
    echo fmt"Source Bonus: {bonusStr}"

  if breakdown.completenessBonus > 0.0:
    let completenessStr = success(fmt"+{breakdown.completenessBonus:.3f}")
    echo fmt"Completeness Bonus: {completenessStr}"

  if breakdown.verificationPenalty > 0.0:
    let penaltyStr = error(fmt"-{breakdown.verificationPenalty:.3f}")
    echo fmt"Verification Penalty: {penaltyStr}"

  if breakdown.ageFactors.len > 0:
    echo "Age Penalties:"
    for (step, ageDays, penalty) in breakdown.ageFactors:
      let penaltyStr = error(fmt"-{penalty:.3f}")
      echo fmt"  • {step}: {ageDays} days old, penalty: {penaltyStr}"

  echo ""

proc displayVerificationStatus*(chain: ProvenanceChain) =
  ## Display verification status details
  echo bold("🔐 Verification Status:")

  let verifiedSteps = chain.steps.countIt(it.verified)
  let totalSteps = chain.steps.len

  if verifiedSteps == totalSteps:
    let verifiedStr = success(fmt"{verifiedSteps}/{totalSteps}")
    echo fmt"All steps verified: {verifiedStr}"
  else:
    let verifiedStr = warning(fmt"{verifiedSteps}/{totalSteps}")
    echo fmt"Steps verified: {verifiedStr}"

  # Show signature status
  let signedSteps = chain.steps.countIt(it.signature.isSome())
  if signedSteps > 0:
    echo fmt"Cryptographically signed steps: {signedSteps}"
  else:
    echo warning("No cryptographic signatures found")

  echo ""

proc displayProvenanceSteps*(steps: seq[ProvenanceStep], verbose: bool) =
  ## Display individual provenance steps
  echo bold("📋 Provenance Chain:")

  for i, step in steps:
    let stepNum = i + 1
    let verifiedSymbol = if step.verified: success("✅") else: error("❌")
    let signedSymbol = if step.signature.isSome(): "🔐" else: "  "

    echo fmt"{stepNum}. {verifiedSymbol} {signedSymbol} {step.stepType.toUpper()}"
    echo fmt"    Timestamp: {step.timestamp}"
    echo fmt"    Actor: {step.actor}"
    echo fmt"    Location: {step.location}"

    if verbose:
      echo fmt"    Input Hash: {step.inputHash[0..min(15, step.inputHash.high)]}..."
      echo fmt"    Output Hash: {step.outputHash[0..min(15, step.outputHash.high)]}..."

      if step.metadata != nil and step.metadata.kind != JNull:
        echo "    Metadata:"
        for key, value in step.metadata.pairs:
          echo fmt"      {key}: {value}"

      if step.signature.isSome():
        let sig = step.signature.get()
        echo fmt"    Signature: {sig.algorithm} (Key: {sig.keyId})"

    echo ""

# =============================================================================
# Structured Output Functions
# =============================================================================

proc formatProvenanceForOutput*(chain: ProvenanceChain, breakdown: TrustScoreBreakdown,
                               options: TrackCommandOptions): JsonNode =
  ## Format provenance data for structured output
  var result = %*{
    "package_id": chain.packageId,
    "version": chain.version,
    "source_type": $chain.source,
    "trust_score": chain.trustScore,
    "last_verified": if chain.lastVerified != default(times.DateTime): newJString($chain.lastVerified) else: newJNull(),
    "verification_errors": chain.verificationErrors
  }

  if chain.originalUrl.isSome():
    result["original_url"] = %chain.originalUrl.get()

  # Add trust score breakdown if requested
  if options.showTrustScore:
    result["trust_breakdown"] = %*{
      "overall_score": breakdown.overallScore,
      "step_scores": breakdown.stepScores.mapIt(%*{
        "step_type": it[0],
        "score": it[1]
      }),
      "source_bonus": breakdown.sourceBonus,
      "completeness_bonus": breakdown.completenessBonus,
      "verification_penalty": breakdown.verificationPenalty,
      "age_factors": breakdown.ageFactors.mapIt(%*{
        "step": it[0],
        "age_days": it[1],
        "penalty": it[2]
      })
    }

  # Add verification details if requested
  if options.showVerification:
    let verifiedSteps = chain.steps.countIt(it.verified)
    let signedSteps = chain.steps.countIt(it.signature.isSome())

    result["verification_details"] = %*{
      "total_steps": chain.steps.len,
      "verified_steps": verifiedSteps,
      "signed_steps": signedSteps,
      "verification_rate": if chain.steps.len > 0: (verifiedSteps.float / chain.steps.len.float) else: 0.0
    }

  # Add provenance steps if requested
  if options.showSteps:
    result["provenance_steps"] = %chain.steps.mapIt(%*{
      "step_type": it.stepType,
      "timestamp": $it.timestamp,
      "actor": it.actor,
      "location": it.location,
      "input_hash": it.inputHash,
      "output_hash": it.outputHash,
      "verified": it.verified,
      "has_signature": it.signature.isSome(),
      "metadata": it.metadata
    })

  return result

# =============================================================================
# Main Track Command Implementation
# =============================================================================

proc executeTrackCommand*(options: TrackCommandOptions): CommandResult =
  ## Execute the nip track command
  try:
    if options.verbose:
      showInfo(fmt"🔍 Tracking provenance for: {options.packageName}")

    # Initialize provenance tracker
    let config = getDefaultProvenanceConfig()
    var tracker = newProvenanceTracker(config)

    # Get provenance chain
    let chainOpt = tracker.getProvenance(options.packageName, options.version)

    if chainOpt.isNone():
      # Try to create provenance for installed package
      let installedChain = trackPackageProvenance(options.packageName, options.version)

      if installedChain.isNone():
        return errorResult(fmt"No provenance information found for package: {options.packageName}")

      # Store the found provenance
      tracker.storeProvenance(installedChain.get())

    let chain = if chainOpt.isSome(): chainOpt.get() else: trackPackageProvenance(options.packageName, options.version).get()

    # Validate chain if requested
    if options.validateChain:
      if options.verbose:
        showInfo("🔐 Validating provenance chain...")

      var mutableChain = chain
      let isValid = verifyProvenanceChain(tracker.trustCalculator, mutableChain)

      if not isValid and options.verbose:
        showWarning(fmt"Provenance chain validation found {mutableChain.verificationErrors.len} issues")

    # Calculate trust score breakdown
    let breakdown = analyzeTrustScore(chain, tracker.trustCalculator)

    # Display results
    case options.outputFormat:
    of OutputHuman:
      displayProvenanceChain(chain, options, breakdown)

      # Show trust policy compliance
      let (policyCompliant, policyReason) = checkTrustPolicy(chain, tracker.trustCalculator.policy)
      echo bold("📋 Trust Policy Compliance:")
      if policyCompliant:
        echo success("✅ Package meets trust policy requirements")
      else:
        echo error(fmt"❌ Policy violation: {policyReason}")

    else:
      # Structured output
      let outputData = formatProvenanceForOutput(chain, breakdown, options)

      # Add policy compliance
      let (policyCompliant, policyReason) = checkTrustPolicy(chain, tracker.trustCalculator.policy)
      outputData["policy_compliance"] = %*{
        "compliant": policyCompliant,
        "reason": policyReason
      }

      outputData(outputData)

    # Log tracking event
    logGlobalSecurityEvent(EventPackageVerification, SeverityInfo, "nip-track",
      fmt"Provenance tracked for {options.packageName}: trust score {chain.trustScore:.3f}")

    # Return result based on trust score and policy compliance
    let (policyCompliant, policyReason) = checkTrustPolicy(chain, tracker.trustCalculator.policy)

    if policyCompliant and chain.trustScore >= tracker.trustCalculator.policy.minimumTrustScore:
      return successResult(fmt"Provenance tracking completed: {options.packageName} has high trust (score: {chain.trustScore:.3f})")
    elif policyCompliant:
      return successResult(fmt"Provenance tracking completed: {options.packageName} meets policy but has medium trust (score: {chain.trustScore:.3f})")
    else:
      return errorResult(fmt"Provenance tracking completed: {options.packageName} fails trust policy - {policyReason}", 1)

  except Exception as e:
    return errorResult(fmt"Track command failed: {e.msg}")

# =============================================================================
# Provenance Creation Helpers
# =============================================================================

proc createProvenanceForGraftedPackage*(packageName: string, version: string,
                                       sourceEcosystem: string, originalUrl: string,
                                       packageHash: string): ProvenanceChain =
  ## Create provenance chain for a newly grafted package
  var chain = createGraftProvenance(packageName, version, sourceEcosystem, originalUrl, packageHash)

  # Add additional metadata for better tracking
  if chain.steps.len > 0:
    chain.steps[^1].metadata["graft_timestamp"] = %($now())
    chain.steps[^1].metadata["nimpak_version"] = %"1.0.0"  # TODO: Get actual version

  return chain

proc createProvenanceForBuiltPackage*(packageName: string, version: string,
                                     sourceUrl: string, sourceHash: string,
                                     buildHash: string, buildFlags: seq[string]): ProvenanceChain =
  ## Create provenance chain for a package built from source
  var chain = newProvenanceChain(packageName, version, SourceOriginal)
  chain.originalUrl = some(sourceUrl)

  # Add source step
  let sourceStep = createSourceStep(sourceUrl, sourceHash, "nimpak-fetcher")
  chain.steps.add(sourceStep)

  # Add build step
  let buildStep = createBuildStep("nim", buildFlags, "nimpak-builder", "build-farm", sourceHash, buildHash)
  chain.steps.add(buildStep)

  return chain

# =============================================================================
# Export main functions
# =============================================================================

export TrackCommandOptions, TrustScoreBreakdown
export parseTrackCommandOptions, executeTrackCommand
export analyzeTrustScore, formatProvenanceForOutput
export createProvenanceForGraftedPackage, createProvenanceForBuiltPackage