#!/usr/bin/env nu
# Check environment prerequisites for ontoref operations.
#
# Usage:
#   nu reflection/bin/check-prereqs.nu
#   nu reflection/bin/check-prereqs.nu --context ci
#   nu reflection/bin/check-prereqs.nu --context agent
#   nu reflection/bin/check-prereqs.nu --form new_adr
#   nu reflection/bin/check-prereqs.nu --context local_dev --severity Hard

def main [
  --context: string  = "local_dev",
  --form: string     = "",
  --severity: string = "",
  --json,
] {
  let base_file     = "reflection/requirements/base.ncl"
  let contexts_file = "reflection/requirements/contexts.ncl"

  # Inline daemon-export with subprocess fallback (standalone script, no module imports).
  let export_base = do { ^nickel export $base_file } | complete
  if $export_base.exit_code != 0 {
    error make { msg: "Failed to load base.ncl — is nickel installed?" }
  }
  let all_tools = ($export_base.stdout | from json | get tools)

  let export_ctx = do { ^nickel export $contexts_file } | complete
  if $export_ctx.exit_code != 0 {
    error make { msg: "Failed to load contexts.ncl" }
  }
  let ctx_candidates = ($export_ctx.stdout | from json | get contexts | where id == $context)
  if ($ctx_candidates | is-empty) {
    error make { msg: $"Unknown context '($context)'. Valid: local_dev | ci | agent | benchmark" }
  }
  let ctx_def = ($ctx_candidates | first)

  let tools = if ($form | is-empty) {
    $all_tools | where { |it| $it.contexts | any { |c| $c == $context } }
  } else {
    $all_tools | where { |it| $it.used_by_forms | any { |f| $f == $form } }
  }

  let tools = if ($severity | is-empty) {
    $tools
  } else {
    $tools | where { |it| $it.severity == $severity }
  }

  let results = $tools | each { |tool|
    let is_plugin = ($tool.plugin_name? | is-not-empty)

    # Presence check: plugin registry or PATH binary
    let present = if $is_plugin {
      plugin list | where name == $tool.plugin_name | is-not-empty
    } else {
      (do { run-external "which" $tool.binary } | complete | get exit_code) == 0
    }

    # Version: run check_cmd via nu -c for both binary and plugin tools
    let version = if $present and ($tool.check_cmd | is-not-empty) {
      do { nu -c $"($tool.check_cmd) | str trim" } | complete
      | if $in.exit_code == 0 { $in.stdout | str trim } else { "unknown" }
    } else if $present {
      "present"
    } else {
      "not installed"
    }

    let version_ok = if $present and $version != "unknown" and $version != "present" {
      let min_parts = ($tool.version_min | split row "." | each { |p| $p | into int })
      let act_result = (
        $version
        | parse --regex $tool.version_extract
        | if ($in | is-empty) { null } else { $in | first | get capture0 }
      )
      if ($act_result | is-empty) {
        true  # Cannot parse version — assume ok (plugin compat versions)
      } else {
        let act_parts = ($act_result | split row "." | each { |p| $p | into int })
        let major_ok = ($act_parts | get 0) >= ($min_parts | get 0)
        let minor_ok = if ($act_parts | get 0) == ($min_parts | get 0) {
          ($act_parts | get 1) >= ($min_parts | get 1)
        } else { true }
        $major_ok and $minor_ok
      }
    } else if $present {
      true
    } else {
      false
    }

    let config_ok = if $present and ($tool.config_check? | is-not-empty) {
      do { nu -c $tool.config_check } | complete | get exit_code | $in == 0
    } else {
      true
    }

    let display_name = if $is_plugin { $"plugin:($tool.plugin_name)" } else { $tool.binary }

    {
      id:           $tool.id,
      binary:       $display_name,
      severity:     $tool.severity,
      present:      $present,
      version:      $version,
      version_min:  $tool.version_min,
      version_ok:   $version_ok,
      config_ok:    $config_ok,
      status:       (if not $present {
        "MISSING"
      } else if not $version_ok {
        "VERSION_LOW"
      } else if not $config_ok {
        "CONFIG_FAIL"
      } else {
        "OK"
      }),
      install_hint: $tool.install_hint,
      config_hint:  ($tool.config_hint? | default ""),
    }
  }

  if $json {
    $results | to json
    return
  }

  print $"Environment check — context: ($context)"
  print $"─────────────────────────────────────────────────────"

  let fails = $results | where status != "OK"

  for r in $results {
    let icon = match $r.status {
      "OK"           => "✓",
      "MISSING"      => "✗",
      "VERSION_LOW"  => "↑",
      "CONFIG_FAIL"  => "⚠",
      _              => "?",
    }
    let sev = if $r.severity == "Hard" { "[Hard]" } else { "[Soft]" }
    print $"($icon) ($sev) ($r.binary) ($r.version) [min: ($r.version_min)]"
  }

  print ""

  if ($fails | is-empty) {
    print "All checks passed."
    return
  }

  let hard_fails = $fails | where severity == "Hard"

  for f in $fails {
    print $"── ($f.binary) — ($f.status) ──"
    match $f.status {
      "MISSING"     => { print $"   Install: ($f.install_hint)" },
      "VERSION_LOW" => { print $"   Installed: ($f.version)  Required: >= ($f.version_min)" ; print $"   Update: ($f.install_hint)" },
      "CONFIG_FAIL" => { print $"   Config issue: ($f.config_hint)" },
    }
    print ""
  }

  if ($hard_fails | is-not-empty) {
    let hard_ids = $hard_fails | get binary | str join ", "
    error make { msg: $"Hard prerequisites not met: ($hard_ids)" }
  }
}
