#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# Hermes CMS controller
#
# Installs and manages the LoongSuite Hermes Agent observability plugin without
# changing Hermes source code. The bootstrap switch is controlled through
# ~/.loongsuite/bootstrap-config.json:
#
#   install   -> install packages + write OTLP config + default bootstrap OFF
#   enable    -> set LOONGSUITE_PYTHON_SITE_BOOTSTRAP=true in config
#   disable   -> set LOONGSUITE_PYTHON_SITE_BOOTSTRAP=false in config
#   status    -> inspect package/config/bootstrap status
#   uninstall -> remove plugin package and managed config keys
#
# ---------------------------------------------------------------------------
set -euo pipefail

PLUGIN_NAME="hermes-agent-cms-plugin"
CONTROLLER_NAME="hermes-cms"
CONTROLLER_HOME="${HOME}/.loongsuite/bin"
CONTROLLER_PATH="${CONTROLLER_HOME}/${CONTROLLER_NAME}"
USER_SHIM_HOME="${HOME}/.local/bin"
USER_SHIM_PATH="${USER_SHIM_HOME}/${CONTROLLER_NAME}"
TEMP_DIRS_TO_CLEANUP=""

DEFAULT_SCRIPT_DIR=""
if [[ -n "${BASH_SOURCE[0]:-}" ]] && [[ "${BASH_SOURCE[0]}" != "bash" ]]; then
  DEFAULT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
fi
if [[ -z "${DEFAULT_SCRIPT_DIR}" ]]; then
  DEFAULT_SCRIPT_DIR="$(pwd)"
fi

DEFAULT_INSTRUMENTATION_URL="https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/hermes-agent-cms-plugin/loongsuite_instrumentation_hermes_agent-0-py3-none-any.whl"
VERSIONED_INSTRUMENTATION_URL_TEMPLATE="https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/hermes-agent-cms-plugin/%s/loongsuite_instrumentation_hermes_agent-%s-py3-none-any.whl"
DEFAULT_CONTROLLER_URL="https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/hermes-agent-cms-plugin/hermes-cms.sh"

BOOTSTRAP_SWITCH_KEY="LOONGSUITE_PYTHON_SITE_BOOTSTRAP"

ENDPOINT=""
LICENSE_KEY=""
ARMS_PROJECT=""
CMS_WORKSPACE=""
SERVICE_NAME=""
EXPORTED_PYTHON="${PYTHON:-}"
CLI_PYTHON=""
BOOTSTRAP_CONFIG=""
OTLP_PROTOCOL=""
INSTRUMENTATION_URL="${DEFAULT_INSTRUMENTATION_URL}"
INSTRUMENTATION_VERSION=""
INSTRUMENTATION_URL_EXPLICIT=false
CONTROLLER_URL="${DEFAULT_CONTROLLER_URL}"
NO_CHAT_MESSAGES=false
SKIP_HERMES_CHECK=false
PURGE_SHARED=false
ACTION="install"

BASE_PKGS=(
  "loongsuite-site-bootstrap"
  "loongsuite-distro"
  "opentelemetry-exporter-otlp"
)
INSTRUMENTATION_PKG="loongsuite-instrumentation-hermes-agent"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BLACK='\033[0;30m'
NC='\033[0m'

