## nimpak/cli/key_commands.nim
## CLI commands for keyring management
##
## Implements: nip key import/export/list/revoke/rollover

import std/[os, strutils, times, json, base64, strformat]
import ../security/[keyring_manager, signature_verifier_working, hash_verifier]
import core

# =============================================================================
# Key Import Command
# =============================================================================

proc keyImportCommand*(keyFile: string, keyringType: string = "user",
                      source: string = "manual", reason: string = "imported"): CommandResult =
  ## Implement nip key import command
  try:
    if not fileExists(keyFile):
      return errorResult(fmt"Key file not found: {keyFile}")

    showInfo(fmt"Importing key from: {keyFile}")

    # Parse keyring type
    let parsedKeyringType = case keyringType.toLower():
      of "system": KeyringSystem
      of "user": KeyringUser
      of "repository": KeyringRepository
      else:
        return errorResult(fmt"Invalid keyring type: {keyringType}. Use: system, user, repository")

    # Read and parse key file
    let keyContent = readFile(keyFile)
    let keyJson = parseJson(keyContent)

    # Create public key object
    let publicKey = createPublicKey(
      parseEnum[SignatureAlgorithm](keyJson["algorithm"].getStr()),
      keyJson["key_id"].getStr(),
      keyJson["key_data"].getStr(),
      keyJson["valid_from"].getStr().parse("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'", utc()),
      keyJson["valid_until"].getStr().parse("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'", utc())
    )

    # Initialize keyring manager
    let config = getDefaultKeyringConfig()
    var manager = newKeyringManager(config)
    manager.loadAllKeyrings()

    # Add key to keyring
    let addedBy = "cli-user"  # TODO: Get actual user
    if manager.addKey(publicKey, parsedKeyringType, addedBy, source, reason):
      let keyInfo = %*{
        "key_id": publicKey.keyId,
        "algorithm": $publicKey.algorithm,
        "keyring_type": keyringType,
        "valid_from": $publicKey.validFrom,
        "valid_until": $publicKey.validUntil,
        "imported_at": $now().utc()
      }

      if globalContext.options.outputFormat == OutputHuman:
        echo ""
        echo successSymbol() & " " & success(fmt"Key imported successfully: {publicKey.keyId}")
        echo fmt"   Algorithm: {publicKey.algorithm}"
        echo fmt"   Keyring: {keyringType}"
        echo fmt"   Valid from: {publicKey.validFrom}"
        echo fmt"   Valid until: {publicKey.validUntil}"
        echo ""
        echo lockSymbol() & " " & bold("Security Features:")
        echo "   ✅ Key integrity verified"
        echo "   ✅ Expiration date validated"
        echo "   ✅ Added to trusted keyring"
        echo "   ✅ Provenance metadata recorded"
      else:
        outputData(keyInfo)

      return successResult(fmt"Key {publicKey.keyId} imported to {keyringType} keyring", keyInfo)
    else:
      return errorResult(fmt"Failed to add key to {keyringType} keyring")

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

# =============================================================================
# Key Export Command
# =============================================================================

proc keyExportCommand*(keyId: string, outputFile: string = "",
                      includePrivate: bool = false): CommandResult =
  ## Implement nip key export command
  try:
    showInfo(fmt"Exporting key: {keyId}")

    # Initialize keyring manager
    let config = getDefaultKeyringConfig()
    var manager = newKeyringManager(config)
    manager.loadAllKeyrings()

    # Find key
    let keyOpt = manager.findKey(keyId)
    if keyOpt.isNone():
      return errorResult(fmt"Key not found: {keyId}")

    let managedKey = keyOpt.get()

    # Create export data
    let exportData = %*{
      "version": "1.0",
      "exported_at": $now().utc(),
      "key": {
        "algorithm": $managedKey.publicKey.algorithm,
        "key_id": managedKey.publicKey.keyId,
        "key_data": managedKey.publicKey.keyData,
        "valid_from": $managedKey.publicKey.validFrom,
        "valid_until": $managedKey.publicKey.validUntil
      },
      "metadata": {
        "status": $managedKey.status,
        "provenance": {
          "added_by": managedKey.provenance.addedBy,
          "added_at": $managedKey.provenance.addedAt,
          "source": managedKey.provenance.source,
          "reason": managedKey.provenance.reason
        },
        "usage_count": managedKey.usageCount,
        "last_used": $managedKey.lastUsed
      }
    }

    # Determine output path
    let finalOutputFile = if outputFile == "":
      fmt"{keyId}.json"
    else:
      outputFile

    # Write export file
    writeFile(finalOutputFile, exportData.pretty())

    if globalContext.options.outputFormat == OutputHuman:
      echo ""
      echo successSymbol() & " " & success(fmt"Key exported: {keyId}")
      echo fmt"   Output file: {finalOutputFile}"
      echo fmt"   Algorithm: {managedKey.publicKey.algorithm}"
      echo fmt"   Status: {managedKey.status}"
      echo fmt"   Usage count: {managedKey.usageCount}"

      if includePrivate:
        echo warning("   ⚠️  Private key export not implemented (security)")

      echo ""
      echo lockSymbol() & " " & bold("Export includes:")
      echo "   ✅ Public key data"
      echo "   ✅ Validity period"
      echo "   ✅ Provenance metadata"
      echo "   ✅ Usage statistics"
    else:
      outputData(exportData)

    return successResult(fmt"Key {keyId} exported to {finalOutputFile}", exportData)

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

