#!/bin/bash

# arty - A bash library repository management system
# Version: 1.0.0

set -euo pipefail

ARTY_CONFIG_FILE="${ARTY_CONFIG_FILE:-arty.yml}"
ARTY_ENV="${ARTY_ENV:-default}"
ARTY_DRY_RUN="${ARTY_DRY_RUN:-0}"
ARTY_VERBOSE="${ARTY_VERBOSE:-0}"

# Enable colors only when stdout is a real terminal (or FORCE_COLOR=1 overrides)
if [[ -t 1 && "${TERM:-}" != "dumb" ]]; then
  export FORCE_COLOR=${FORCE_COLOR:-"1"}
else
  export FORCE_COLOR=${FORCE_COLOR:-"0"}
fi
if [[ "$FORCE_COLOR" = "0" ]]; then
  export RED=''
  export GREEN=''
  export YELLOW=''
  export BLUE=''
  export CYAN=''
  export MAGENTA=''
  export BOLD=''
  export NC=''
  else
  export RED='\033[0;31m'
  export GREEN='\033[0;32m'
  export YELLOW='\033[1;33m'
  export BLUE='\033[0;34m'
  export CYAN='\033[0;36m'
  export MAGENTA='\033[0;35m'
  export BOLD='\033[1m'
  export NC='\033[0m'
fi

# Global array to track installation stack (prevent circular dependencies)
declare -g -A ARTY_INSTALL_STACK

# Logging functions
log_info() {
  echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
  echo -e "${GREEN}[ok]${NC} $1"
}

log_warn() {
  echo -e "${YELLOW}[!]${NC} $1"
}

log_error() {
  echo -e "${RED}[x]${NC} $1"
}

log_debug() {
  if [[ "$ARTY_VERBOSE" == "1" ]]; then
    echo -e "${CYAN}[DEBUG]${NC} $1"
  fi
}

log_step() {
  echo -e "${CYAN}[!]${NC} $1"
}

# ============================================================================
# ARTY.SH CORE FUNCTIONS - Repository Management
# ============================================================================

# Expand environment variables in a string with validation
# Supports nested expansion (e.g., $AI_ROOT/subdir where AI_ROOT contains another var)
# Fails if any undefined variables remain after expansion
expand_env_vars() {
  local input="$1"
  local max_iterations=10
  local iteration=0
  local expanded="$input"

  # Keep expanding until no more variables or max iterations reached
  while [[ "$expanded" =~ \$ ]] && [[ $iteration -lt $max_iterations ]]; do
    # Use eval to expand variables
    expanded=$(eval echo "$expanded" 2>/dev/null || echo "$expanded")
    iteration=$((iteration+1))
  done

  # Check if any unexpanded variables remain
  if [[ "$expanded" =~ \$[A-Za-z_][A-Za-z0-9_]* ]]; then
    # Extract the undefined variable name for better error message
    local undefined_var=$(echo "$expanded" | grep -o '\$[A-Za-z_][A-Za-z0-9_]*' | head -1)
    log_error "Undefined environment variable in path: ${undefined_var}"
    log_error "  Original: $input"
    log_error "  Expanded: $expanded"
    log_error ""
    log_error "Available environment variables from arty.yml:"
    if [[ -f "$ARTY_CONFIG_FILE" ]]; then
      yq eval '.envs.default | keys | .[]' "$ARTY_CONFIG_FILE" 2>/dev/null | sed 's/^/  - /' || true
    fi
    return 1
  fi

  echo "$expanded"
}

# Load environment variables from arty.yml
load_env_vars() {
  local config_file="${1:-$ARTY_CONFIG_FILE}"

  if [[ ! -f "$config_file" ]]; then
    return 0 # No config file, nothing to load
  fi

  # Check if YAML is valid
  if ! yq eval '.' "$config_file" >/dev/null 2>&1; then
    log_warn "Invalid YAML in config file, skipping env vars"
    return 0
  fi

  # Check if envs section exists
  local has_envs=$(yq eval '.envs' "$config_file" 2>/dev/null)
  if [[ "$has_envs" == "null" ]] || [[ -z "$has_envs" ]]; then
    return 0 # No envs section
  fi

  local current_env="$ARTY_ENV"
  log_debug "Loading environment variables from '$current_env' environment"

  # First load default variables if they exist
  local default_envs=$(yq eval '.envs.default | to_entries | .[] | .key + "=" + .value' "$config_file" 2>/dev/null)
  if [[ -n "$default_envs" ]] && [[ "$default_envs" != "null" ]]; then
    while IFS='=' read -r key value; do
      if [[ -n "$key" ]] && [[ "$key" != "null" ]] && [[ -n "$value" ]]; then
        # Only export if not already set (for default env only)
        if [[ "$current_env" == "default" ]] && [[ -n "${!key:-}" ]]; then
          log_debug "  Skipping $key (already set)"
          continue
        fi
        export "$key=$value"
        log_debug "  Set $key (from default)"
      fi
    done <<< "$default_envs"
  fi

  # Then load environment-specific variables (which can override defaults)
  if [[ "$current_env" != "default" ]]; then
    local env_envs=$(yq eval ".envs.$current_env | to_entries | .[] | .key + \"=\" + .value" "$config_file" 2>/dev/null)
    if [[ -n "$env_envs" ]] && [[ "$env_envs" != "null" ]]; then
      while IFS='=' read -r key value; do
        if [[ -n "$key" ]] && [[ "$key" != "null" ]] && [[ -n "$value" ]]; then
          export "$key=$value"
          log_debug "  Set $key (from $current_env)"
        fi
      done <<< "$env_envs"
    fi
  fi
}

# Wrapper to limit Go thread usage on hosts with low ulimit -u (e.g. IONOS shared hosting).
# Always returns 0 so set -e doesn't propagate a yq crash through `local var=$(yq ...)`.
yq() {
  GOMAXPROCS=1 GOGC=off command yq "$@" || return 0
}