info()  { echo -e "${CYAN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
ok()    { echo -e "${GREEN}[OK]${NC}    $*"; }

current_script_path() {
  local source_path="${BASH_SOURCE[0]:-}"
  if [[ -n "${source_path}" && -r "${source_path}" ]]; then
    printf '%s\n' "${source_path}"
    return 0
  fi
  return 1
}

usage() {
  cat <<'EOF'
Hermes CMS controller

Usage:
  hermes-cms.sh install [version] [options]
  hermes-cms.sh enable [options]
  hermes-cms.sh disable [options]
  hermes-cms.sh status [options]
  hermes-cms.sh uninstall [options]

When the first argument starts with "--", "install" is assumed for
backward-friendly curl | bash usage.

Install options:
  --endpoint URL
  --x-arms-license-key VALUE
  --x-arms-project VALUE
  --x-cms-workspace VALUE
  --serviceName VALUE

Shared options:
  --python PATH
  --bootstrap-config PATH
  --instrumentation-url URL
  --controller-url URL
  --otlp-protocol VALUE
  --no-chat-messages
  --skip-hermes-check
  --purge-shared        uninstall only: also remove shared bootstrap packages
  -h, --help

Examples:
  curl -fsSL https://.../hermes-cms.sh | bash -s -- install \
    --x-arms-license-key "auto" \
    --x-arms-project "" \
    --x-cms-workspace "default-cms-xxx" \
    --serviceName "hermes" \
    --endpoint "https://.../apm/trace/opentelemetry"

  curl -fsSL https://.../hermes-cms.sh | bash -s -- install 0.1.1 ...
  hermes-cms enable
  hermes-cms disable
  hermes-cms status
EOF
}

cleanup_temp_dirs() {
  local dir
  for dir in ${TEMP_DIRS_TO_CLEANUP}; do
    [[ -n "${dir}" ]] || continue
    rm -rf "${dir}" 2>/dev/null || true
  done
}

trap cleanup_temp_dirs EXIT

resolve_hermes_python_from_path() {
  local hermes_cmd
  hermes_cmd="$(command -v hermes || true)"
  if [[ -z "${hermes_cmd}" || ! -f "${hermes_cmd}" ]]; then
    return 1
  fi

  python3 - "${hermes_cmd}" <<'PY'
import pathlib
import shlex
import sys

hermes_path = pathlib.Path(sys.argv[1])


def read_text(path):
    try:
        return path.read_text(encoding="utf-8", errors="ignore")
    except Exception:
        return ""


def python_from_entrypoint(path, seen=None):
    if seen is None:
        seen = set()
    path = pathlib.Path(path)
    if path in seen or not path.is_file():
        return None
    seen.add(path)

    text = read_text(path)
    lines = text.splitlines()
    if not lines:
        return None

    first = lines[0]
    if first.startswith("#!"):
        try:
            shebang = shlex.split(first[2:].strip())
        except ValueError:
            shebang = []
        if shebang:
            candidate = pathlib.Path(shebang[0])
            if candidate.name == "env" and len(shebang) >= 2:
                candidate = pathlib.Path(shebang[1])
            if "python" in candidate.name and candidate.exists():
                # Keep the venv entry path itself. Resolving here can collapse
                # /path/to/venv/bin/python3 into the base interpreter symlink target,
                # which loses the virtualenv site-packages and breaks Hermes imports.
                return candidate

    # Current upstream install.sh writes ~/.local/bin/hermes as a bash launcher
    # that execs the real venv entrypoint after clearing Python env vars.
    for line in lines:
        stripped = line.strip()
        if not stripped.startswith("exec "):
            continue
        try:
            parts = shlex.split(stripped)
        except ValueError:
            continue
        if len(parts) < 2 or "$" in parts[1]:
            continue
        target = pathlib.Path(parts[1])
        if not target.is_absolute():
            target = path.parent / target
        found = python_from_entrypoint(target, seen)
        if found is not None:
            return found

    return None


candidate = python_from_entrypoint(hermes_path)
if candidate is not None:
    print(candidate)
    raise SystemExit(0)

raise SystemExit(1)
PY
}

select_python() {
  local selected_from=""
  local detected_hermes_python=""
  if detected_hermes_python="$(resolve_hermes_python_from_path 2>/dev/null)"; then
    :
  else
    detected_hermes_python=""
  fi

  if [[ -n "${CLI_PYTHON}" ]]; then
    PYTHON="${CLI_PYTHON}"
    selected_from="--python"
  elif [[ -n "${VIRTUAL_ENV:-}" && -x "${VIRTUAL_ENV}/bin/python" ]]; then
    PYTHON="${VIRTUAL_ENV}/bin/python"
    selected_from="VIRTUAL_ENV"
  elif [[ -n "${CONDA_PREFIX:-}" && -x "${CONDA_PREFIX}/bin/python" ]]; then
    PYTHON="${CONDA_PREFIX}/bin/python"
    selected_from="CONDA_PREFIX"
  elif [[ -n "${detected_hermes_python}" ]]; then
    PYTHON="${detected_hermes_python}"
    selected_from="hermes"
  elif [[ -n "${EXPORTED_PYTHON}" ]]; then
    PYTHON="${EXPORTED_PYTHON}"
    selected_from="PYTHON"
  else
    PYTHON="python3"
    selected_from="python3"
  fi

  if [[ -z "${BOOTSTRAP_CONFIG}" ]]; then
    BOOTSTRAP_CONFIG="${HOME}/.loongsuite/bootstrap-config.json"
  fi

  PYTHON_SELECTED_FROM="${selected_from}"
  HERMES_DETECTED_PYTHON="${detected_hermes_python}"
}

run_target_python() {
  LOONGSUITE_PYTHON_SITE_BOOTSTRAP=false "${PYTHON}" "$@"
}

run_target_pip() {
  LOONGSUITE_PYTHON_SITE_BOOTSTRAP=false "${PYTHON}" -m pip "$@"
}

run_target_pip_quiet() {
  local log_file
  log_file="$(mktemp)"
  if run_target_pip "$@" >"${log_file}" 2>&1; then
    rm -f "${log_file}"
    return 0
  fi

  error "pip command failed: pip $*"
  cat "${log_file}" >&2 || true
  rm -f "${log_file}"
  return 1
}

is_virtualenv_python() {
  run_target_python - <<'PY'
import pathlib
import sys

exe = pathlib.Path(sys.executable).resolve()
prefix = pathlib.Path(sys.prefix).resolve()
base_prefix = pathlib.Path(getattr(sys, "base_prefix", sys.prefix)).resolve()

if prefix != base_prefix:
    raise SystemExit(0)

for candidate in (exe.parent.parent / "pyvenv.cfg", prefix / "pyvenv.cfg"):
    if candidate.exists():
        raise SystemExit(0)

raise SystemExit(1)
PY
}

is_uv_virtualenv_python() {
  run_target_python - <<'PY'
import pathlib
import sys

exe = pathlib.Path(sys.executable).resolve()
prefix = pathlib.Path(sys.prefix).resolve()

for candidate in (exe.parent.parent / "pyvenv.cfg", prefix / "pyvenv.cfg"):
    if candidate.exists():
        text = candidate.read_text(encoding="utf-8", errors="ignore")
        if "\nuv = " in "\n" + text:
            raise SystemExit(0)

raise SystemExit(1)
PY
}

target_conda_prefix() {
  run_target_python - <<'PY'
import pathlib
import sys

prefix = pathlib.Path(sys.prefix).resolve()
if (prefix / "conda-meta").is_dir():
    print(prefix)
    raise SystemExit(0)

raise SystemExit(1)
PY
}

ensure_target_pip() {
  if run_target_pip --version &>/dev/null; then
    return 0
  fi

  warn "pip is not available in the selected Python environment."

  if ! is_virtualenv_python; then
    local conda_prefix
    if conda_prefix="$(target_conda_prefix 2>/dev/null)" && command -v conda >/dev/null 2>&1; then
      info "Attempting to install pip into conda environment: ${conda_prefix}"
      if conda install -y -p "${conda_prefix}" --force-reinstall pip >/dev/null 2>&1 && run_target_pip --version &>/dev/null; then
        ok "pip installed with conda"
        return 0
      fi
    fi
    warn "Selected Python is not a virtual environment or supported conda environment; refusing to auto-bootstrap pip."
    return 1
  fi

  info "Attempting to bootstrap pip into the selected virtual environment..."
  if run_target_python -m ensurepip --upgrade >/dev/null 2>&1 && run_target_pip --version &>/dev/null; then
    ok "pip bootstrapped with ensurepip"
    return 0
  fi

  if command -v uv >/dev/null 2>&1; then
    if is_uv_virtualenv_python; then
      info "ensurepip was unavailable; attempting uv-native pip bootstrap into ${PYTHON}..."
    else
      info "ensurepip was unavailable; attempting uv pip bootstrap into ${PYTHON}..."
    fi
    if uv pip install --python "${PYTHON}" -U pip setuptools wheel >/dev/null 2>&1 && run_target_pip --version &>/dev/null; then
      ok "pip bootstrapped with uv"
      return 0
    fi
  fi

  return 1
}

ensure_selected_python_ready() {
  if [[ "${PYTHON}" == */* ]]; then
    if [[ ! -x "${PYTHON}" ]]; then
      error "Python not found: ${PYTHON}"
      exit 1
    fi
  elif ! command -v "${PYTHON}" &>/dev/null; then
    error "Python not found: ${PYTHON}"
    exit 1
  fi

  if ! run_target_python -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)" 2>/dev/null; then
    _py_ver="$(run_target_python -c "import sys; print('%d.%d.%d' % sys.version_info[:3])")"
    error "Python >= 3.10 is required (current: ${_py_ver})"
    exit 1
  fi

  _py_ver="$(run_target_python -c "import sys; print('%d.%d.%d' % sys.version_info[:3])")"
  ok "Python ${_py_ver}"
}

ensure_selected_python_pip() {
  if ! ensure_target_pip; then
    error "pip is not available for ${PYTHON}, and automatic bootstrap failed."
    exit 1
  fi
  ok "pip $(run_target_pip --version | awk '{print $2}')"
}

ensure_python_ready() {
  select_python

  info "Using Python: ${PYTHON}"
  if [[ "${PYTHON_SELECTED_FROM:-}" == "VIRTUAL_ENV" ]]; then
    warn "Detected active virtual environment: ${VIRTUAL_ENV}"
    warn "The script will verify whether this virtualenv actually contains Hermes before continuing."
  fi
  if [[ "${PYTHON_SELECTED_FROM:-}" == "CONDA_PREFIX" ]]; then
    warn "Detected active conda environment: ${CONDA_DEFAULT_ENV:-unknown} (${CONDA_PREFIX})"
    warn "The script will verify whether this conda Python actually contains Hermes before continuing."
  fi
  if [[ "${PYTHON_SELECTED_FROM:-}" == "PYTHON" ]]; then
    warn "Selected Python came from the PYTHON environment variable."
    warn "If this is not your Hermes environment, pass --python explicitly or activate the target env first."
  fi
  ensure_selected_python_ready
}

check_hermes_runtime() {
  if [[ "${SKIP_HERMES_CHECK}" == true ]]; then
    warn "Skipping Hermes environment check (--skip-hermes-check)."
    return
  fi

  info "Checking Hermes runtime in the selected Python environment..."
  if ! run_target_python -c "import run_agent, hermes_cli.main" >/dev/null 2>&1; then
    if [[ -n "${HERMES_DETECTED_PYTHON:-}" && "${PYTHON}" != "${HERMES_DETECTED_PYTHON}" ]]; then
      warn "The currently selected Python does not contain Hermes modules."
      if [[ "${PYTHON_SELECTED_FROM:-}" == "VIRTUAL_ENV" ]]; then
        warn "Your shell is in virtual environment '${VIRTUAL_ENV}', but Hermes is installed in a different Python environment."
      fi
      if [[ "${PYTHON_SELECTED_FROM:-}" == "CONDA_PREFIX" ]]; then
        warn "Your shell is in conda environment '${CONDA_DEFAULT_ENV:-unknown}', but Hermes is installed in a different Python environment."
      fi
      warn "Automatically switching to Hermes Python: ${HERMES_DETECTED_PYTHON}"
      PYTHON="${HERMES_DETECTED_PYTHON}"
      PYTHON_SELECTED_FROM="hermes"
      if ! run_target_python -c "import run_agent, hermes_cli.main" >/dev/null 2>&1; then
        error "The detected Hermes Python environment still cannot import Hermes modules."
        error "Please pass --python explicitly if you want to target a different environment."
        exit 1
      fi
      ok "Hermes modules import successfully after switching to ${PYTHON}"
      ensure_selected_python_ready
      return
    fi
    error "The selected Python environment cannot import Hermes modules."
    error "Please activate the Hermes Python environment first, or pass --python explicitly."
    exit 1
  fi
  ok "Hermes modules import successfully in ${PYTHON}"
}

verify_instrumentation_artifact() {
  if [[ -z "${INSTRUMENTATION_URL}" ]]; then
    error "Missing Hermes instrumentation artifact URL."
    exit 1
  fi

  info "Using instrumentation artifact: ${INSTRUMENTATION_URL}"
  if [[ "${INSTRUMENTATION_URL}" == file://* ]]; then
    local artifact_path="${INSTRUMENTATION_URL#file://}"
    if [[ ! -f "${artifact_path}" ]]; then
      error "Instrumentation artifact file does not exist: ${artifact_path}"
      exit 1
    fi
    ok "Instrumentation artifact found at ${artifact_path}"
    return
  fi

  if [[ -f "${INSTRUMENTATION_URL}" ]]; then
    ok "Instrumentation artifact found at ${INSTRUMENTATION_URL}"
    return
  fi

  local artifact_http_code
  artifact_http_code="$(curl -o /dev/null -sS -L -w "%{http_code}" "${INSTRUMENTATION_URL}" -m 15 2>/dev/null || echo "000")"
  if [[ "${artifact_http_code}" == "000" ]]; then
    error "Instrumentation artifact is unreachable (HTTP code: 000): ${INSTRUMENTATION_URL}"
    exit 1
  fi
  ok "Instrumentation artifact reachable (HTTP ${artifact_http_code})"
}

resolve_instrumentation_url() {
  if [[ "${INSTRUMENTATION_URL_EXPLICIT}" == true ]]; then
    return
  fi

  if [[ -n "${INSTRUMENTATION_VERSION}" ]]; then
    printf -v INSTRUMENTATION_URL "${VERSIONED_INSTRUMENTATION_URL_TEMPLATE}" "${INSTRUMENTATION_VERSION}" "${INSTRUMENTATION_VERSION}"
  else
    INSTRUMENTATION_URL="${DEFAULT_INSTRUMENTATION_URL}"
  fi
}

prepare_instrumentation_install_target() {
  INSTALL_TARGET="${INSTRUMENTATION_URL}"
}

infer_otlp_protocol() {
  local ep="$1"
  if [[ -n "${OTLP_PROTOCOL}" ]]; then
    echo "${OTLP_PROTOCOL}"
    return
  fi
  if [[ "${ep}" =~ :4317(/|$) ]]; then
    echo "grpc"
  else
    echo "http/protobuf"
  fi
}

check_endpoint_connectivity() {
  info "Checking endpoint connectivity: ${ENDPOINT}"
  local endpoint_http_code
  endpoint_http_code="$(curl -o /dev/null -sS -w "%{http_code}" "${ENDPOINT}" -m 10 2>/dev/null || echo "000")"
  if [[ "${endpoint_http_code}" == "000" ]]; then
    error "Endpoint is unreachable (HTTP code: 000). Please check network connectivity."
    exit 1
  fi
  ok "Endpoint reachable (HTTP ${endpoint_http_code})"
}

verify_installation() {
  info "Verifying installed bootstrap and instrumentor registration..."
  if ! run_target_python - <<'PY'
import importlib
import importlib.metadata as metadata

required = {
    "loongsuite-site-bootstrap": False,
    "loongsuite-distro": False,
    "loongsuite-instrumentation-hermes-agent": False,
}

for dist in list(required):
    try:
        metadata.version(dist)
        required[dist] = True
    except metadata.PackageNotFoundError:
        required[dist] = False

registered = False
for ep in metadata.entry_points(group="opentelemetry_instrumentor"):
    if ep.name == "hermes-agent":
        registered = True
        break

try:
    importlib.import_module("opentelemetry.instrumentation.hermes_agent")
    imported = True
except Exception:
    imported = False

missing = [name for name, ok in required.items() if not ok]
if not registered:
    missing.append("opentelemetry_instrumentor:hermes-agent")
if not imported:
    missing.append("import opentelemetry.instrumentation.hermes_agent")

if missing:
    print("Verification failed: " + ", ".join(missing))
    raise SystemExit(1)
PY
  then
    error "Post-install verification failed. The Hermes instrumentor may not be registered in this environment."
    exit 1
  fi
  ok "Hermes instrumentor registration verified"
}


install_local_controller() {
  local source_path

  mkdir -p "${CONTROLLER_HOME}" "${USER_SHIM_HOME}"
  if source_path="$(current_script_path)"; then
    cat "${source_path}" > "${CONTROLLER_PATH}"
  else
    info "Current script path is not available; downloading controller from ${CONTROLLER_URL}"
    curl -fsSL "${CONTROLLER_URL}" -o "${CONTROLLER_PATH}"
  fi
  chmod +x "${CONTROLLER_PATH}"

  cat > "${USER_SHIM_PATH}" <<EOF
#!/usr/bin/env bash
exec "${CONTROLLER_PATH}" "\$@"
EOF
  chmod +x "${USER_SHIM_PATH}"

  ok "Installed local controller: ${CONTROLLER_PATH}"
  ok "Installed user command shim: ${USER_SHIM_PATH}"

  case ":${PATH}:" in
    *":${USER_SHIM_HOME}:"*)
      ;;
    *)
      warn "${USER_SHIM_HOME} is not currently in PATH."
      warn "Add it to your shell profile if you want to run '${CONTROLLER_NAME}' directly."
      ;;
  esac
}

remove_local_controller() {
  local removed=false
  if [[ -e "${USER_SHIM_PATH}" ]]; then
    rm -f "${USER_SHIM_PATH}"
    removed=true
  fi
  if [[ -e "${CONTROLLER_PATH}" ]]; then
    rm -f "${CONTROLLER_PATH}"
    removed=true
  fi
  if [[ "${removed}" == true ]]; then
    ok "Removed local controller command"
  else
    info "Local controller command was not installed"
  fi
}

write_install_config() {
  local protocol="$1"
  local cfg_out
  cfg_out="$(run_target_python - "${BOOTSTRAP_CONFIG}" "${ENDPOINT}" "${LICENSE_KEY}" "${ARMS_PROJECT}" "${CMS_WORKSPACE}" "${SERVICE_NAME}" "${protocol}" "${NO_CHAT_MESSAGES}" "${BOOTSTRAP_SWITCH_KEY}" <<'PY'
import json
import sys
from pathlib import Path
from urllib.parse import quote

cfg_path = Path(sys.argv[1])
endpoint = sys.argv[2]
license_key = sys.argv[3]
arms_project = sys.argv[4]
cms_workspace = sys.argv[5]
service_name = sys.argv[6]
protocol = sys.argv[7]
no_chat_messages = sys.argv[8].lower() in ("true", "1", "yes")
bootstrap_key = sys.argv[9]

def enc(value: str) -> str:
    return quote(value, safe="@-_.~")

headers = ",".join(
    [
        f"x-arms-license-key={enc(license_key)}",
        f"x-arms-project={enc(arms_project)}",
        f"x-cms-workspace={enc(cms_workspace)}",
    ]
)

otel_resource_attributes = (
    f"service.name={service_name},"
    f"acs.cms.workspace={cms_workspace},"
    f"acs.arms.service.feature=genai_app,"
    f"gen_ai.instrumentation.sdk.name=loongsuite-genai-utils,"
    f"gen_ai.agent.system=hermes-agent"
)

cfg_path.parent.mkdir(parents=True, exist_ok=True)

data = {}
if cfg_path.exists():
    try:
        loaded = json.loads(cfg_path.read_text(encoding="utf-8"))
        if isinstance(loaded, dict):
            data = loaded
    except Exception:
        data = {}

managed = {
    "OTEL_SERVICE_NAME": service_name,
    "OTEL_RESOURCE_ATTRIBUTES": otel_resource_attributes,
    "OTEL_EXPORTER_OTLP_ENDPOINT": endpoint,
    "OTEL_EXPORTER_OTLP_PROTOCOL": protocol,
    "OTEL_TRACES_EXPORTER": "otlp",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_HEADERS": headers,
    bootstrap_key: "false",
}
if not no_chat_messages:
    managed["OTEL_SEMCONV_STABILITY_OPT_IN"] = "gen_ai_latest_experimental"
    managed["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "SPAN_ONLY"

data.update(managed)
if no_chat_messages:
    data.pop("OTEL_SEMCONV_STABILITY_OPT_IN", None)
    data.pop("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", None)

cfg_path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
print(str(cfg_path))
PY
)"
  ok "Bootstrap config updated: ${cfg_out}"
}

ensure_install_config_ready() {
  if ! run_target_python - "${BOOTSTRAP_CONFIG}" <<'PY'
import json
import sys
from pathlib import Path

cfg_path = Path(sys.argv[1])
if not cfg_path.exists():
    raise SystemExit(1)

data = json.loads(cfg_path.read_text(encoding="utf-8"))
required = [
    "OTEL_SERVICE_NAME",
    "OTEL_EXPORTER_OTLP_ENDPOINT",
    "OTEL_EXPORTER_OTLP_HEADERS",
]
missing = [key for key in required if not data.get(key)]
if missing:
    print(", ".join(missing))
    raise SystemExit(2)
PY
  then
    error "Managed bootstrap config is missing or incomplete: ${BOOTSTRAP_CONFIG}"
    error "Run '${CONTROLLER_NAME}.sh install ...' first."
    exit 1
  fi
}

set_bootstrap_switch() {
  local desired="$1"
  ensure_install_config_ready
  run_target_python - "${BOOTSTRAP_CONFIG}" "${BOOTSTRAP_SWITCH_KEY}" "${desired}" <<'PY'
import json
import sys
from pathlib import Path

cfg_path = Path(sys.argv[1])
bootstrap_key = sys.argv[2]
desired = sys.argv[3]

data = json.loads(cfg_path.read_text(encoding="utf-8"))
data[bootstrap_key] = desired
cfg_path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
PY
}

cleanup_managed_config() {
  if [[ ! -f "${BOOTSTRAP_CONFIG}" ]]; then
    info "Managed bootstrap config not found: ${BOOTSTRAP_CONFIG}"
    return
  fi

  run_target_python - "${BOOTSTRAP_CONFIG}" "${BOOTSTRAP_SWITCH_KEY}" <<'PY'
import json
import sys
from pathlib import Path

cfg_path = Path(sys.argv[1])
bootstrap_key = sys.argv[2]

managed_keys = [
    "OTEL_SERVICE_NAME",
    "OTEL_RESOURCE_ATTRIBUTES",
    "OTEL_EXPORTER_OTLP_ENDPOINT",
    "OTEL_EXPORTER_OTLP_PROTOCOL",
    "OTEL_TRACES_EXPORTER",
    "OTEL_METRICS_EXPORTER",
    "OTEL_EXPORTER_OTLP_HEADERS",
    "OTEL_SEMCONV_STABILITY_OPT_IN",
    "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
    bootstrap_key,
]

try:
    data = json.loads(cfg_path.read_text(encoding="utf-8"))
except Exception:
    raise SystemExit(0)

if not isinstance(data, dict):
    raise SystemExit(0)

for key in managed_keys:
    data.pop(key, None)

cfg_path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
PY
  ok "Managed config keys removed from ${BOOTSTRAP_CONFIG}"
}

print_status() {
  ensure_python_ready
  local status_json
  status_json="$(run_target_python - "${BOOTSTRAP_CONFIG}" "${BOOTSTRAP_SWITCH_KEY}" "${CONTROLLER_PATH}" "${USER_SHIM_PATH}" <<'PY'
import json
import sys
import sysconfig
from importlib import metadata
from pathlib import Path

cfg_path = Path(sys.argv[1])
bootstrap_key = sys.argv[2]
controller_path = Path(sys.argv[3])
shim_path = Path(sys.argv[4])
purelib = Path(sysconfig.get_path("purelib"))

packages = {}
for name in (
    "loongsuite-site-bootstrap",
    "loongsuite-distro",
    "loongsuite-instrumentation-hermes-agent",
    "opentelemetry-exporter-otlp",
):
    try:
        packages[name] = metadata.version(name)
    except metadata.PackageNotFoundError:
        packages[name] = None

instrumentors = []
for ep in sorted(metadata.entry_points(group="opentelemetry_instrumentor"), key=lambda e: e.name):
    instrumentors.append(ep.name)

config_exists = cfg_path.exists()
config = {}
if config_exists:
    try:
        loaded = json.loads(cfg_path.read_text(encoding="utf-8"))
        if isinstance(loaded, dict):
            config = loaded
    except Exception:
        config = {"_invalid": True}

print(json.dumps({
    "python": sys.executable,
    "purelib": str(purelib),
    "config_path": str(cfg_path),
    "config_exists": config_exists,
    "config_valid": isinstance(config, dict) and "_invalid" not in config,
    "bootstrap_switch": config.get(bootstrap_key),
    "service_name": config.get("OTEL_SERVICE_NAME"),
    "endpoint": config.get("OTEL_EXPORTER_OTLP_ENDPOINT"),
    "controller_installed": controller_path.exists(),
    "controller_path": str(controller_path),
    "shim_installed": shim_path.exists(),
    "shim_path": str(shim_path),
    "packages": packages,
    "instrumentors": instrumentors,
}, ensure_ascii=False))
PY
)"

  run_target_python - "${status_json}" <<'PY'
import json
import sys

data = json.loads(sys.argv[1])
print(f"Python:          {data['python']}")
print(f"Site-packages:   {data['purelib']}")
print(f"Config file:     {data['config_path']}")
print(f"Config exists:   {'yes' if data['config_exists'] else 'no'}")
print(f"Config valid:    {'yes' if data['config_valid'] else 'no'}")
print(f"Bootstrap:       {data['bootstrap_switch']!r}")
print(f"Service name:    {data['service_name']!r}")
print(f"OTLP endpoint:   {data['endpoint']!r}")
print(f"Controller:      {'installed' if data['controller_installed'] else 'absent'} ({data['controller_path']})")
print(f"Command shim:    {'installed' if data['shim_installed'] else 'absent'} ({data['shim_path']})")
print("Packages:")
for name, version in data["packages"].items():
    print(f"  {name} = {version or 'MISSING'}")
print("Instrumentors:")
for name in data["instrumentors"]:
    print(f"  {name}")
PY
}

perform_install() {
  local missing=()
  [[ -z "${ENDPOINT}" ]]      && missing+=("--endpoint")
  [[ -z "${LICENSE_KEY}" ]]   && missing+=("--x-arms-license-key")
  [[ -z "${ARMS_PROJECT}" ]]  && missing+=("--x-arms-project")
  [[ -z "${CMS_WORKSPACE}" ]] && missing+=("--x-cms-workspace")
  [[ -z "${SERVICE_NAME}" ]]  && missing+=("--serviceName")

  if [[ ${#missing[@]} -gt 0 ]]; then
    error "Missing required parameters: ${missing[*]}"
    exit 1
  fi

  local protocol
  protocol="$(infer_otlp_protocol "${ENDPOINT}")"

  ensure_python_ready
  check_hermes_runtime
  ensure_selected_python_pip
  resolve_instrumentation_url
  verify_instrumentation_artifact
  prepare_instrumentation_install_target
  check_endpoint_connectivity

  info "Installing shared LoongSuite packages..."
  if ! run_target_pip_quiet install -U "${BASE_PKGS[@]}"; then
    error "Failed to install shared LoongSuite packages."
    exit 1
  fi
  ok "Shared packages installed"

  info "Installing Hermes instrumentation..."
  if ! run_target_pip_quiet install -U "${INSTALL_TARGET}"; then
    error "Failed to install Hermes instrumentation artifact: ${INSTRUMENTATION_URL}"
    exit 1
  fi
  ok "Hermes instrumentation installed"

  verify_installation
  info "Installing local controller command..."
  install_local_controller || true

  info "Writing bootstrap config: ${BOOTSTRAP_CONFIG}"
  write_install_config "${protocol}"

  echo ""
  echo -e "${BLACK}════════════════════════════════════════════════════${NC}"
  echo -e "${GREEN}  ✅ ${PLUGIN_NAME} installed successfully!${NC}"
  echo -e "${BLACK}════════════════════════════════════════════════════${NC}"
  echo ""
  echo "  Config file:     ${BOOTSTRAP_CONFIG}"
  echo "  OTLP endpoint:   ${ENDPOINT}"
  echo "  OTLP protocol:   ${protocol}"
  echo "  Service name:    ${SERVICE_NAME}"
  echo "  Python:          $(run_target_python -c 'import sys; print(sys.executable)')"
  echo "  Artifact install:  ${INSTRUMENTATION_URL}"
  echo "  Controller:      ${USER_SHIM_PATH}"
  echo "  Site-bootstrap:  OFF in config (default)"
  if [[ "${NO_CHAT_MESSAGES}" == true ]]; then
    echo "  Gen-ai messages: OFF in config (--no-chat-messages)"
  else
    echo "  Gen-ai messages: ON in config"
  fi
  echo ""
  echo -e "${CYAN}Next steps:${NC}"
  echo "  1. Enable auto-bootstrap when you want the current user default ON:"
  echo "       ${CONTROLLER_NAME} enable${CLI_PYTHON:+ --python ${CLI_PYTHON}}"
  echo "  2. Or keep it OFF and export manually per shell/process:"
  echo "       export ${BOOTSTRAP_SWITCH_KEY}=true"
  echo "       hermes"
  echo ""
}

perform_enable() {
  ensure_python_ready
  info "Enabling ${BOOTSTRAP_SWITCH_KEY}=true in ${BOOTSTRAP_CONFIG}"
  set_bootstrap_switch "true"
  ok "Bootstrap default is now ON for this user config"
}

perform_disable() {
  ensure_python_ready
  info "Disabling ${BOOTSTRAP_SWITCH_KEY} in ${BOOTSTRAP_CONFIG}"
  set_bootstrap_switch "false"
  ok "Bootstrap default is now OFF for this user config"
}

perform_uninstall() {
  ensure_python_ready
  ensure_selected_python_pip

  info "Uninstalling Hermes instrumentation package..."
  run_target_pip_quiet uninstall -y "${INSTRUMENTATION_PKG}" || true
  ok "Instrumentation package uninstall requested"

  if [[ "${PURGE_SHARED}" == true ]]; then
    info "Removing shared bootstrap packages..."
    run_target_pip_quiet uninstall -y "${BASE_PKGS[@]}" || true
    ok "Shared bootstrap package uninstall requested"
  fi

  cleanup_managed_config
  remove_local_controller

  echo ""
  echo -e "${BLACK}════════════════════════════════════════════════════${NC}"
  echo -e "${GREEN}  ✅ ${PLUGIN_NAME} uninstalled${NC}"
  echo -e "${BLACK}════════════════════════════════════════════════════${NC}"
  echo ""
}

need_value() {
  if [[ $# -lt 2 ]] || [[ "$2" == --* ]]; then
    error "Option $1 requires a value"
    exit 1
  fi
}

if [[ $# -gt 0 ]]; then
  case "$1" in
    install|enable|disable|status|uninstall|help)
      ACTION="$1"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    --*)
      ACTION="install"
      ;;
    *)
      error "Unknown action: $1"
      usage
      exit 1
      ;;
  esac
fi

while [[ $# -gt 0 ]]; do
  case "$1" in
    --endpoint)               need_value "$@"; ENDPOINT="$2"; shift 2 ;;
    --x-arms-license-key)     need_value "$@"; LICENSE_KEY="$2"; shift 2 ;;
    --x-arms-project)         need_value "$@"; ARMS_PROJECT="$2"; shift 2 ;;
    --x-cms-workspace)        need_value "$@"; CMS_WORKSPACE="$2"; shift 2 ;;
    --serviceName)            need_value "$@"; SERVICE_NAME="$2"; shift 2 ;;
    --python)                 need_value "$@"; CLI_PYTHON="$2"; shift 2 ;;
    --bootstrap-config)       need_value "$@"; BOOTSTRAP_CONFIG="$2"; shift 2 ;;
    --instrumentation-url)    need_value "$@"; INSTRUMENTATION_URL="$2"; INSTRUMENTATION_URL_EXPLICIT=true; shift 2 ;;
    --controller-url)         need_value "$@"; CONTROLLER_URL="$2"; shift 2 ;;
    --otlp-protocol)          need_value "$@"; OTLP_PROTOCOL="$2"; shift 2 ;;
    --no-chat-messages)       NO_CHAT_MESSAGES=true; shift ;;
    --skip-hermes-check)      SKIP_HERMES_CHECK=true; shift ;;
    --purge-shared)           PURGE_SHARED=true; shift ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      if [[ "${ACTION}" == "install" && -z "${INSTRUMENTATION_VERSION}" ]]; then
        INSTRUMENTATION_VERSION="$1"
        shift
        continue
      fi
      error "Unknown option: $1"
      usage
      exit 1
      ;;
  esac
done

case "${ACTION}" in
  install)
    perform_install
    ;;
  enable)
    perform_enable
    ;;
  disable)
    perform_disable
    ;;
  status)
    print_status
    ;;
  uninstall)
    perform_uninstall
    ;;
  help)
    usage
    ;;
  *)
    error "Unsupported action: ${ACTION}"
    exit 1
    ;;
esac