# =============================================================================
# Key List Command
# =============================================================================

proc keyListCommand*(keyringType: string = "all", status: string = "all",
  verbose: bool = false): CommandResult =
  ## Implement nip key list command
  try:
    showInfo("Listing keys from keyrings")

    # Parse filters
    let keyringFilter = if keyringType == "all":
      none(KeyringType)
    else:
      case keyringType.toLower():
      of "system": some(KeyringSystem)
      of "user": some(KeyringUser)
      of "repository": some(KeyringRepository)
      else:
        return errorResult(fmt"Invalid keyring type: {keyringType}")

    let statusFilter = if status == "all":
      none(KeyStatus)
    else:
      case status.toLower():
      of "active": some(KeyActive)
      of "deprecated": some(KeyDeprecated)
      of "revoked": some(KeyRevoked)
      of "expired": some(KeyExpired)
      of "superseded": some(KeySuperseded)
      else:
        return errorResult(fmt"Invalid status: {status}")

    # Initialize keyring manager
    let config = getDefaultKeyringConfig()
    var manager = newKeyringManager(config)
    manager.loadAllKeyrings()

    # List keys
    let keys = manager.listKeys(keyringFilter, statusFilter)

    let keysData = %*{
      "total_keys": keys.len,
      "filters": {
        "keyring_type": keyringType,
        "status": status
      },
      "keys": keys.mapIt(%*{
        "key_id": it.publicKey.keyId,
        "algorithm": $it.publicKey.algorithm,
        "status": $it.status,
        "valid_from": $it.publicKey.validFrom,
        "valid_until": $it.publicKey.validUntil,
        "added_by": it.provenance.addedBy,
        "usage_count": it.usageCount,
        "revoked": it.publicKey.revoked
      })
    }

    if globalContext.options.outputFormat == OutputHuman:
      echo ""
      echo bold(fmt"Keys in {keyringType} keyring(s) ({keys.len} total)")
      echo "=".repeat(50)

      if keys.len == 0:
        echo info("No keys found matching the specified criteria")
        echo ""
        echo "💡 To import a key: nip key import <keyfile>"
        echo "💡 To generate a key: nip key generate --algorithm=ed25519"
      else:
        for key in keys:
          let statusColor = case key.status:
            of KeyActive: success($key.status)
            of KeyDeprecated: warning($key.status)
            of KeyRevoked: error($key.status)
            of KeyExpired: error($key.status)
            of KeySuperseded: info($key.status)

          echo lockSymbol() & " " & bold(key.publicKey.keyId) & " (" & highlight($key.publicKey.algorithm) & ")"
          echo fmt"    Status: {statusColor}"
          echo fmt"    Valid: {key.publicKey.validFrom} → {key.publicKey.validUntil}"
          echo fmt"    Added by: {key.provenance.addedBy}"

          if verbose:
            echo fmt"    Source: {key.provenance.source}"
            echo fmt"    Usage count: {key.usageCount}"
            echo fmt"    Last used: {key.lastUsed}"
            if key.revokedAt.isSome():
              echo fmt"    Revoked: {key.revokedAt.get()} by {key.revokedBy}"

          echo ""

      echo lockSymbol() & " " & bold("Keyring Statistics:")
      let activeKeys = keys.countIt(it.status == KeyActive)
      let revokedKeys = keys.countIt(it.status == KeyRevoked)
      let expiredKeys = keys.countIt(it.status == KeyExpired)

      echo fmt"   Active: {activeKeys}"
      echo fmt"   Revoked: {revokedKeys}"
      echo fmt"   Expired: {expiredKeys}"
    else:
      outputData(keysData)

    return successResult(fmt"Listed {keys.len} keys", keysData)

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

# =============================================================================
# Key Revoke Command
# =============================================================================