# Auto-install mikefarah/yq v4 using whatever package manager is available.
_install_yq() {
  if command -v brew &>/dev/null; then
    log_info "Installing yq via Homebrew..."
    brew install yq && return 0
  fi

  if command -v snap &>/dev/null; then
    log_info "Installing yq via snap..."
    snap install yq && return 0
  fi

  if command -v apk &>/dev/null; then
    log_info "Installing yq via apk..."
    apk add --no-cache yq-go && return 0
  fi

  if command -v pacman &>/dev/null; then
    log_info "Installing yq via pacman..."
    pacman -S --noconfirm go-yq && return 0
  fi

  if command -v go &>/dev/null; then
    log_info "Installing yq via go install..."
    local go_bin="${HOME}/.local/bin"
    mkdir -p "$go_bin"
    GOBIN="$go_bin" go install github.com/mikefarah/yq/v4@latest && {
      export PATH="${go_bin}:${PATH}"
      return 0
    }
  fi

  # Binary download fallback (Linux / macOS)
  local os arch install_dir use_sudo=0
  os=$(uname -s | tr '[:upper:]' '[:lower:]')
  case "$(uname -m)" in
    x86_64)        arch="amd64" ;;
    aarch64|arm64) arch="arm64" ;;
    armv7*)        arch="arm" ;;
    *)             arch="amd64" ;;
  esac

  local yq_url="https://github.com/mikefarah/yq/releases/latest/download/yq_${os}_${arch}"

  if [[ -w /usr/local/bin ]]; then
    install_dir="/usr/local/bin"
  elif command -v sudo &>/dev/null && sudo -n true 2>/dev/null; then
    install_dir="/usr/local/bin"
    use_sudo=1
  else
    install_dir="${HOME}/.local/bin"
    mkdir -p "$install_dir"
    export PATH="${install_dir}:${PATH}"
  fi

  log_info "Downloading yq binary (${os}/${arch}) to ${install_dir}..."

  if command -v wget &>/dev/null; then
    if [[ "$use_sudo" == "1" ]]; then
      sudo wget -q "$yq_url" -O "${install_dir}/yq" || return 1
      sudo chmod +x "${install_dir}/yq"
    else
      wget -q "$yq_url" -O "${install_dir}/yq" || return 1
      chmod +x "${install_dir}/yq"
    fi
    return 0
  elif command -v curl &>/dev/null; then
    if [[ "$use_sudo" == "1" ]]; then
      sudo curl -fsSL "$yq_url" -o "${install_dir}/yq" || return 1
      sudo chmod +x "${install_dir}/yq"
    else
      curl -fsSL "$yq_url" -o "${install_dir}/yq" || return 1
      chmod +x "${install_dir}/yq"
    fi
    return 0
  fi

  return 1
}

# Check if mikefarah/yq v4 is installed; auto-install if not.
check_yq() {
  local need_install=0

  # Use type -P to find the actual binary; command -v also matches the yq shell wrapper
  # function defined in this script and would give a false positive.
  if ! type -P yq &>/dev/null; then
    log_warn "yq is not installed — attempting automatic installation..."
    need_install=1
  else
    local ver_out
    ver_out=$(command yq --version 2>&1) || true  # guard: command not found → exit 127 + set -e
    if [[ ! "$ver_out" =~ mikefarah ]]; then
      log_warn "Found yq but it is not mikefarah/yq v4 (got: ${ver_out})"
      log_warn "arty requires mikefarah/yq v4 — attempting automatic installation..."
      need_install=1
    fi
  fi

  if [[ "$need_install" == "0" ]]; then
    return 0
  fi

  if _install_yq && command -v yq &>/dev/null; then
    local ver_out
    ver_out=$(command yq --version 2>&1)
    log_success "yq installed: ${ver_out}"
    return 0
  fi

  log_error "Could not install yq automatically. Please install mikefarah/yq v4:"
  log_error "  brew install yq                                  (macOS / Linuxbrew)"
  log_error "  snap install yq                                  (Linux with snap)"
  log_error "  apk add yq-go                                    (Alpine Linux)"
  log_error "  pacman -S go-yq                                  (Arch Linux)"
  log_error "  go install github.com/mikefarah/yq/v4@latest     (Go toolchain)"
  log_error "  wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 \\"
  log_error "       -O /usr/local/bin/yq && chmod +x /usr/local/bin/yq"
  log_error "See: https://github.com/mikefarah/yq#install"
  exit 1
}

# Initialize arty environment
init_arty() {
  local arty_home="${ARTY_HOME:-$PWD/.arty}"
  local libs_dir="${ARTY_LIBS_DIR:-$arty_home/libs}"
  local bin_dir="${ARTY_BIN_DIR:-$arty_home/bin}"

  if [[ ! -d "$arty_home" ]]; then
    mkdir -p "$libs_dir"
    mkdir -p "$bin_dir"
    log_success "Initialized arty at $arty_home"
  fi
}

# Get a field from YAML using yq
get_yaml_field() {
  local file="$1"
  local field="$2"

  if [[ ! -f "$file" ]]; then
    return 1
  fi

  # Check if file has valid YAML - yq will exit with error on invalid YAML
  if ! yq eval '.' "$file" >/dev/null 2>&1; then
    return 1
  fi

  yq eval ".$field" "$file" 2>/dev/null || echo ""
}

# Get array items from YAML using yq
get_yaml_array() {
  local file="$1"
  local field="$2"

  if [[ ! -f "$file" ]]; then
    return 1
  fi

  # Check if file has valid YAML - yq will exit with error on invalid YAML
  if ! yq eval '.' "$file" >/dev/null 2>&1; then
    return 1
  fi

  yq eval ".${field}[]" "$file" 2>/dev/null
}

# Get script command from YAML
get_yaml_script() {
  local file="$1"
  local script_name="$2"

  if [[ ! -f "$file" ]]; then
    return 1
  fi

  yq eval ".scripts[\"${script_name}\"]" "$file" 2>/dev/null || echo "null"
}

# List all script names from YAML
list_yaml_scripts() {
  local file="$1"

  if [[ ! -f "$file" ]]; then
    return 1
  fi

  yq eval '.scripts | keys | .[]' "$file" 2>/dev/null
}

# List all scripts defined in arty.yml
list_scripts() {
  if [[ ! -f "$ARTY_CONFIG_FILE" ]]; then
    log_error "Config file not found: $ARTY_CONFIG_FILE"
    log_info "Run this command in a directory with arty.yml"
    return 1
  fi

  local script_names
  script_names=$(list_yaml_scripts "$ARTY_CONFIG_FILE")

  if [[ -z "$script_names" ]]; then
    log_info "No scripts defined in $ARTY_CONFIG_FILE"
    return 0
  fi

  echo
  printf "${BOLD}Scripts${NC}\n"
  echo
  while IFS= read -r name; do
    [[ -z "$name" ]] && continue
    local cmd
    cmd=$(get_yaml_script "$ARTY_CONFIG_FILE" "$name")
    printf "  ${GREEN}%-20s${NC} %s\n" "$name" "$cmd"
  done <<< "$script_names"
  echo
}

