#!/usr/bin/env nu
# services.nu - Config-driven service lifecycle management with dependency resolution.
#
# Services configured in .ontoref/config.ncl:
#   services.services[] - array of {id, enabled, depends_on[], config}
#   services.startup_order - explicit startup order (optional)
#   services.shutdown_order - explicit shutdown order (optional)
#
# Usage:
#   ontoref services                     # Show all services status
#   ontoref services start [id]          # Start service (and dependencies)
#   ontoref services stop [id]           # Stop service
#   ontoref services restart [id]        # Restart service
#   ontoref services status [id]         # Show service status
#   ontoref services health [id]         # Health check service

use ../modules/store.nu *

# -- Configuration ------------------------------------------------------------

def get-project-config []: nothing -> record {
  let root = ($env.ONTOREF_PROJECT_ROOT? | default $env.ONTOREF_ROOT)
  let config_path = $"($root)/.ontoref/config.ncl"
  if not ($config_path | path exists) {
    return { services: { services: [] }, daemon: {} }
  }
  let result = (do { ^nickel export $config_path } | complete)
  if $result.exit_code == 0 {
    $result.stdout | from json
  } else {
    { services: { services: [] }, daemon: {} }
  }
}

def get-internal-services []: nothing -> list {
  # Only daemon is managed by onref. DB is external.
  [
    { id: "daemon", enabled: false, depends_on: [], managed: true }
  ]
}

def get-services-list []: nothing -> list {
  let config = (get-project-config)
  let all = ($config.services.services? | default [])
  # Filter to only managed services (daemon)
  $all | where { |s| $s.id == "daemon" }
}

def get-service [id: string]: nothing -> record {
  let services = (get-services-list)
  let service = ($services | where id == $id | first)
  if ($service | is-empty) {
    error make { msg: $"Service not found: ($id)" }
  }
  $service
}

def get-startup-order []: nothing -> list {
  let config = (get-project-config)
  let explicit = ($config.services.startup_order? | default null)
  if ($explicit | is-not-empty) {
    $explicit
  } else {
    # Default order: daemon first, then db
    ["daemon", "db"]
  }
}

def get-shutdown-order []: nothing -> list {
  let config = (get-project-config)
  let explicit = ($config.services.shutdown_order? | default null)
  if ($explicit | is-not-empty) {
    $explicit
  } else {
    # Reverse order: db first, then daemon
    ["db", "daemon"]
  }
}

def daemon-pid-file []: nothing -> string {
  $"($env.HOME)/.ontoref/daemon.pid"
}

def daemon-running? []: nothing -> bool {
  let pid_file = (daemon-pid-file)
  if not ($pid_file | path exists) { return false }
  let pid = (open $pid_file | str trim)
  let result = (do { ^kill -0 $pid } | complete)
  $result.exit_code == 0
}

# -- Status -------------------------------------------------------------------

def "services" [action?: string, id?: string]: nothing -> nothing {
  match ($action | default "") {
    "" => { services overview },
    "start" => { services start $id },
    "stop" => { services stop $id },
    "restart" => { services restart $id },
    "status" => { services status $id },
    "health" => { services health $id },
    _ => { print $"Unknown action: ($action). Use: start, stop, restart, status, health" }
  }
}

export def "main" [action?: string, id?: string]: nothing -> nothing {
  services $action $id
}

export def "services overview" []: nothing -> nothing {
  let config = (get-project-config)
  let services = (get-services-list)

  # Color codes (defined once)
  let cyan = (ansi cyan)
  let blue = (ansi blue)
  let green = (ansi green)
  let red = (ansi red)
  let gray = (ansi dark_gray)
  let yellow = (ansi yellow)
  let reset = (ansi reset)

  print ""
  print ($cyan + "  ontoref services (managed by ontoref)" + $reset)
  print ($gray + "  ----------------------------------------" + $reset)
  print ""

  # Show managed services (daemon)
  for service in $services {
    let status_text = if (daemon-running?) { "running" } else { "stopped" }
    let status_icon = if ($status_text == "running") { "✓" } else { "✗" }

    if $service.enabled {
      let port = ($config.daemon.port? | default 7891)
      let status_color = if ($status_text == "running") { $green } else { $red }
      let line = $blue + "  " + $service.id + $reset + " [" + $status_color + $status_icon + $reset + "] " + $status_text + " - port " + $yellow + ($port | into string) + $reset
      print $line
    } else {
      let line = $blue + "  " + $service.id + $reset + " [" + $gray + "-" + $reset + "] " + $gray + "disabled" + $reset
      print $line
    }
  }

  print ""
  print ($cyan + "  External services (monitored only)" + $reset)
  print ($gray + "  ----------------------------------------" + $reset)
  print ""

  # Show external services: DB and NATS
  let db_config = ($config.services.services? | default [] | where id == "db" | first)
  if ($db_config | is-not-empty) {
    if ($db_config.enabled | default false) {
      let db_url = ($config.db.url? | default "")
      if ($db_url | is-not-empty) {
        let line = $blue + "  db" + $reset + " " + $gray + "[status check only]" + $reset + "  " + $yellow + $db_url + $reset
        print $line
      } else {
        let line = $blue + "  db" + $reset + " " + $gray + "[disabled]" + $reset + "  no URL configured"
        print $line
      }
    } else {
      let line = $blue + "  db" + $reset + " [" + $gray + "-" + $reset + "] " + $gray + "disabled" + $reset
      print $line
    }
  }

  # NATS status
  let nats_config = ($config.nats_events? | default { enabled: false, url: "" })
  if ($nats_config.enabled | default false) {
    let nats_url = ($nats_config.url? | default "nats://localhost:4222")
    let line = $blue + "  nats" + $reset + " " + $gray + "[event system]" + $reset + "  " + $yellow + $nats_url + $reset
    print $line
  } else {
    let line = $blue + "  nats" + $reset + " [" + $gray + "-" + $reset + "] " + $gray + "disabled" + $reset
    print $line
  }

  print ""
  print ($gray + "  Manage:   ontoref services <start|stop|restart> daemon" + $reset)
  print ($gray + "  Monitor:  ontoref services <status|health> [daemon|db]" + $reset)
  print ($gray + "  Events:   strat nats <status|listen|emit>" + $reset)
  print ""
}

