#!/usr/bin/env bash

# ============================================
# Command Registry
# ============================================

declare -A _LOADED_COMMANDS=()

readonly _COMMAND_NAMESPACE="cmd"
readonly _COMMAND_AUTO_LOAD_HOOK="on_load"

# ============================================
# Helpers
# ============================================

function command::loaded() { [[ -n "${_LOADED_COMMANDS["$1"]:-}" ]]; }

# Convert path-style name to namespace
# e.g. service/wireguard -> service::wireguard
function command::to_namespace() { echo "${1//\//:}"; }

# Build fully qualified function name
# e.g. command::fn "add" "run" -> cmd::add::run
# e.g. command::fn "service/wg" "run" -> cmd::service::wg::run
function command::fn() {
  local name namespace
  namespace=$(command::to_namespace "$1")
  echo "${_COMMAND_NAMESPACE}::${namespace}::${2}"
}

function command::has_function() { declare -F "$(command::fn "$1" "$2")" >/dev/null 2>&1; }
function command::is_auto_load() { declare -F "$(command::fn "$1" on_load)" >/dev/null 2>&1; }
function command::exists()       { command::has_function "$1" run; }

# ============================================
# Runner
# ============================================

function command::run() {
  local cmd="$1"
  shift
  local fn
  fn=$(command::fn "$cmd" run)
  core::call_function "$fn" "$@"
}

function core::call_function() {
  local fn="$1"
  shift
  "$fn" "$@"
}

# ============================================
# Loader
# ============================================

function load_command() {
  local name="$1"

  command::loaded "$name" && return 0

  local path
  path="$(ctx::commands)/${name}.command.sh"

  if [[ ! -f "$path" ]]; then
    log::error "Command not found: ${name} (${path})"
    return 1
  fi

  source "$path"
  _LOADED_COMMANDS["$name"]=1

  core::call_if_exists "$(command::fn "$name" on_load)"

  return 0
}