# Execute a script defined in arty.yml
exec_script() {
  local script_name="$1"
  shift

  if [[ ! -f "$ARTY_CONFIG_FILE" ]]; then
    log_error "Config file not found: $ARTY_CONFIG_FILE"
    log_info "Run this command in a directory with arty.yml"
    return 1
  fi

  local cmd
  cmd=$(get_yaml_script "$ARTY_CONFIG_FILE" "$script_name")

  if [[ -z "$cmd" ]] || [[ "$cmd" == "null" ]]; then
    log_error "Unknown command: $script_name"
    log_info "Available scripts:"
    while IFS= read -r name; do
      [[ -n "$name" ]] && echo "  - $name"
    done < <(list_yaml_scripts "$ARTY_CONFIG_FILE")
    return 1
  fi

  log_info "Running: $script_name"
  [[ ! "$cmd" =~ \$@ ]] && cmd="$cmd"' "$@"'
  bash -c "$cmd" -- "$@"
}

# Get library name from repository URL
get_lib_name() {
  local repo_url="$1"
  basename "$repo_url" .git
}

# Parse reference - can be a string URL or an object with url, into, ref, env
# Returns: url|into|ref|env (pipe-delimited)
# env can be a single value or comma-separated list
parse_reference() {
  local config_file="$1"
  local ref_index="$2"

  # Single yq call using only // and select — if-then-else is not supported in
  # all yq v4 builds. .url//.  handles both string refs (no .url key) and object
  # refs. env array is joined; null env falls back to empty string.
  yq eval ".references[$ref_index] | (.url // .) + \"|\" + (.into // \"\") + \"|\" + (.ref // \"\") + \"|\" + ((.env | select(tag == \"!!seq\") | join(\",\")) // .env // \"\")" "$config_file" 2>/dev/null
}

# Check if current environment matches the filter
# env_filter can be a single env or comma-separated list
check_env_match() {
  local current_env="$1"
  local env_filter="$2"

  # No filter means match all
  [[ -z "$env_filter" ]] && return 0

  # Convert comma-separated list to array
  IFS=',' read -ra env_list <<< "$env_filter"

  # Check if current env is in the list
  for env in "${env_list[@]}"; do
    # Trim whitespace
    env=$(echo "$env" | xargs)
    if [[ "$env" == "$current_env" ]]; then
      return 0
    fi
  done

  return 1
}

# Get git information for a repository
get_git_info() {
  local repo_dir="$1"

  if [[ ! -d "$repo_dir/.git" ]]; then
    echo "|||0"
    return
  fi

  # Get short commit hash
  local commit_hash=$(cd "$repo_dir" && git rev-parse --short HEAD 2>/dev/null || echo "")

  # Get all refs pointing to current commit (tags, branches)
  local refs=$(cd "$repo_dir" && git describe --all --exact-match 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || echo "")

  # Clean up refs (remove heads/ and tags/ prefixes)
  refs=$(echo "$refs" | sed 's#^heads/##' | sed 's#^tags/##')

  # Check if dirty (has uncommitted changes)
  local is_dirty=0
  if [[ -n "$(cd "$repo_dir" && git status --porcelain 2>/dev/null)" ]]; then
    is_dirty=1
  fi

  echo "$commit_hash|$refs|$is_dirty"
}

# Normalize library identifier for tracking
normalize_lib_id() {
  local repo_url="$1"
  # Convert to lowercase and remove .git suffix for consistent tracking
  local normalized="${repo_url,,}"
  normalized=$(echo "$normalized" | sed 's/\.git$//')

  # Normalize different Git URL formats to the same path
  # https://github.com/user/repo -> github.com/user/repo
  # git@github.com:user/repo -> github.com/user/repo
  # ssh://git@github.com/user/repo -> github.com/user/repo
  normalized=$(echo "$normalized" | sed -E 's#^https?://##' | sed -E 's#^git@([^:]+):#\1/#' | sed -E 's#^ssh://git@##')

  echo "$normalized"
}

# Check if library is in installation stack
is_installing() {
  local lib_id="$1"
  [[ -n "${ARTY_INSTALL_STACK[$lib_id]:-}" ]]
}

# Add library to installation stack
mark_installing() {
  local lib_id="$1"
  ARTY_INSTALL_STACK[$lib_id]=1
}

# Remove library from installation stack
unmark_installing() {
  local lib_id="$1"
  unset ARTY_INSTALL_STACK[$lib_id]
}

# Check if library is already installed
is_installed() {
  local lib_name="$1"
  local libs_dir="${ARTY_LIBS_DIR:-${ARTY_HOME:-$PWD/.arty}/libs}"
  [[ -d "$libs_dir/$lib_name" ]]
}