# -- Lifecycle Management -----------------------------------------------------

export def "services start" [id?: string]: nothing -> nothing {
  let requested = ($id | default "daemon")

  match $requested {
    "daemon" => {
      if (daemon-running?) {
        print "  ✓ daemon already running"
      } else {
        daemon-start
      }
    },
    "db" => { print "  [ext] database is external - manage separately" },
    _ => { print $"  [warn] unknown service: ($requested)" }
  }
}

export def "services stop" [id?: string]: nothing -> nothing {
  let requested = ($id | default "daemon")

  match $requested {
    "daemon" => { daemon-stop },
    "db" => { print "  [ext] database must be stopped externally" },
    _ => { print $"  [warn] unknown service: ($requested)" }
  }
}

export def "services restart" [id?: string]: nothing -> nothing {
  let requested = ($id | default "daemon")

  match $requested {
    "daemon" => {
      print "  Restarting daemon..."
      daemon-stop
      sleep 500ms
      daemon-start
    },
    "db" => { print "  [ext] database must be restarted externally" },
    _ => { print $"  [warn] unknown service: ($requested)" }
  }
}

export def "services status" [id?: string]: nothing -> nothing {
  if ($id | is-not-empty) {
    match $id {
      "daemon" => {
        print ""
        if (daemon-running?) {
          let pid_file = (daemon-pid-file)
          let pid = (open $pid_file | str trim)
          print $"  daemon: running (PID $pid)"
        } else {
          print "  daemon: stopped"
        }
        print ""
      },
      "db" => { print "  database: check externally" },
      _ => { print $"  unknown service: ($id)" }
    }
  } else {
    services overview
  }
}

export def "services health" [id?: string]: nothing -> nothing {
  if ($id | is-not-empty) {
    match $id {
      "daemon" => { daemon-health },
      "db" => { db-health },
      _ => { print $"  unknown service: ($id)" }
    }
  } else {
    let services = (get-services-list | get id)
    for svc_id in $services {
      match $svc_id {
        "daemon" => { daemon-health },
        "db" => { db-health },
        _ => {}
      }
    }
  }
}

# -- Built-in Service Handlers ------------------------------------------------

def daemon-start []: nothing -> nothing {
  let root = ($env.ONTOREF_PROJECT_ROOT? | default $env.ONTOREF_ROOT)
  let pid_file = (daemon-pid-file)

  print "  Starting daemon..."
  mkdir ($pid_file | path dirname)

  do { \^ontoref-daemon --project-root $root --pid-file $pid_file } &

  sleep 500ms

  if (daemon-running?) {
    print "  ✓ daemon started"
  } else {
    print "  ✗ failed to start daemon"
  }
}

def daemon-stop []: nothing -> nothing {
  if not (daemon-running?) {
    print "  ✓ daemon not running"
    return
  }

  let pid_file = (daemon-pid-file)
  let pid = (open $pid_file | str trim)

  print "  Stopping daemon..."
  do { ^kill $pid } | complete

  sleep 500ms

  if not (daemon-running?) {
    print "  ✓ daemon stopped"
    rm -f $pid_file
  } else {
    do { ^kill -9 $pid } | complete
    rm -f $pid_file
  }
}

def daemon-health []: nothing -> nothing {
  if not (daemon-running?) {
    print "  ✗ daemon not running"
    return
  }

  let url = ($env.ONTOREF_DAEMON_URL? | default "http://127.0.0.1:7891")
  let result = (do { ^curl -sf $"($url)/health" } | complete)

  if $result.exit_code != 0 {
    print "  ✗ daemon health check failed"
    return
  }

  let health = ($result.stdout | from json)
  print $"  ✓ daemon healthy (uptime: ($health.uptime_secs)s, cache: ($health.cache_entries) entries)"
}

def db-health []: nothing -> nothing {
  let config = (get-project-config)
  let db_config = ($config.services.services? | default [] | where id == "db" | first)

  if ($db_config.config.url? | default "" | is-empty) {
    print "  ⚠ database URL not configured"
    return
  }

  let url = $db_config.config.url
  let result = (do { ^curl -sf $url } | complete)

  if $result.exit_code == 0 {
    print $"  ✓ database healthy ($url)"
  } else {
    print $"  ✗ database check failed ($url)"
  }
}
