#!/usr/bin/env bash

set -euo pipefail

BASE_URL="${AGENT_SKILLS_BASE_URL:-}"
TARGET="all"
DEST=""
FORCE=0
BACKUP=0
DRY_RUN=0
LIST_ONLY=0
TARGETS_ONLY=0
YES=0
REQUESTED_SKILLS=()
DETECTABLE_TARGETS=(codex-cli claude-cli opencode pi-agent hermes-agent)

usage() {
  cat <<'EOF'
用法:
  curl -fsSL "$AGENT_SKILLS_BASE_URL/install.sh" | bash -s -- [选项]

选项:
  --base-url URL           Pages 站点根地址；也可使用 AGENT_SKILLS_BASE_URL 环境变量
  --list                  列出远程可安装的 skill 后退出
  --skill NAME            只安装指定 skill，可重复使用
  --all                   安装全部远程 skill（默认）
  --target NAME           目标适配器，默认 all（自动探测已安装的 Agent）；可选 codex-cli、codex-desktop、claude-cli、claude-desktop、opencode、pi-agent、hermes-agent、agents-shared、generic
  --dest DIR              自定义目标 skill 目录；优先级高于 --target
  --force                 覆盖已有目标目录（默认不保留备份）
  --backup                与 --force 一起使用，在覆盖前保留时间戳备份
  --dry-run               只显示动作，不下载或修改文件
  --yes                   确认安装到探测到的 Agent（默认目标 all 时可跳过交互确认）
  --list-targets          列出支持的 Agent 目标和默认目录后退出
  -h, --help              显示帮助
EOF
}

print_targets() {
  printf '%-16s %s\n' "all" "自动探测已安装的 Agent（需 --yes）"
  printf '%-16s %s\n' "codex-cli" "$(target_destination codex-cli)"
  printf '%-16s %s\n' "codex-desktop" "$(target_destination codex-desktop)"
  printf '%-16s %s\n' "claude-cli" "$(target_destination claude-cli)"
  printf '%-16s %s\n' "claude-desktop" "$(target_destination claude-desktop)"
  printf '%-16s %s\n' "opencode" "$(target_destination opencode)"
  printf '%-16s %s\n' "pi-agent" "$(target_destination pi-agent)"
  printf '%-16s %s\n' "hermes-agent" "$(target_destination hermes-agent)"
  printf '%-16s %s\n' "agents-shared" "$(target_destination agents-shared)"
  printf '%-16s %s\n' "generic" "通过 --dest DIR 指定"
}

agent_is_installed() {
  case "$1" in
    codex-cli)
      command -v codex >/dev/null 2>&1 \
        || [[ -n "${CODEX_HOME:-}" && -e "${CODEX_HOME:-}" ]] \
        || [[ -n "${HOME:-}" && -e "${HOME:-}/.codex" ]] \
        || [[ -d "/Applications/Codex.app" || -d "${HOME:-}/Applications/Codex.app" ]]
      ;;
    claude-cli)
      command -v claude >/dev/null 2>&1 \
        || [[ -n "${CLAUDE_HOME:-}" && -e "${CLAUDE_HOME:-}" ]] \
        || [[ -n "${HOME:-}" && -e "${HOME:-}/.claude" ]] \
        || [[ -d "/Applications/Claude.app" || -d "${HOME:-}/Applications/Claude.app" ]]
      ;;
    opencode)
      command -v opencode >/dev/null 2>&1 \
        || [[ -n "${HOME:-}" && -e "${HOME:-}/.config/opencode" ]]
      ;;
    pi-agent)
      command -v pi >/dev/null 2>&1 \
        || [[ -n "${HOME:-}" && -e "${HOME:-}/.pi" ]]
      ;;
    hermes-agent)
      command -v hermes >/dev/null 2>&1 \
        || command -v hermes-agent >/dev/null 2>&1 \
        || [[ -n "${HOME:-}" && -e "${HOME:-}/.hermes" ]]
      ;;
    *)
      return 1
      ;;
  esac
}

die() {
  echo "错误: $*" >&2
  exit 1
}

