#!/usr/bin/env bash
# CLI for isolated per-provider install, hot-update, uninstall, and status.
set -euo pipefail

if [[ -n "${AXIOWL_INSTALLER_ROOT:-}" ]]; then
  INSTALLER="${AXIOWL_INSTALLER_ROOT}"
elif [[ -d /opt/axiowl/repo/apps/linux-desktop/installer ]]; then
  INSTALLER="/opt/axiowl/repo/apps/linux-desktop/installer"
else
  INSTALLER="/usr/share/axiowl"
fi
# shellcheck source=../lib/provider-common.sh
source "${INSTALLER}/lib/provider-common.sh"
# shellcheck source=../lib/provider-package.sh
source "${INSTALLER}/lib/provider-package.sh"

usage() {
  cat <<'EOF'
usage: axiowl-provider <command> [provider...]

commands:
  install <provider...>              Install provider package (does not reinstall axiowl core)
  update <provider...>               Hot-update to newest published provider package
  repair <provider...>                Re-apply the installed provider package worker
  uninstall <provider...>            Remove provider integration
  status [provider...]               Show discovery + package receipt status
  verify [provider...]               Verify installed manifest, payloads, and receipt
  dispatch <provider> <operation>    Dispatch one declared worker operation
  list                               List provider ids

providers:
  provider ids declared in provider-packages/inventory.json

examples:
  axiowl-provider install cursor
  axiowl-provider repair cursor
  axiowl-provider dispatch cursor verify
  axiowl-provider status
EOF
}

provider_inventory_ids() {
  local inventory="${INSTALLER}/provider-packages/inventory.json"
  if [[ ! -f "$inventory" && -f "${PROVIDER_PACKAGE_PUBLISH}/inventory.json" ]]; then
    inventory="${PROVIDER_PACKAGE_PUBLISH}/inventory.json"
  fi
  [[ -f "$inventory" ]] || {
    echo "provider inventory not found: ${inventory}" >&2
    return 1
  }
  python3 - "$inventory" <<'PY'
import json, re, sys

inventory = json.load(open(sys.argv[1], encoding="utf-8"))
providers = inventory.get("providers")
if not isinstance(providers, list):
    raise SystemExit("provider inventory providers must be an array")
seen = set()
for entry in providers:
    provider_id = entry.get("provider_id") if isinstance(entry, dict) else None
    if not isinstance(provider_id, str) or not re.fullmatch(r"[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?", provider_id):
        raise SystemExit(f"invalid provider_id in inventory: {provider_id}")
    if provider_id in seen:
        raise SystemExit(f"duplicate provider_id in inventory: {provider_id}")
    seen.add(provider_id)
print(" ".join(entry["provider_id"] for entry in providers))
PY
}

expand_providers() {
  local id
  local include_all=false
  local -a selected=()
  for id in "$@"; do
    if [[ "$id" == "all" ]]; then
      include_all=true
    else
      selected+=("$id")
    fi
  done
  if [[ "$include_all" == true ]]; then
    selected=( $(provider_inventory_ids) "${selected[@]}" )
  fi
  printf '%s\n' "${selected[@]}"
}

require_known_provider() {
  local id="$1"
  [[ "$id" == "all" ]] && return 0
  local ids
  ids="$(provider_inventory_ids)" || return 1
  [[ " $ids " == *" $id "* ]] || {
    echo "unknown provider: ${id}" >&2
    return 1
  }
}

validate_requested_providers() {
  local id
  for id in "$@"; do
    require_known_provider "$id" || return 1
  done
}

require_provider_store_write() {
  [[ "$(id -u)" -eq 0 ]] && return 0
  local writable_root="$PROVIDER_PACKAGE_STORE"
  if [[ ! -e "$writable_root" ]]; then
    writable_root="$(dirname "$writable_root")"
  fi
  if [[ -w "$writable_root" ]]; then
    return 0
  fi
  local desktop_user="${AXIOWL_DESKTOP_USER:-${USER:-}}"
  printf 'provider package operations require root; the store is not writable: %s\n' \
    "$PROVIDER_PACKAGE_STORE" >&2
  printf 'rerun with: sudo env AXIOWL_DESKTOP_USER=%q /usr/bin/axiowl-provider' \
    "$desktop_user" >&2
  printf ' %q' "$cmd" "$@" >&2
  printf '\n' >&2
  return 1
}

