use store.nu [daemon-export]

# Manifest operations — operational modes and publication services.
#
# Reads $ONTOREF_PROJECT_ROOT/.ontology/manifest.ncl to determine:
#   - layers, operational_modes, consumption_modes, publication_services
#
# Commands:
#   manifest load                   export and parse the project manifest
#   manifest mode <id>              switch to an operational mode (run pre/post activate)
#   manifest mode list              list available operational modes
#   manifest publish <service-id>   run a publication workflow
#   manifest publish list           list available publication services
#   manifest layers                 show layers with committed status
#   manifest consumers              show consumption modes

def manifest-path []: nothing -> string {
  let root = if ($env.ONTOREF_PROJECT_ROOT? | is-not-empty) {
    $env.ONTOREF_PROJECT_ROOT
  } else {
    $env.PWD
  }
  $"($root)/.ontology/manifest.ncl"
}

# Export and parse the project manifest NCL
export def "manifest load" []: nothing -> record {
  let path = (manifest-path)
  if not ($path | path exists) {
    error make { msg: $"No manifest at ($path) — create .ontology/manifest.ncl first" }
  }

  # Build import path: project .ontology/ + ontoref ontology/ + existing NICKEL_IMPORT_PATH
  let project_ontology = ($path | path dirname)
  let ontoref_ontology = if ($env.ONTOREF_ROOT? | is-not-empty) {
    $"($env.ONTOREF_ROOT)/ontology"
  } else {
    ""
  }

  let extra_paths = ([$project_ontology, $ontoref_ontology] | where { $in | is-not-empty })
  let existing = ($env.NICKEL_IMPORT_PATH? | default "")
  let import_path = if ($existing | is-not-empty) {
    ($extra_paths | append ($existing | split row ":") | uniq | str join ":")
  } else {
    ($extra_paths | str join ":")
  }

  daemon-export $path --import-path $import_path
}

# Find an operational mode by id
def find-mode [manifest: record, id: string]: nothing -> record {
  let modes = ($manifest.operational_modes? | default [])
  let found = ($modes | where { ($in.id? | default "") == $id })
  if ($found | is-empty) {
    let available = ($modes | each { $in.id? | default "?" } | str join ", ")
    error make { msg: $"Mode '($id)' not found. Available: ($available)" }
  }
  $found | first
}

# Find a publication service by id
def find-service [manifest: record, id: string]: nothing -> record {
  let services = ($manifest.publication_services? | default [])
  let found = ($services | where { ($in.id? | default "") == $id })
  if ($found | is-empty) {
    let available = ($services | each { $in.id? | default "?" } | str join ", ")
    error make { msg: $"Service '($id)' not found. Available: ($available)" }
  }
  $found | first
}

# Run a list of shell commands sequentially, abort on failure
def run-commands [commands: list<string>, label: string]: nothing -> bool {
  for cmd in $commands {
    print $"  [$label] ($cmd)"
    let result = (do { nu -c $cmd } | complete)
    if $result.exit_code != 0 {
      print $"  [$label] FAILED: ($cmd)"
      if ($result.stderr | is-not-empty) {
        print $"  ($result.stderr | lines | first 5 | str join '\n  ')"
      }
      return false
    }
    print $"  [$label] OK"
  }
  true
}

# Switch to an operational mode — runs pre_activate, shows status, runs post_activate
export def "manifest mode" [
  id: string         # Mode id: dev, publish, investigate, ci
  --dry-run (-n)     # Show what would happen without executing
]: nothing -> record {
  let m = (manifest load)
  let mode = (find-mode $m $id)

  let layers = ($m.layers? | default [])
  let visible = ($mode.visible_layers? | default [])
  let hidden = ($layers | where { not ($in.id in $visible) } | each { $in.id })

  print ""
  print $"── Mode: ($mode.id) ──"
  if ($mode.description? | is-not-empty) {
    print $"   ($mode.description)"
  }
  print ""
  print $"   Audit level: ($mode.audit_level)"
  print $"   Visible layers: ($visible | str join ', ')"
  if ($hidden | is-not-empty) {
    print $"   Hidden layers: ($hidden | str join ', ')"
  }

  let pre = ($mode.pre_activate? | default [])
  let post = ($mode.post_activate? | default [])

  if $dry_run {
    if ($pre | is-not-empty) {
      print ""
      print "   Would run pre_activate:"
      for cmd in $pre { print $"     ($cmd)" }
    }
    if ($post | is-not-empty) {
      print ""
      print "   Would run post_activate:"
      for cmd in $post { print $"     ($cmd)" }
    }
    print ""
    return { mode: $id, status: "dry-run", pre_ok: true, post_ok: true }
  }

  mut pre_ok = true
  if ($pre | is-not-empty) {
    print ""
    print "   pre_activate:"
    $pre_ok = (run-commands $pre "pre")
    if not $pre_ok {
      print ""
      print $"   Mode activation ABORTED — pre_activate failed."
      print $"   Fix the issues and retry: ontoref mode ($id)"
      return { mode: $id, status: "failed", pre_ok: false, post_ok: false }
    }
  }

  mut post_ok = true
  if ($post | is-not-empty) {
    print ""
    print "   post_activate:"
    $post_ok = (run-commands $post "post")
    if not $post_ok {
      print ""
      print $"   WARNING: post_activate failed. Mode is active but not fully verified."
    }
  }

  let status = if $pre_ok and $post_ok { "active" } else { "partial" }
  print ""
  print $"   Mode '($id)' → ($status)"
  { mode: $id, status: $status, pre_ok: $pre_ok, post_ok: $post_ok }
}

