#!/usr/bin/env nu
# Execute the constraint checks declared by accepted ADRs and report per constraint.
#
# Constraints live in `.ontoref/adrs/adr-*.ncl` at the constellation root. Each carries a
# `check` in one of three shapes:
#
#   NuCmd      { cmd, expect_exit }        run cmd, compare exit code
#   Grep       { pattern, paths, must_be_empty }
#   FileExists { path, present }
#
# Reported states:
#   ✓  the check ran and passed
#   ✗  the check ran and failed
#   ⊘  no check ran — the constraint declares `cmd = "exit 0"` (a no-op that cannot fail)
#      or its shape is unknown. Reported apart from ✓ on purpose: a constraint that cannot
#      fail is not a satisfied constraint, and counting it green is the lie this runner exists
#      to prevent.
#
# Only `Accepted` ADRs gate. `Proposed` ones are listed as skipped.
#
# Exits 0 regardless of findings unless `--strict` is passed, so this can be read before it
# is enforced.

def adrs_dir [] { $env.FILE_PWD | path join ".." ".." ".ontoref" "adrs" | path expand }

# A cmd that cannot fail is not a check. Extend this list as no-op idioms appear.
def is_noop [cmd: string] { ($cmd | str trim) in ["exit 0", "true", ""] }

def run_nucmd [check: record] {
  if (is_noop ($check.cmd? | default "")) {
    return { state: "skip", detail: "check is `exit 0` — a no-op that cannot fail" }
  }
  let expect = ($check.expect_exit? | default 0)
  let r = (do --ignore-errors { ^nu -c $check.cmd } | complete)
  if $r.exit_code == $expect {
    { state: "pass", detail: $"exit ($r.exit_code) == expected ($expect)" }
  } else {
    { state: "fail", detail: $"exit ($r.exit_code), expected ($expect)" }
  }
}

def run_grep [check: record] {
  let paths = ($check.paths? | default [])
  if ($paths | is-empty) {
    return { state: "skip", detail: "Grep check declares no paths" }
  }
  let existing = ($paths | where { |p| ($p | path exists) })
  if ($existing | is-empty) {
    return { state: "skip", detail: $"none of the declared paths exist: ($paths | str join ', ')" }
  }
  let r = (do --ignore-errors { ^rg -n --no-heading $check.pattern ...$existing } | complete)
  let hits = if ($r.stdout | str trim | is-empty) { 0 } else { $r.stdout | lines | length }
  let must_be_empty = ($check.must_be_empty? | default true)
  if $must_be_empty and $hits > 0 {
    { state: "fail", detail: $"($hits) hits, must_be_empty=true" }
  } else if (not $must_be_empty) and $hits == 0 {
    { state: "fail", detail: "0 hits, must_be_empty=false" }
  } else {
    { state: "pass", detail: $"($hits) hits" }
  }
}

def run_fileexists [check: record] {
  let want = ($check.present? | default true)
  let got = ($check.path | path exists)
  if $got == $want {
    { state: "pass", detail: $"present=($got), expected ($want)" }
  } else {
    { state: "fail", detail: $"present=($got), expected ($want) — ($check.path)" }
  }
}

def run_check [check: record] {
  match ($check.tag? | default "UNKNOWN") {
    "NuCmd" => (run_nucmd $check),
    "Grep" => (run_grep $check),
    "FileExists" => (run_fileexists $check),
    _ => { state: "skip", detail: $"unknown check shape: ($check.tag? | default '<none>')" },
  }
}

def main [
  --strict   # exit 1 if any constraint fails
  --verbose  # print the detail line for passing constraints too
] {
  let dir = (adrs_dir)
  let files = (glob ($dir | path join "adr-*.ncl")
    | where { |f| not (($f | path basename) starts-with "_") }
    | where { |f| ($f | path basename) not-in ["adr-defaults.ncl" "adr-schema.ncl" "adr-constraints.ncl"] }
    | sort)

  mut rows = []
  mut skipped_adrs = []

  for f in $files {
    let export = (do --ignore-errors { ^nickel export $f } | complete)
    if $export.exit_code != 0 {
      print $"(ansi red)✗(ansi reset) ($f | path basename) — nickel export failed"
      print ($export.stderr | lines | first 3 | str join "\n")
      continue
    }
    let adr = ($export.stdout | from json)
    if $adr.status != "Accepted" {
      $skipped_adrs = ($skipped_adrs | append $"($adr.id) [($adr.status)]")
      continue
    }
    for c in $adr.constraints {
      let res = (run_check $c.check)
      $rows = ($rows | append {
        adr: $adr.id, id: $c.id, severity: ($c.severity? | default "Hard"),
        state: $res.state, detail: $res.detail,
      })
    }
  }

  print $"(ansi default_bold)ADR constraints(ansi reset) — ($rows | length) from accepted ADRs\n"

  for r in $rows {
    let mark = match $r.state {
      "pass" => $"(ansi green)✓(ansi reset)",
      "fail" => $"(ansi red)✗(ansi reset)",
      _ => $"(ansi yellow)⊘(ansi reset)",
    }
    if $r.state == "pass" and (not $verbose) {
      print $"  ($mark) ($r.adr) ($r.id)"
    } else {
      print $"  ($mark) ($r.adr) ($r.id)"
      print $"      ($r.detail)"
    }
  }

  let passed = ($rows | where state == "pass" | length)
  let failed = ($rows | where state == "fail" | length)
  let skipped = ($rows | where state == "skip" | length)

  print ""
  print $"  (ansi green)($passed) ✓(ansi reset) · (ansi red)($failed) ✗(ansi reset) · (ansi yellow)($skipped) ⊘(ansi reset)"
  if not ($skipped_adrs | is-empty) {
    print $"  not gating \(status\): ($skipped_adrs | str join ', ')"
  }
  if $skipped > 0 {
    print $"  (ansi yellow)⊘(ansi reset) = declared but unexecutable — neither green nor red. A no-op check is not a check."
  }

  if $strict and $failed > 0 {
    exit 1
  }
}