# Install a library from git repository
install_lib() {
  local repo_url="$1"
  local lib_name="${2:-$(get_lib_name "$repo_url")}"
  local git_ref="${3:-}"
  local custom_into="${4:-}"
  local config_file="${5:-$ARTY_CONFIG_FILE}"

  # Determine installation directory
  local lib_dir
  if [[ -n "$custom_into" ]]; then
    # Expand environment variables in custom_into path
    local expanded_into
    if ! expanded_into=$(expand_env_vars "$custom_into"); then
      log_error "Failed to expand environment variables in 'into' path for ${repo_url}"
      return 1
    fi

    # Check if expanded path is absolute or relative
    if [[ "$expanded_into" == /* ]]; then
      # Absolute path - use as-is
      lib_dir="$expanded_into"
    else
      # Relative path - make relative to config file directory
      local config_dir=$(dirname "$(realpath "${config_file}")")
      lib_dir="$config_dir/$expanded_into"
    fi
  else
    # Use global .arty/libs for libraries without custom 'into'
    lib_dir="$ARTY_LIBS_DIR/$lib_name"
  fi

  # Normalize the library identifier for circular dependency detection
  # Include installation path to allow same library at different locations
  local lib_id=$(normalize_lib_id "$repo_url")
  local lib_id_with_path="${lib_id}@${lib_dir}"

  # Check for circular dependency
  if is_installing "$lib_id_with_path"; then
    log_warn "Circular dependency detected: $lib_name (already being installed at $lib_dir)"
    log_info "Skipping to prevent infinite loop"
    return 0
  fi

  # Check if already installed (optimization)
  if [[ -d "$lib_dir" ]]; then
    log_info "Library '$lib_name' already installed at $lib_dir"

    if [[ "$ARTY_DRY_RUN" == "1" ]]; then
      log_info "[DRY RUN] Would check for updates..."
      return 0
    fi

    # Try to update
    if [[ -n "$git_ref" ]]; then
      (cd "$lib_dir" && git fetch -q 2>/dev/null && git checkout -q "$git_ref" && git pull -q 2>/dev/null) || {
        log_warn "Failed to update library (continuing with existing version)"
      }
    else
      (cd "$lib_dir" && git fetch -q 2>/dev/null && git pull -q 2>/dev/null) || {
        log_warn "Failed to update library (continuing with existing version)"
      }
    fi

    return 0
  fi

  # Mark as currently installing
  mark_installing "$lib_id_with_path"

  if [[ "$ARTY_DRY_RUN" != "1" ]]; then
    init_arty
  fi

  log_info "Installing library: $lib_name"
  log_info "Repository: $repo_url"
  log_info "Git ref: ${git_ref:-<default>}"
  log_info "Location: $lib_dir"

  if [[ "$ARTY_DRY_RUN" == "1" ]]; then
    log_info "[DRY RUN] Would clone repository${git_ref:+ and checkout $git_ref}"
    unmark_installing "$lib_id_with_path"
    return 0
  fi

  # Clone the repository
  git clone "$repo_url" "$lib_dir" || {
    log_error "Failed to clone repository"
    unmark_installing "$lib_id_with_path"
    return 1
  }

  # Checkout the specified ref (skip if none given; clone already checked out the default branch)
  if [[ -n "$git_ref" ]]; then
    (cd "$lib_dir" && git checkout -q "$git_ref") || {
      log_warn "Failed to checkout ref '$git_ref', using default branch"
    }
  fi

  # Run setup hook if exists
  if [[ -f "$lib_dir/setup.sh" ]]; then
    log_info "Running setup hook..."
    (cd "$lib_dir" && bash setup.sh) || {
      log_warn "Setup hook failed, continuing anyway..."
    }
  fi

  # Process arty.yml if it exists
  if [[ -f "$lib_dir/arty.yml" ]]; then
    # Link main script to .arty/bin (only for standard installations, not custom 'into')
    if [[ -z "$custom_into" ]]; then
      local main_script=$(get_yaml_field "$lib_dir/arty.yml" "main")
      if [[ -n "$main_script" ]] && [[ "$main_script" != "null" ]]; then
        local main_file="$lib_dir/$main_script"
        if [[ -f "$main_file" ]]; then
          local local_bin_dir="$ARTY_BIN_DIR"
          local lib_name_stripped="$(basename $main_file .sh)"
          local bin_link="$local_bin_dir/$lib_name_stripped"

          log_info "Linking main script: $main_script -> $bin_link"
          ln -sf "$main_file" "$bin_link"
          chmod +x "$main_file"
          log_success "Main script linked to $bin_link"
        fi
      fi
    fi

    # Install nested dependencies (always, regardless of 'into')
    log_info "Found arty.yml, checking for references..."
    install_references "$lib_dir/arty.yml"
  fi

  # Unmark as installing (we're done with this library)
  unmark_installing "$lib_id_with_path"

  log_success "Library '$lib_name' installed successfully"
  log_info "Location: $lib_dir"
}

# Find if a library is defined in current or ancestor configs with 'into'
find_ancestor_into() {
  local lib_url="$1"
  local current_config="$2"
  local lib_url_normalized=$(normalize_lib_id "$lib_url")

  # Build list of ancestor configs to check
  # Start with current config's directory and walk up to find parent arty.yml files
  local config_to_check="$current_config"
  local current_dir=$(dirname "$(realpath "$current_config")")

  # Check current config and walk up the directory tree
  while true; do
    if [[ -f "$config_to_check" ]]; then
      # Get count of references
      local ref_count=$(yq eval '.references | length' "$config_to_check" 2>/dev/null)
      if [[ -n "$ref_count" ]] && [[ "$ref_count" != "null" ]] && [[ "$ref_count" != "0" ]]; then
        # Check each reference
        for ((i = 0; i < ref_count; i++)); do
          local ref_data=$(parse_reference "$config_to_check" "$i")
          IFS='|' read -r url into git_ref env_filter <<<"$ref_data"

          local url_normalized=$(normalize_lib_id "$url")
          if [[ "$url_normalized" == "$lib_url_normalized" ]] && [[ -n "$into" ]]; then
            # Found it with an 'into' directive
            echo "$into|$config_to_check"
            return
          fi
        done
      fi
    fi

    # Check if we've reached the root ARTY_CONFIG_FILE (after checking it)
    if [[ "$(realpath "$config_to_check" 2>/dev/null)" == "$(realpath "$ARTY_CONFIG_FILE" 2>/dev/null)" ]]; then
      # We've checked the root config, stop here
      break
    fi

    # Move up one directory
    local parent_dir=$(dirname "$current_dir")
    if [[ "$parent_dir" == "$current_dir" ]] || [[ "$parent_dir" == "/" ]]; then
      # Reached filesystem root, stop
      break
    fi
    current_dir="$parent_dir"
    config_to_check="$current_dir/arty.yml"
  done

  echo ""
}

# Install all references from arty.yml
install_references() {
  local config_file="${1:-$ARTY_CONFIG_FILE}"

  if [[ ! -f "$config_file" ]]; then
    log_error "Config file not found: $config_file"
    return 1
  fi

  # Check if YAML is valid by trying to read it
  if ! yq eval '.' "$config_file" >/dev/null 2>&1; then
    log_error "Invalid YAML in config file: $config_file"
    return 1
  fi

  # Initialize arty directory structure first (unless dry run)
  if [[ "$ARTY_DRY_RUN" != "1" ]]; then
    init_arty
  fi

  # Count references
  local ref_count=$(yq eval '.references | length' "$config_file" 2>/dev/null)
  if [[ -z "$ref_count" ]] || [[ "$ref_count" == "null" ]] || [[ "$ref_count" == "0" ]]; then
    log_info "No references to install"
    return 0
  fi

  # Process each reference by index
  local i
  for ((i = 0; i < ref_count; i++)); do
    # Parse the reference
    local ref_data=$(parse_reference "$config_file" "$i")
    IFS='|' read -r url into git_ref env_filter <<<"$ref_data"

    # Skip empty URLs
    if [[ -z "$url" ]] || [[ "$url" == "null" ]]; then
      continue
    fi

    # Check environment filter using the new check_env_match function
    if [[ -n "$env_filter" ]] && ! check_env_match "$ARTY_ENV" "$env_filter"; then
      log_info "Skipping reference (env filter: [$env_filter], current: $ARTY_ENV): $url"
      continue
    fi

    # Get library name
    local lib_name=$(get_lib_name "$url")

    # If no 'into' specified, check if an ancestor config defines it with 'into'
    local effective_config="$config_file"
    if [[ -z "$into" ]] && [[ "$config_file" != "$ARTY_CONFIG_FILE" ]]; then
      local ancestor_result=$(find_ancestor_into "$url" "$config_file")
      if [[ -n "$ancestor_result" ]]; then
        IFS='|' read -r ancestor_into ancestor_config <<<"$ancestor_result"
        into="$ancestor_into"
        effective_config="$ancestor_config"
      fi
    fi

    log_info "Installing reference: $url"
    [[ -n "$into" ]] && log_info "Custom location: $into"
    [[ -n "$env_filter" ]] && log_info "Environment filter: [$env_filter]"

    install_lib "$url" "$lib_name" "$git_ref" "$into" "$effective_config" || log_warn "Failed to install reference: $url"
  done
}

# Build a reference tree from arty.yml showing all dependencies
build_reference_tree() {
  local config_file="${1:-$ARTY_CONFIG_FILE}"
  local indent="${2:-}"
  local is_last="${3:-1}"
  local visited_file="${4:-/tmp/arty_visited_$$}"
  local depth="${5:-0}"

  if [[ ! -f "$config_file" ]]; then
    return 0
  fi

  # Track visited library directories to prevent infinite circular recursion
  # Only track at depth > 0 to allow root-level refs to show even if already nested
  local config_dir=$(dirname "$(realpath "$config_file" 2>/dev/null || echo "$config_file")")
  if [[ "$depth" -gt 0 ]]; then
    if grep -qx "$config_dir" "$visited_file" 2>/dev/null; then
      return 0
    fi
    echo "$config_dir" >>"$visited_file"
  fi

  # Count references
  local ref_count=$(yq eval '.references | length' "$config_file" 2>/dev/null)
  if [[ -z "$ref_count" ]] || [[ "$ref_count" == "null" ]] || [[ "$ref_count" == "0" ]]; then
    return 0
  fi

  # Process each reference
  local i
  for ((i = 0; i < ref_count; i++)); do
    local ref_data=$(parse_reference "$config_file" "$i")
    IFS='|' read -r url into git_ref env_filter <<<"$ref_data"

    # Skip if no URL or env filter doesn't match
    if [[ -z "$url" ]] || [[ "$url" == "null" ]]; then
      continue
    fi
    if [[ -n "$env_filter" ]] && ! check_env_match "$ARTY_ENV" "$env_filter"; then
      continue
    fi

    local lib_name=$(get_lib_name "$url")

    # Calculate is_last_ref by counting remaining valid refs
    local is_last_ref=0
    local remaining=0
    for ((j = i + 1; j < ref_count; j++)); do
      local check_ref=$(parse_reference "$config_file" "$j")
      IFS='|' read -r check_url _ _ check_env <<<"$check_ref"
      if [[ -n "$check_url" ]] && [[ "$check_url" != "null" ]]; then
        if [[ -z "$check_env" ]] || check_env_match "$ARTY_ENV" "$check_env"; then
          remaining=1
          break
        fi
      fi
    done
    [[ "$remaining" == "0" ]] && is_last_ref=1

    # If no 'into' specified, check if an ancestor config defines it with 'into'
    local effective_config="$config_file"
    if [[ -z "$into" ]] && [[ "$config_file" != "$ARTY_CONFIG_FILE" ]]; then
      local ancestor_result=$(find_ancestor_into "$url" "$config_file")
      if [[ -n "$ancestor_result" ]]; then
        IFS='|' read -r ancestor_into ancestor_config <<<"$ancestor_result"
        into="$ancestor_into"
        effective_config="$ancestor_config"
      fi
    fi

    # Determine installation directory
    local lib_dir
    if [[ -n "$into" ]]; then
      # Expand environment variables
      local expanded_into
      expanded_into=$(expand_env_vars "$into" 2>/dev/null) || expanded_into="$into"

      # Check if expanded path is absolute
      if [[ "$expanded_into" == /* ]]; then
        lib_dir="$expanded_into"
      else
        local config_dir=$(dirname "$(realpath "${effective_config}")")
        lib_dir="$config_dir/$expanded_into"
      fi
    else
      lib_dir="$ARTY_LIBS_DIR/$lib_name"
    fi

    # Get version and git info
    local version=""
    local location="$lib_dir"

    if [[ -f "$lib_dir/arty.yml" ]]; then
      version=$(get_yaml_field "$lib_dir/arty.yml" "version")
      [[ "$version" == "null" ]] || [[ -z "$version" ]] && version=""
    fi

    # Get git information
    local git_info=$(get_git_info "$lib_dir")
    IFS='|' read -r commit_hash git_refs is_dirty <<<"$git_info"

    # Determine display version
    local display_version="${version:-$commit_hash}"
    [[ -z "$display_version" ]] && display_version="unknown"

    # Tree characters
    local tree_char="  "
    local tree_continue="   "
    if [[ "$is_last_ref" == "1" ]]; then
      tree_char="  "
      tree_continue="    "
    fi

    # Print the reference line
    printf "%s${tree_char} ${BOLD}${GREEN}%s${NC}" "$indent" "$lib_name"
    printf " ${CYAN}%s${NC}" "$display_version"

    # Add git refs if available
    if [[ -n "$git_refs" ]]; then
      printf " ${BLUE}(%s)${NC}" "$git_refs"
    fi

    # Add dirty indicator
    if [[ "$is_dirty" == "1" ]]; then
      printf " ${YELLOW}*${NC}"
    fi

    # Add location info
    if [[ -n "$into" ]]; then
      printf " ${MAGENTA}-> %s${NC}" "$into"
    fi

    echo

    # Recursively show nested dependencies
    if [[ -f "$lib_dir/arty.yml" ]]; then
      local next_depth=$((depth + 1))
      build_reference_tree "$lib_dir/arty.yml" "${indent}${tree_continue}" "$is_last_ref" "$visited_file" "$next_depth"
    fi
  done
}

# List installed libraries with tree visualization
list_libs() {
  init_arty

  if [[ ! -f "$ARTY_CONFIG_FILE" ]]; then
    # Fallback to simple list if no arty.yml
    if [[ ! -d "$ARTY_LIBS_DIR" ]] || [[ -z "$(ls -A "$ARTY_LIBS_DIR" 2>/dev/null)" ]]; then
      log_info "No libraries installed"
      return 0
    fi

    log_info "Installed libraries:"
    echo

    for lib_dir in "$ARTY_LIBS_DIR"/*; do
      if [[ -d "$lib_dir" ]]; then
        local lib_name=$(basename "$lib_dir")
        local version=""

        # Try to get version from arty.yml using yq
        if [[ -f "$lib_dir/arty.yml" ]]; then
          version=$(get_yaml_field "$lib_dir/arty.yml" "version")
          if [[ "$version" == "null" ]] || [[ -z "$version" ]]; then
            version=""
          fi
        fi

        printf "  ${GREEN}%-20s${NC} %s\n" "$lib_name" "${version:-(unknown version)}"
      fi
    done
    echo
    return 0
  fi

  # Get project info
  local project_name=$(get_yaml_field "$ARTY_CONFIG_FILE" "name")
  local project_version=$(get_yaml_field "$ARTY_CONFIG_FILE" "version")

  # Clean up name/version
  [[ "$project_name" == "null" ]] && project_name="$(basename "$PWD")"
  [[ "$project_version" == "null" ]] && project_version=""

  # Check if there are any references
  local ref_count=$(yq eval '.references | length' "$ARTY_CONFIG_FILE" 2>/dev/null)
  if [[ -z "$ref_count" ]] || [[ "$ref_count" == "null" ]] || [[ "$ref_count" == "0" ]]; then
    # No references defined, check for installed libraries
    local libs_dir="${ARTY_LIBS_DIR:-${ARTY_HOME:-$PWD/.arty}/libs}"
    if [[ ! -d "$libs_dir" ]] || [[ -z "$(ls -A "$libs_dir" 2>/dev/null)" ]]; then
      # Show header but indicate no libraries
      echo
      printf "${BOLD}${GREEN}%s${NC}" "$project_name"
      if [[ -n "$project_version" ]]; then
        printf " ${CYAN}%s${NC}" "$project_version"
      fi
      echo
      echo
      log_info "No libraries installed"
      echo
      return 0
    fi
  fi

  # Print header
  echo
  printf "${BOLD}${GREEN}%s${NC}" "$project_name"
  if [[ -n "$project_version" ]]; then
    printf " ${CYAN}%s${NC}" "$project_version"
  fi
  echo
  echo

  # Create temporary file for tracking visited configs
  local visited_file="/tmp/arty_visited_$$"
  : >"$visited_file"

  # Build and display tree
  build_reference_tree "$ARTY_CONFIG_FILE" "" 1 "$visited_file"

  # Cleanup
  rm -f "$visited_file"

  echo
}

# Display notes from arty.yml with markdown rendering
show_notes() {
  local config_file="${1:-$ARTY_CONFIG_FILE}"

  if [[ ! -f "$config_file" ]]; then
    log_error "Config file not found: $config_file"
    return 1
  fi

  # Get notes field from YAML
  local notes=$(yq eval '.notes' "$config_file" 2>/dev/null)

  if [[ -z "$notes" ]] || [[ "$notes" == "null" ]]; then
    log_info "No notes found in $config_file"
    log_info "Add a 'notes' field to your arty.yml to display project notes"
    return 0
  fi

  # Get project name for header
  local project_name=$(get_yaml_field "$config_file" "name")
  [[ "$project_name" == "null" ]] && project_name="$(basename "$PWD")"

  echo
  echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}${GREEN}  Notes: ${project_name}${NC}"
  echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo

  # Try to render markdown with available tools
  if command -v glow &>/dev/null; then
    # Use glow if available (best option)
    echo "$notes" | glow -
  elif command -v mdcat &>/dev/null; then
    # Use mdcat if available (good alternative)
    echo "$notes" | mdcat
  elif command -v python3 &>/dev/null; then
    # Fallback to Python markdown rendering - create temp file to avoid quoting issues
    local temp_notes=$(mktemp)
    echo "$notes" > "$temp_notes"
    python3 - "$temp_notes" <<'EOPY' 2>/dev/null || echo "$notes"
import sys
import re

# Read from temp file
with open(sys.argv[1], 'r') as f:
    text = f.read()

# Simple markdown-like formatting for terminal
# Code blocks first (before other processing)
lines = text.split('\n')
in_code_block = False
processed_lines = []
for line in lines:
    if line.strip().startswith('```'):
        in_code_block = not in_code_block
        if in_code_block:
            processed_lines.append('\033[2;37m' + '─' * 60 + '\033[0m')
        else:
            processed_lines.append('\033[2;37m' + '─' * 60 + '\033[0m')
        continue
    if in_code_block:
        processed_lines.append('\033[0;36m  ' + line + '\033[0m')
    else:
        processed_lines.append(line)

text = '\n'.join(processed_lines)

# Headers
text = re.sub(r'^### (.+)$', r'\033[1;36m\1\033[0m', text, flags=re.MULTILINE)
text = re.sub(r'^## (.+)$', r'\033[1;32m\1\033[0m', text, flags=re.MULTILINE)
text = re.sub(r'^# (.+)$', r'\033[1;33m\1\033[0m', text, flags=re.MULTILINE)

# Bold
text = re.sub(r'\*\*(.+?)\*\*', r'\033[1m\1\033[0m', text)
text = re.sub(r'__(.+?)__', r'\033[1m\1\033[0m', text)

# Italic (avoid matching bold)
text = re.sub(r'(?<!\*)\*([^\*]+?)\*(?!\*)', r'\033[3m\1\033[0m', text)
text = re.sub(r'(?<!_)_([^_]+?)_(?!_)', r'\033[3m\1\033[0m', text)

# Inline code (using backticks)
text = re.sub(r'`([^`]+?)`', r'\033[0;36m\1\033[0m', text)

# Links
text = re.sub(r'\[(.+?)\]\((.+?)\)', r'\033[4;34m\1\033[0m (\2)', text)

# Lists
text = re.sub(r'^- (.+)$', r'  • \1', text, flags=re.MULTILINE)
text = re.sub(r'^• (.+)$', r'  • \1', text, flags=re.MULTILINE)
text = re.sub(r'^\* (.+)$', r'  • \1', text, flags=re.MULTILINE)
text = re.sub(r'^\+ (.+)$', r'  • \1', text, flags=re.MULTILINE)

# Numbered lists
text = re.sub(r'^(\d+)\. (.+)$', r'  \1. \2', text, flags=re.MULTILINE)

print(text)
EOPY
    rm -f "$temp_notes"
  else
    # Absolute fallback: just print the raw text
    echo "$notes"
  fi

  echo
  echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo
}

# Remove a library
remove_lib() {
  local lib_name="$1"
  local lib_dir="$ARTY_LIBS_DIR/$lib_name"

  if [[ ! -d "$lib_dir" ]]; then
    log_error "Library not found: $lib_name"
    return 1
  fi

  log_info "Removing library: $lib_name"
  rm -rf "$lib_dir"
  log_success "Library removed"
}

# Initialize a new arty.yml project
init_project() {
  local project_name="${1:-$(basename "$PWD")}"

  if [[ -f "$ARTY_CONFIG_FILE" ]]; then
    log_error "arty.yml already exists in current directory"
    return 1
  fi

  log_info "Initializing new arty project: $project_name"

  # Create local .arty folder structure
  local local_arty_dir=".arty"
  local local_bin_dir="$local_arty_dir/bin"
  local local_libs_dir="$local_arty_dir/libs"

  log_info "Creating project structure"
  mkdir -p "$local_bin_dir" "$local_libs_dir"

  cat >"$ARTY_CONFIG_FILE" <<EOF
name: "$project_name"
version: "0.1.0"
description: "A bash library project"
author: ""
license: "MIT"

references:
  # - https://github.com/user/some-lib.git
  # - url: git@github.com:user/repo.git
  #   into: some/path
  #   ref: v1.0.0
EOF

  log_success "Created $ARTY_CONFIG_FILE"
  log_success "Created .arty/ folder structure"
}

# COMPLETION
# ============================================================================

_completion_bash() {
  cat << 'BASH'
# arty bash completion — source this or place in /etc/bash_completion.d/
_arty_complete() {
  local cur prev words cword
  # -n : keeps colons inside words so 'go:install'-style names are not split.
  _init_completion -n : 2>/dev/null || {
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
  }

  local script="${COMP_WORDS[0]}"

  # Discover scripts from arty.yml in the current directory at completion time
  local -a arty_scripts=()
  if [[ -f arty.yml ]] && command -v yq &>/dev/null; then
    mapfile -t arty_scripts < <(yq eval '.scripts | keys | .[]' arty.yml 2>/dev/null)
  fi

  # Discover installed libraries for the remove command
  local -a arty_libs=()
  if [[ -d .arty/libs ]]; then
    local d
    for d in .arty/libs/*/; do
      [[ -d "$d" ]] && arty_libs+=("$(basename "$d")")
    done
  fi

  local commands="install deps list ls notes scripts remove rm init completion help"
  local global_flags="--dry-run --verbose -v --help -h"

  case "${COMP_WORDS[1]}" in
    completion) COMPREPLY=( $(compgen -W "bash zsh --install" -- "$cur") ); return ;;
    remove|rm)  COMPREPLY=( $(compgen -W "${arty_libs[*]}" -- "$cur") ); return ;;
    install|deps|list|ls|notes|scripts|init) return ;;
  esac

  if [[ "$cur" == -* ]]; then
    COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") ); return
  fi

  if [[ $COMP_CWORD -eq 1 ]]; then
    COMPREPLY=( $(compgen -W "$commands ${arty_scripts[*]}" -- "$cur") )
    # Trim the portion before the last colon from each candidate so bash can
    # complete 'go:in' → 'go:install' without duplicating the prefix.
    declare -f __ltrim_colon_completions >/dev/null 2>&1 && __ltrim_colon_completions "$cur"
  fi
}