proc keyRevokeCommand*(keyId: string, reason: string = "unspecified",
                      reasonText: string = "", supersededBy: string = "",
                      emergency: bool = false): CommandResult =
  ## Implement nip key revoke command
  try:
    if emergency:
      showInfo(fmt"🚨 EMERGENCY REVOCATION: {keyId}")
    else:
      showInfo(fmt"Revoking key: {keyId}")

    # Parse revocation reason
    let revocationReason = case reason.toLower():
      of "unspecified": ReasonUnspecified
      of "compromise", "key-compromise": ReasonKeyCompromise
      of "ca-compromise": ReasonCACompromise
      of "affiliation-changed": ReasonAffiliationChanged
      of "superseded": ReasonSuperseded
      of "cessation": ReasonCessationOfOperation
      of "hold": ReasonCertificateHold
      of "privilege-withdrawn": ReasonPrivilegeWithdrawn
      else:
        return errorResult(fmt"Invalid revocation reason: {reason}")

    let finalReasonText = if reasonText == "":
      case revocationReason:
      of ReasonKeyCompromise: "Key compromise detected"
      of ReasonSuperseded: "Key replaced by newer version"
      of ReasonAffiliationChanged: "Key holder affiliation changed"
      else: "Key revoked"
    else:
      reasonText

    # Confirm revocation (unless emergency)
    if not emergency and not globalContext.options.dryRun:
      let confirmMsg = fmt"Revoke key {keyId}? This action cannot be undone."
      if not confirmAction(confirmMsg, defaultYes = false):
        return successResult("Key revocation cancelled by user")

    # Initialize keyring manager
    let config = getDefaultKeyringConfig()
    var manager = newKeyringManager(config)
    manager.loadAllKeyrings()

    # Revoke key
    let supersededByOpt = if supersededBy == "": none(string) else: some(supersededBy)
    let revokedBy = "cli-user"  # TODO: Get actual user

    if manager.revokeKey(keyId, revocationReason, finalReasonText, revokedBy, supersededByOpt):
      let revocationInfo = %*{
        "key_id": keyId,
        "reason_code": revocationReason.int,
        "reason_text": finalReasonText,
        "revoked_by": revokedBy,
        "revoked_at": $now().utc(),
        "superseded_by": supersededBy,
        "emergency": emergency
      }

      if globalContext.options.outputFormat == OutputHuman:
        echo ""
        if emergency:
          echo errorSymbol() & " " & error(fmt"EMERGENCY REVOCATION COMPLETED: {keyId}")
        else:
          echo successSymbol() & " " & success(fmt"Key revoked: {keyId}")

        echo fmt"   Reason: {finalReasonText}"
        echo fmt"   Revoked by: {revokedBy}"
        echo fmt"   Revoked at: {now().utc()}"

        if supersededBy != "":
          echo fmt"   Superseded by: {supersededBy}"

        echo ""
        echo lockSymbol() & " " & bold("Revocation Actions:")
        echo "   ✅ Key marked as revoked in all keyrings"
        echo "   ✅ Certificate Revocation List (CRL) updated"
        echo "   ✅ Security event logged with tamper-evident chain"

        if emergency:
          echo "   🚨 Emergency broadcast initiated"
          echo "   🚨 All repositories will be notified within 15 minutes"

        echo ""
        echo warning("⚠️  All future signature verifications with this key will fail")
      else:
        outputData(revocationInfo)

      return successResult(fmt"Key {keyId} revoked successfully", revocationInfo)
    else:
      return errorResult(fmt"Failed to revoke key: {keyId} (key not found)")

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

# =============================================================================
# Key Rollover Command
# =============================================================================