# Run one operation for every selected provider.  Provider workers are
# intentionally independent: a failure for one provider must be reported but
# must not prevent the remaining inventory entries from being attempted.
# Keep the caller's set -e policy for validation and return one aggregate
# failure after the complete batch has been processed.
provider_batch_dispatch() {
  local operation="$1"
  local id="$2"
  case "$operation" in
    install)
      if [[ -f "${PROVIDER_PACKAGE_STORE}/${id}/active.json" ]]; then
        provider_hot_update "$id"
      else
        provider_hot_install "$id"
      fi
      ;;
    update)
      provider_hot_update "$id"
      ;;
    repair)
      provider_hot_repair "$id"
      ;;
    uninstall)
      provider_hot_uninstall "$id"
      ;;
    verify)
      provider_hot_verify "$id"
      ;;
    status)
      local failed=0 package_version
      if ! provider_status "$id"; then
        failed=1
      fi
      if package_version="$(provider_active_package_version "$id")"; then
        echo "  package_version=${package_version}"
      else
        failed=1
        echo "  package_version=-"
      fi
      # Receipt status is informational and historically does not make the
      # status command fail on its own.
      provider_hot_status "$id" | sed 's/^/  receipt=/' || true
      return "$failed"
      ;;
    *)
      echo "unsupported provider batch operation: ${operation}" >&2
      return 2
      ;;
  esac
}

provider_batch_run() {
  local operation="$1"
  shift
  local providers
  providers="$(expand_providers "$@")" || return 1

  local failed=0 id rc
  while IFS= read -r id; do
    [[ -n "$id" ]] || continue
    if provider_batch_dispatch "$operation" "$id"; then
      :
    else
      rc=$?
      failed=1
      printf 'provider %s failed: %s (exit %d)\n' "$operation" "$id" "$rc" >&2
    fi
  done <<< "$providers"
  return "$failed"
}

cmd="${1:-}"
shift || true

case "$cmd" in
  list)
    provider_inventory_ids
    ;;
  install)
    [[ $# -gt 0 ]] || { usage >&2; exit 2; }
    validate_requested_providers "$@"
    require_provider_store_write "$@" || exit 1
    provider_batch_run install "$@"
    ;;
  update)
    [[ $# -gt 0 ]] || { usage >&2; exit 2; }
    validate_requested_providers "$@"
    require_provider_store_write "$@" || exit 1
    provider_batch_run update "$@"
    ;;
  repair)
    [[ $# -gt 0 ]] || { usage >&2; exit 2; }
    validate_requested_providers "$@"
    require_provider_store_write "$@" || exit 1
    provider_batch_run repair "$@"
    ;;
  dispatch)
    [[ $# -eq 2 ]] || { usage >&2; exit 2; }
    validate_requested_providers "$1"
    require_provider_store_write "$1" || exit 1
    provider_hot_dispatch "$1" "$2"
    ;;
  uninstall)
    [[ $# -gt 0 ]] || { usage >&2; exit 2; }
    validate_requested_providers "$@"
    require_provider_store_write "$@" || exit 1
    provider_batch_run uninstall "$@"
    ;;
  status)
    if [[ $# -eq 0 ]]; then
      set -- $(expand_providers all)
    fi
    validate_requested_providers "$@"
    provider_batch_run status "$@"
    ;;
  verify)
    if [[ $# -eq 0 ]]; then
      set -- $(expand_providers all)
    fi
    validate_requested_providers "$@"
    provider_batch_run verify "$@"
    ;;
  -h|--help|help|"")
    usage
    ;;
  *)
    echo "unknown command: $cmd" >&2
    usage >&2
    exit 2
    ;;
esac