# List available operational modes
export def "manifest mode list" [
  --fmt: string = "table"    # Output format: table | json
]: nothing -> table {
  let m = (manifest load)
  let modes = ($m.operational_modes? | default [])
  let default_mode = ($m.default_mode? | default "dev")

  let rows = ($modes | each {|mode|
    {
      id: $mode.id,
      description: ($mode.description? | default ""),
      audit: ($mode.audit_level? | default "Standard"),
      layers: ($mode.visible_layers? | default [] | length),
      pre: ($mode.pre_activate? | default [] | length),
      post: ($mode.post_activate? | default [] | length),
      default: ($mode.id == $default_mode),
    }
  })

  match $fmt {
    "json" => { $rows | to json }
    _ => { $rows }
  }
}

# Run a publication workflow — pre_publish, confirmation, post_publish
export def "manifest publish" [
  id: string         # Service id: crates-io-public, gitea-private, etc.
  --dry-run (-n)     # Show what would happen without executing
  --yes (-y)         # Skip confirmation prompt
]: nothing -> record {
  let m = (manifest load)
  let svc = (find-service $m $id)

  print ""
  print $"── Publish: ($svc.id) ──"
  print $"   Artifact: ($svc.artifact)"
  print $"   Scope: ($svc.scope)"
  if ($svc.registry_url? | is-not-empty) {
    print $"   Registry: ($svc.registry_url)"
  }
  print $"   Auth: ($svc.auth_method)"
  print $"   Trigger: ($svc.trigger)"
  if ($svc.condition? | is-not-empty) {
    print $"   Condition: ($svc.condition)"
  }

  let pre = ($svc.pre_publish? | default [])
  let post = ($svc.post_publish? | default [])

  if $dry_run {
    if ($pre | is-not-empty) {
      print ""
      print "   Would run pre_publish:"
      for cmd in $pre { print $"     ($cmd)" }
    }
    print ""
    print "   Would execute publish action"
    if ($post | is-not-empty) {
      print ""
      print "   Would run post_publish:"
      for cmd in $post { print $"     ($cmd)" }
    }
    print ""
    return { service: $id, status: "dry-run" }
  }

  # Pre-publish checks
  mut pre_ok = true
  if ($pre | is-not-empty) {
    print ""
    print "   pre_publish:"
    $pre_ok = (run-commands $pre "pre")
    if not $pre_ok {
      print ""
      print $"   Publish ABORTED — pre_publish failed."
      return { service: $id, status: "failed", stage: "pre_publish" }
    }
  }

  # Confirmation gate
  if not $yes {
    print ""
    let answer = (input $"   Proceed with publish to ($svc.id)? [y/n] ")
    if not ($answer | str starts-with "y") {
      print "   Publish cancelled."
      return { service: $id, status: "cancelled" }
    }
  }

  # Post-publish actions
  mut post_ok = true
  if ($post | is-not-empty) {
    print ""
    print "   post_publish:"
    $post_ok = (run-commands $post "post")
    if not $post_ok {
      print ""
      print "   WARNING: post_publish failed. Publish may have succeeded but follow-up actions incomplete."
    }
  }

  let status = if $pre_ok and $post_ok { "published" } else { "partial" }
  print ""
  print $"   Service '($id)' → ($status)"
  { service: $id, status: $status }
}

# List available publication services
export def "manifest publish list" [
  --fmt: string = "table"
]: nothing -> table {
  let m = (manifest load)
  let services = ($m.publication_services? | default [])

  let rows = ($services | each {|svc|
    {
      id: $svc.id,
      artifact: ($svc.artifact? | default ""),
      scope: ($svc.scope? | default ""),
      auth: ($svc.auth_method? | default "None"),
      trigger: ($svc.trigger? | default ""),
      pre: ($svc.pre_publish? | default [] | length),
      post: ($svc.post_publish? | default [] | length),
    }
  })

  match $fmt {
    "json" => { $rows | to json }
    _ => { $rows }
  }
}

# Show all layers with their visibility status
export def "manifest layers" [
  --mode (-m): string = ""     # Show visibility for a specific mode
]: nothing -> table {
  let m = (manifest load)
  let layers = ($m.layers? | default [])

  if ($mode | is-not-empty) {
    let op_mode = (find-mode $m $mode)
    let visible = ($op_mode.visible_layers? | default [])
    $layers | each {|l|
      {
        id: $l.id,
        committed: $l.committed,
        visible: ($l.id in $visible),
        paths: ($l.paths | str join ", "),
        description: ($l.description? | default ""),
      }
    }
  } else {
    $layers | each {|l|
      {
        id: $l.id,
        committed: $l.committed,
        paths: ($l.paths | str join ", "),
        description: ($l.description? | default ""),
      }
    }
  }
}

# Show consumption modes — who consumes and what they need
export def "manifest consumers" [
  --fmt: string = "table"
]: nothing -> table {
  let m = (manifest load)
  let consumers = ($m.consumption_modes? | default [])

  let rows = ($consumers | each {|c|
    {
      consumer: ($c.consumer? | default ""),
      needs: ($c.needs? | default [] | str join ", "),
      audit: ($c.audit_level? | default "Standard"),
      description: ($c.description? | default ""),
    }
  })

  match $fmt {
    "json" => { $rows | to json }
    _ => { $rows }
  }
}