proc keyRolloverCommand*(oldKeyId: string, newKeyFile: string = "",
                        gracePeriod: int = 7, schedule: string = ""): CommandResult =
  ## Implement nip key rollover command
  try:
    showInfo(fmt"Initiating key rollover for: {oldKeyId}")

    # Initialize keyring manager
    let config = getDefaultKeyringConfig()
    var manager = newKeyringManager(config)
    manager.loadAllKeyrings()

    # Verify old key exists
    let oldKeyOpt = manager.findKey(oldKeyId)
    if oldKeyOpt.isNone():
      return errorResult(fmt"Old key not found: {oldKeyId}")

    # Generate or load new key
    var newKey: PublicKey

    if newKeyFile == "":
      # Generate new key (placeholder)
      let newKeyId = fmt"{oldKeyId}-rollover-{epochTime().int}"
      newKey = createPublicKey(
        SigEd25519,
        newKeyId,
        encode("new-generated-key-data-placeholder"),
        now().utc(),
        now().utc() + initDuration(days = 730)  # 2 years
      )

      showInfo(fmt"Generated new key: {newKeyId}")
    else:
      # Load new key from file
      if not fileExists(newKeyFile):
        return errorResult(fmt"New key file not found: {newKeyFile}")

      let keyContent = readFile(newKeyFile)
      let keyJson = parseJson(keyContent)

      newKey = createPublicKey(
        parseEnum[SignatureAlgorithm](keyJson["algorithm"].getStr()),
        keyJson["key_id"].getStr(),
        keyJson["key_data"].getStr(),
        keyJson["valid_from"].getStr().parse("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'", utc()),
        keyJson["valid_until"].getStr().parse("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'", utc())
      )

      showInfo(fmt"Loaded new key from file: {newKey.keyId}")

    # Parse schedule
    let rolloverDate = if schedule == "":
      now().utc() + initDuration(days = 1)  # Default: tomorrow
    else:
      # TODO: Parse schedule string
      now().utc() + initDuration(days = 1)

    # Schedule rollover
    if manager.scheduleKeyRollover(oldKeyId, newKey, rolloverDate, gracePeriod):
      let rolloverInfo = %*{
        "old_key_id": oldKeyId,
        "new_key_id": newKey.keyId,
        "rollover_date": $rolloverDate,
        "grace_period_days": gracePeriod,
        "scheduled_at": $now().utc()
      }

      if globalContext.options.outputFormat == OutputHuman:
        echo ""
        echo successSymbol() & " " & success(fmt"Key rollover scheduled: {oldKeyId} → {newKey.keyId}")
        echo fmt"   Rollover date: {rolloverDate}"
        echo fmt"   Grace period: {gracePeriod} days"
        echo fmt"   New key algorithm: {newKey.algorithm}"
        echo ""
        echo lockSymbol() & " " & bold("Rollover Timeline:")
        echo fmt"   📅 {rolloverDate}: New key becomes primary"
        echo fmt"   📅 {rolloverDate + initDuration(days = gracePeriod)}: Old key revoked"
        echo fmt"   📅 Now → {rolloverDate}: Both keys valid (overlap period)"
        echo ""
        echo info("💡 Use 'nip key list' to monitor rollover status")
      else:
        outputData(rolloverInfo)

      return successResult(fmt"Key rollover scheduled for {oldKeyId}", rolloverInfo)
    else:
      return errorResult(fmt"Failed to schedule key rollover for {oldKeyId}")

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

# =============================================================================
# Key Generate Command (Bonus)
# =============================================================================

proc keyGenerateCommand*(algorithm: string = "ed25519", keyId: string = "",
                        validity: int = 730): CommandResult =
  ## Implement nip key generate command
  try:
    showInfo(fmt"Generating new {algorithm} key")

    # Parse algorithm
    let keyAlgorithm = case algorithm.toLower():
      of "ed25519": SigEd25519
      of "dilithium": SigDilithium
      of "rsa": SigRSA
      else:
        return errorResult(fmt"Unsupported algorithm: {algorithm}")

    # Generate key ID if not provided
    let finalKeyId = if keyId == "":
      fmt"nexusos-{algorithm}-{epochTime().int}"
    else:
      keyId

    # Generate key (placeholder implementation)
    let newKey = createPublicKey(
      keyAlgorithm,
      finalKeyId,
      encode(fmt"generated-{algorithm}-key-data-{epochTime().int}"),
      now().utc(),
      now().utc() + initDuration(days = validity)
    )

    # Save key to file
    let keyData = %*{
      "version": "1.0",
      "generated_at": $now().utc(),
      "algorithm": $keyAlgorithm,
      "key_id": finalKeyId,
      "key_data": newKey.keyData,
      "valid_from": $newKey.validFrom,
      "valid_until": $newKey.validUntil,
      "private_key": "PLACEHOLDER-PRIVATE-KEY-DATA"  # TODO: Actual key generation
    }

    let keyFile = fmt"{finalKeyId}.json"
    writeFile(keyFile, keyData.pretty())

    if globalContext.options.outputFormat == OutputHuman:
      echo ""
      echo successSymbol() & " " & success(fmt"Key generated: {finalKeyId}")
      echo fmt"   Algorithm: {keyAlgorithm}"
      echo fmt"   Validity: {validity} days"
      echo fmt"   Key file: {keyFile}"
      echo ""
      echo lockSymbol() & " " & bold("Next Steps:")
      echo fmt"   1. Import key: nip key import {keyFile}"
      echo "   2. Distribute public key to repositories"
      echo "   3. Begin signing packages with new key"
      echo ""
      echo warning("⚠️  Keep the private key secure and backed up!")
    else:
      outputData(keyData)

    return successResult(fmt"Key {finalKeyId} generated", keyData)

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

# =============================================================================
# Export all key command functions
# =============================================================================

export keyImportCommand, keyExportCommand, keyListCommand
export keyRevokeCommand, keyRolloverCommand, keyGenerateCommand