complete -F _arty_complete arty.sh
complete -F _arty_complete arty
BASH
}

_completion_zsh() {
  cat << 'ZSH'
#compdef arty.sh arty

_arty() {
  # Discover scripts from arty.yml in the current directory at completion time.
  # Fetch key + first line of value together so display shows the command.
  local -a arty_scripts=() arty_script_cmds=()
  if [[ -f arty.yml ]] && command -v yq &>/dev/null; then
    local _k _v
    while IFS=$'\t' read -r _k _v; do
      [[ -n "$_k" ]] || continue
      arty_scripts+=("$_k")
      arty_script_cmds+=("$_v")
    done < <(yq eval '.scripts | to_entries | .[] | .key + "\t" + (.value | split("\n") | .[0])' arty.yml 2>/dev/null)
  fi

  # Discover installed libraries for the remove command
  local -a arty_libs=()
  if [[ -d .arty/libs ]]; then
    local d
    for d in .arty/libs/*/; do
      [[ -d "$d" ]] && arty_libs+=("${d:t}")
    done
  fi

  local -a commands=(
    'install:Install a library from git'
    'deps:Install all dependencies from arty.yml'
    'list:List installed libraries'
    'notes:Display project notes from arty.yml'
    'scripts:List all scripts defined in arty.yml'
    'remove:Remove an installed library'
    'init:Initialize a new arty.yml project'
    'completion:Generate shell completion script'
    'help:Show help'
  )

  _arguments -C \
    '--dry-run[Simulate without making changes]' \
    '(-v --verbose)'{-v,--verbose}'[Verbose output]' \
    '(-h --help)'{-h,--help}'[Show help]' \
    '1:command:->cmd' \
    '*:args:->args' && return

  case "$state" in
    cmd)
      _describe 'command' commands
      if [[ ${#arty_scripts[@]} -gt 0 ]]; then
        local -a _arty_disp=()
        local _i
        for (( _i = 1; _i <= ${#arty_scripts[@]}; _i++ )); do
          _arty_disp+=("${arty_scripts[_i]} -- ${arty_script_cmds[_i]}")
        done
        compadd -d _arty_disp -a arty_scripts
      fi
      ;;
    args)
      case "${words[2]}" in
        remove|rm)
          _values 'library' "${arty_libs[@]}"
          ;;
        completion)
          _arguments \
            '--install[Install the completion file]' \
            '1:shell:(bash zsh)'
          ;;
      esac
      ;;
  esac
}

_arty "$@"
ZSH
}

cmd_completion() {
  local shell="" install=false

  for arg in "$@"; do
    case "$arg" in
      bash|zsh)  shell="$arg" ;;
      --install) install=true ;;
      --dry-run) ;; # already handled globally
      *) log_error "Unknown argument: $arg  (bash|zsh [--install])"; return 1 ;;
    esac
  done

  if [[ -z "$shell" ]]; then
    shell="$(basename "${SHELL:-bash}")"
    [[ "$shell" == "bash" || "$shell" == "zsh" ]] || shell="bash"
    log_warn "No shell specified, defaulting to: $shell"
  fi

  local content
  case "$shell" in
    bash) content="$(_completion_bash)" ;;
    zsh)  content="$(_completion_zsh)" ;;
    *)    log_error "Unsupported shell: $shell  (bash|zsh)"; return 1 ;;
  esac

  if ! $install; then
    echo "$content"
    return
  fi

  local dest
  case "$shell" in
    bash)
      if [[ -w /etc/bash_completion.d ]]; then
        dest="/etc/bash_completion.d/arty"
      else
        dest="${HOME}/.local/share/bash-completion/completions/arty"
        mkdir -p "$(dirname "$dest")"
      fi
      ;;
    zsh)
      dest=""
      local -a fpath_dirs=()
      while IFS=: read -ra fpath_dirs; do :; done <<< "${FPATH:-}"
      for d in "${fpath_dirs[@]}"; do
        [[ -d "$d" && -w "$d" ]] && { dest="$d/_arty"; break; }
      done
      if [[ -z "$dest" ]]; then
        dest="${HOME}/.zsh/completions/_arty"
        mkdir -p "$(dirname "$dest")"
        log_info "Add to ~/.zshrc: ${BOLD}fpath=(~/.zsh/completions \$fpath)${NC}"
        log_info "Then reload:     ${BOLD}autoload -U compinit && compinit${NC}"
      fi
      ;;
  esac

  if [[ "$ARTY_DRY_RUN" == "1" ]]; then
    log_info "[DRY RUN] Would write $shell completion to: $dest"
    return
  fi

  echo "$content" > "$dest"
  log_success "$shell completion installed: $dest"
  if [[ "$shell" == "bash" ]]; then
    log_info "Reload: source $dest"
  fi
}

# USAGE AND HELP
# ============================================================================

# Show usage
show_usage() {
  cat <<'EOF'
arty.sh - A bash library repository and release management system

USAGE:
    arty <command> [arguments] [--dry-run] [-v|--verbose]

COMMANDS:
    install <repo-url> [name]  Install a library from git repository
    deps [--dry-run]           Install all dependencies from arty.yml
    list                       List installed libraries with dependency tree
    notes                      Display project notes from arty.yml (supports markdown)
    scripts                    List all scripts defined in arty.yml
    remove <name>              Remove an installed library
    init [name]                Initialize a new arty.yml project
    completion bash|zsh        Generate shell completion script
    <script-name> [args]       Run a script defined in arty.yml scripts section

FLAGS:
    --dry-run      Simulate installation without making changes
    -v, --verbose  Enable verbose/debug output
    -h, --help     Show this help message

EXAMPLES:
    arty install https://github.com/user/bash-utils.git
    arty install https://github.com/user/lib.git my-lib
    arty deps
    arty deps --dry-run
    arty list
    arty notes
    arty scripts
    arty init my-project
    arty completion zsh --install
    arty completion bash --install
    arty npm/install
    arty rust/install

REFERENCE FORMATS:
    Simple URL:
       references:
         - https://github.com/user/repo.git

    Extended object:
       references:
         - url: git@github.com:user/repo.git
           into: custom/path   # installation directory (relative to arty.yml)
           ref: v1.0.0        # branch, tag, or commit hash
           env: production    # only install in this environment

    Multiple environments:
       references:
         - url: git@github.com:user/dev-tools.git
           env: [dev, ci]

PROJECT STRUCTURE:
    project/
       .arty/
          libs/   # cloned dependencies
       arty.yml

ENVIRONMENT:
    ARTY_HOME   Home directory for arty (default: $PWD/.arty)
    ARTY_ENV    Environment name to load from arty.yml (default: default)

EOF
}

# ============================================================================
# MAIN FUNCTION
# ============================================================================

# Main function
main() {
  # Check for yq availability first
  check_yq

  # Configuration
  PROJECT_DIR="$PWD/.arty"
  ARTY_HOME="${ARTY_HOME:-$PROJECT_DIR}"
  ARTY_LIBS_DIR="${ARTY_LIBS_DIR:-$ARTY_HOME/libs}"
  ARTY_BIN_DIR="${ARTY_BIN_DIR:-$ARTY_HOME/bin}"

  if [[ $# -eq 0 ]]; then
    show_usage
    exit 0
  fi

  # Scan flags first
  local dry_run_flag=0
  local verbose_flag=0

  for arg in "$@"; do
    case "$arg" in
      --dry-run)   dry_run_flag=1 ;;
      --verbose|-v) verbose_flag=1 ;;
    esac
  done

  if [[ "$verbose_flag" == "1" ]]; then
    export ARTY_VERBOSE=1
  fi

  load_env_vars

  if [[ "$dry_run_flag" == "1" ]]; then
    export ARTY_DRY_RUN=1
    log_info "${YELLOW}[DRY RUN MODE]${NC} Simulating actions, no changes will be made"
    echo
  fi

  # Extract command and positional args, stripping flags
  local args=()
  local command=""

  for arg in "$@"; do
    case "$arg" in
      --dry-run|--verbose|-v|-h|--help) continue ;;
      *)
        if [[ -z "$command" ]]; then
          command="$arg"
        else
          args+=("$arg")
        fi
        ;;
    esac
  done

  if [[ "$command" == "help" ]] || [[ "$command" == "--help" ]] || [[ "$command" == "-h" ]]; then
    show_usage
    exit 0
  fi

  case "$command" in
  install)
  if [[ ${#args[@]} -eq 0 ]]; then
    install_references
    local install_result=$?
    else
    install_lib "${args[@]}"
    local install_result=$?
  fi
  if [[ $install_result -eq 0 ]] && [[ -f "$ARTY_CONFIG_FILE" ]]; then
    local ref_count=$(yq eval '.references | length' "$ARTY_CONFIG_FILE" 2>/dev/null)
    if [[ -n "$ref_count" ]] && [[ "$ref_count" != "null" ]] && [[ "$ref_count" != "0" ]]; then
      echo
      log_success "Installation complete!"
      list_libs
    fi
  fi
  ;;
  deps)
  install_references
  local install_result=$?
  if [[ $install_result -eq 0 ]] && [[ -f "$ARTY_CONFIG_FILE" ]]; then
    local ref_count=$(yq eval '.references | length' "$ARTY_CONFIG_FILE" 2>/dev/null)
    if [[ -n "$ref_count" ]] && [[ "$ref_count" != "null" ]] && [[ "$ref_count" != "0" ]]; then
      echo
      log_success "Dependencies installed!"
      list_libs
    fi
  fi
  ;;
  list | ls)
  list_libs
  ;;
  notes)
  show_notes
  ;;
  scripts)
  list_scripts
  ;;
  remove | rm)
  if [[ ${#args[@]} -eq 0 ]]; then
    log_error "Library name required"
    exit 1
  fi
  remove_lib "${args[0]}"
  ;;
  init)
  init_project "${args[@]}"
  ;;
  completion)
  cmd_completion "${args[@]+"${args[@]}"}"
  ;;
  *)
  exec_script "$command" "${args[@]}"
  ;;
  esac
}

# Run main if script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  main "$@"
fi
