#!/usr/bin/env bash

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

declare -A _LOADED_COMMANDS=()

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

# ============================================
# 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

#   command::_reset_mixin_state          # reset values only, keep _ACTIVE_MIXINS

#   local -a args=("$@")
#   command::_preprocess_flags args

#   local fn
#   fn=$(command::fn "$cmd" run)
#   core::call_function "$fn" ${args[@]+"${args[@]}"}
# }

function command::run() {
  local cmd="$1"
  shift
 
  command::_reset_mixin_state
 
  # Build default args from config
  local -a default_args=()
  local defaults="${_COMMAND_DEFAULTS[$cmd]:-}"
  if [[ -n "$defaults" ]]; then
    read -ra default_args <<< "$defaults"
  fi
 
  local -a user_args=("$@")
  [[ $# -gt 0 ]] && user_args=("$@")
 
  # Resolve exclusive group conflicts — user args override defaults
  local groups="${_FLAG_EXCLUSIVE_GROUPS[$cmd]:-}"
  if [[ -n "$groups" && ${#default_args[@]} -gt 0 && ${#user_args[@]} -gt 0 ]]; then
    command::_resolve_conflicts default_args user_args "$groups"
  fi

  local -a cleaned_defaults=()
  for _d in "${default_args[@]:-}"; do
    [[ -n "$_d" ]] && cleaned_defaults+=("$_d")
  done
  default_args=("${cleaned_defaults[@]:-}")

  local -a args=()
  for _d in "${default_args[@]:-}"; do
    [[ -n "$_d" ]] && args+=("$_d")
  done
  for _u in "${user_args[@]:-}"; do
    [[ -n "$_u" ]] && args+=("$_u")
  done
 
  # Preprocess mixin flags (--json, --no-color etc)
  command::_preprocess_flags args

  local fn
  fn=$(command::fn "$cmd" run)
  core::call_function "$fn" ${args[@]+"${args[@]}"}
}
 

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

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

  return 0
}