target_destination() {
  case "$1" in
    codex|codex-cli|codex-desktop)
      printf '%s\n' "${CODEX_HOME:-${HOME:?HOME 未设置}/.codex}/skills"
      ;;
    claude|claude-cli|claude-desktop)
      printf '%s\n' "${CLAUDE_HOME:-${HOME:?HOME 未设置}/.claude}/skills"
      ;;
    opencode)
      printf '%s\n' "${HOME:?HOME 未设置}/.config/opencode/skills"
      ;;
    pi|pi-agent)
      printf '%s\n' "${HOME:?HOME 未设置}/.pi/agent/skills"
      ;;
    hermes|hermes-agent)
      printf '%s\n' "${HOME:?HOME 未设置}/.hermes/skills"
      ;;
    agents|agents-shared)
      printf '%s\n' "${HOME:?HOME 未设置}/.agents/skills"
      ;;
    generic)
      [[ -n "$DEST" ]] || die "使用 --target generic 时必须同时提供 --dest DIR"
      printf '%s\n' "$DEST"
      ;;
    *)
      die "未知目标适配器: $1（使用 --list-targets 查看支持列表）"
      ;;
  esac
}

resolve_destinations() {
  TARGET_NAMES=()
  TARGET_DIRS=()

  if [[ -n "$DEST" ]]; then
    TARGET_DISPLAY="generic"
    TARGET_NAMES=(generic)
    TARGET_DIRS=("$DEST")
    return
  fi

  if [[ "$TARGET" == "all" ]]; then
    TARGET_DISPLAY="detected"
    for target_name in "${DETECTABLE_TARGETS[@]}"; do
      if agent_is_installed "$target_name"; then
        destination_dir="$(target_destination "$target_name")"
        already_added=0
        if [[ ${#TARGET_DIRS[@]} -gt 0 ]]; then
          for existing_dir in "${TARGET_DIRS[@]}"; do
            if [[ "$existing_dir" == "$destination_dir" ]]; then
              already_added=1
              break
            fi
          done
        fi
        if [[ "$already_added" -eq 0 ]]; then
          TARGET_NAMES+=("$target_name")
          TARGET_DIRS+=("$destination_dir")
        fi
      fi
    done
    [[ ${#TARGET_DIRS[@]} -gt 0 ]] \
      || die "未检测到已安装的 Agent；请使用 --target 指定目标，或使用 --dest 指定目录"
    return
  fi

  TARGET_NAMES=("$TARGET")
  TARGET_DISPLAY="$TARGET"
  TARGET_DIRS=("$(target_destination "$TARGET")")
}

confirm_all_targets() {
  [[ "$TARGET" == "all" && ${#TARGET_DIRS[@]} -gt 1 && "$DRY_RUN" -eq 0 && "$YES" -eq 0 ]] || return 0

  echo "已探测到以下 Agent:" >&2
  for target_index in "${!TARGET_DIRS[@]}"; do
    echo "  ${TARGET_NAMES[$target_index]}: ${TARGET_DIRS[$target_index]}" >&2
  done
  if [[ ! -r /dev/tty ]]; then
    die "默认目标是已探测到的 Agent；非交互环境请添加 --yes 确认"
  fi
  if ! read -r -p "请输入 yes 确认继续: " answer < /dev/tty; then
    die "默认目标是已探测到的 Agent；非交互环境请添加 --yes 确认"
  fi
  [[ "$answer" == "yes" ]] || die "未确认安装"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --base-url)
      [[ $# -ge 2 ]] || die "--base-url 需要一个 URL"
      BASE_URL="${2%/}"
      shift 2
      ;;
    --list)
      LIST_ONLY=1
      shift
      ;;
    --list-targets)
      TARGETS_ONLY=1
      shift
      ;;
    --skill)
      [[ $# -ge 2 ]] || die "--skill 需要一个名称"
      REQUESTED_SKILLS+=("$2")
      shift 2
      ;;
    --all)
      REQUESTED_SKILLS=()
      shift
      ;;
    --target)
      [[ $# -ge 2 ]] || die "--target 需要一个名称"
      TARGET="$2"
      shift 2
      ;;
    --dest)
      [[ $# -ge 2 ]] || die "--dest 需要一个目录"
      DEST="$2"
      shift 2
      ;;
    --force)
      FORCE=1
      shift
      ;;
    --backup)
      BACKUP=1
      shift
      ;;
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    --yes)
      YES=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      die "未知参数: $1（使用 --help 查看帮助）"
      ;;
  esac
done

if [[ "$TARGETS_ONLY" -eq 1 ]]; then
  print_targets
  exit 0
fi

[[ -n "$BASE_URL" ]] || die "必须提供 --base-url URL 或设置 AGENT_SKILLS_BASE_URL"
command -v curl >/dev/null 2>&1 || die "需要 curl"
command -v tar >/dev/null 2>&1 || die "需要 tar"

case "$BASE_URL" in
  https://*|http://localhost*|http://127.0.0.1:*|http://\[::1\]:*) ;;
  *) die "远程安装要求使用 HTTPS；仅允许 localhost/回环地址使用 HTTP 做本地测试" ;;
esac

resolve_destinations

manifest="$(curl -fsSL --retry 2 "$BASE_URL/skills.json")" \
  || die "无法读取远程 skill 清单: $BASE_URL/skills.json"

REMOTE_NAMES=()
while IFS= read -r remote_name; do
  [[ -n "$remote_name" ]] || continue
  [[ "$remote_name" =~ ^[a-z0-9][a-z0-9-]{0,62}$ ]] \
    || die "远程清单包含非法 skill name: $remote_name"
  REMOTE_NAMES+=("$remote_name")
done < <(printf '%s\n' "$manifest" | awk -F'"' '/"name"[[:space:]]*:/ { print $4 }')

[[ ${#REMOTE_NAMES[@]} -gt 0 ]] || die "远程清单中没有 skill"

if [[ "$LIST_ONLY" -eq 1 ]]; then
  printf '%s\n' "${REMOTE_NAMES[@]}"
  exit 0
fi

SELECTED_NAMES=()
if [[ ${#REQUESTED_SKILLS[@]} -eq 0 ]]; then
  SELECTED_NAMES=("${REMOTE_NAMES[@]}")
else
  for requested_name in "${REQUESTED_SKILLS[@]}"; do
    found=0
    for remote_name in "${REMOTE_NAMES[@]}"; do
      if [[ "$remote_name" == "$requested_name" ]]; then
        SELECTED_NAMES+=("$remote_name")
        found=1
        break
      fi
    done
    [[ "$found" -eq 1 ]] || die "远程清单中找不到 skill: $requested_name"
  done
fi

for skill_name in "${SELECTED_NAMES[@]}"; do
  for destination_root in "${TARGET_DIRS[@]}"; do
    destination="${destination_root}/${skill_name}"
    if [[ ( -e "$destination" || -L "$destination" ) && "$FORCE" -ne 1 ]]; then
      die "目标已存在: ${destination}（如需替换请使用 --force）"
    fi
  done
done

confirm_all_targets

echo "远程站点: $BASE_URL"
echo "目标目录:"
for target_index in "${!TARGET_DIRS[@]}"; do
  echo "  ${TARGET_NAMES[$target_index]}: ${TARGET_DIRS[$target_index]}"
done
echo "目标适配器: $TARGET_DISPLAY"

temp_root="$(mktemp -d "${TMPDIR:-/tmp}/agent-skill-http.XXXXXX")"
trap 'rm -rf "$temp_root"' EXIT

for skill_name in "${SELECTED_NAMES[@]}"; do
  archive="$temp_root/${skill_name}.tar.gz"
  archive_url="$BASE_URL/skills/${skill_name}.tar.gz"

  if [[ "$DRY_RUN" -eq 1 ]]; then
    for target_index in "${!TARGET_DIRS[@]}"; do
      echo "[dry-run] $archive_url -> ${TARGET_DIRS[$target_index]}/${skill_name}"
    done
    continue
  fi

  curl -fsSL --retry 2 "$archive_url" -o "$archive" \
    || die "无法下载 skill 包: $archive_url"

  while IFS= read -r archive_entry; do
    case "$archive_entry" in
      /*|..|../*|*/../*|*/..)
        die "远程 skill 包包含不安全路径: $archive_entry"
        ;;
    esac
  done < <(tar -tzf "$archive")

  extracted="$temp_root/$skill_name"
  mkdir -p "$extracted"
  tar -xzf "$archive" -C "$extracted"
  [[ -f "$extracted/SKILL.md" ]] || die "远程 skill 包缺少 SKILL.md: $skill_name"

  for target_index in "${!TARGET_DIRS[@]}"; do
    target_name="${TARGET_NAMES[$target_index]}"
    destination_root="${TARGET_DIRS[$target_index]}"
    destination="${destination_root}/${skill_name}"
    mkdir -p "$destination_root"
    if [[ -e "$destination" || -L "$destination" ]]; then
      if [[ "$BACKUP" -eq 1 ]]; then
        timestamp="$(date +%Y%m%d%H%M%S)"
        backup="${destination}.backup.${timestamp}"
        suffix=1
        while [[ -e "$backup" || -L "$backup" ]]; do
          backup="${destination}.backup.${timestamp}.${suffix}"
          suffix=$((suffix + 1))
        done
        mv "$destination" "$backup"
        echo "已备份: $backup"
      else
        rm -rf "$destination"
      fi
    fi

    mkdir -p "$destination"
    cp -R "$extracted/." "$destination/"
    echo "已安装: $skill_name -> $target_name"
  done
done

if [[ "$DRY_RUN" -eq 0 ]]; then
  echo "提示: 安装完成后请新建会话或重新加载 Agent，使其重新扫描 skills 目录。"
fi
