#!/bin/bash
# Sync nutshell-skills to AI agent skill directories.
#
# Usage:
#   ./sync-skills.sh                                        # all skills, all agents, global
#   ./sync-skills.sh --agent claude                          # Claude Code only
#   ./sync-skills.sh --agent claude,pi                       # Claude Code + Pi
#   ./sync-skills.sh --project ~/lc/tidb                     # project-level install
#   ./sync-skills.sh --skill platform                        # all platform skills
#   ./sync-skills.sh --skill diagnosis.tikv                  # diagnosis/tikv-* skills
#   ./sync-skills.sh --skill platform.clinic-api,diagnosis.pd  # specific skills
#
# Skill filter syntax (--skill):
#   category                 all skills under skills/category/
#   category.prefix          skills whose name starts with prefix
#   category.exact-name      a single skill by exact name
#   Comma-separated for multiple filters.
#
# Supported agents: claude, opencode, codex, pi (default: all)
#
# Global install creates symlinks in:
#   ~/.claude/skills/<name>/            (Claude Code)
#   ~/.config/opencode/skills/<name>/   (OpenCode)
#   ~/.codex/skills/<name>/             (Codex)
#   ~/.pi/skills/<name>/                (Pi)
#
# Project install (--project <path>) creates symlinks in:
#   <path>/.claude/skills/<name>/
#   <path>/.opencode/skills/<name>/
#   <path>/.codex/skills/<name>/
#   <path>/.pi/skills/<name>/

set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILLS_DIR="${SCRIPT_DIR}/skills"

ALL_AGENTS="claude,opencode,codex,pi"
AGENTS=""
PROJECT_DIR=""
SKILL_FILTER=""

usage() {
    cat <<'EOF'
Usage: ./sync-skills.sh [--agent AGENTS] [--skill FILTER] [--project PATH]

Options:
  --agent AGENTS    Comma-separated list of agents (default: all)
                    Supported: claude, opencode, codex, pi
  --skill FILTER    Comma-separated skill filters (default: all skills)
                    Format: category[.prefix]
  --project PATH    Install to project-level skill directories instead of global
  --list            List available skills grouped by category and exit
  -h, --help        Show this help

Skill filter examples:
  platform                            all platform/* skills
  diagnosis.tikv                      diagnosis/tikv-* skills
  platform.clinic-api                 single skill by exact name
  platform,diagnosis.pd,utilities     mix of categories and prefixes

Examples:
  ./sync-skills.sh                                          # all skills, all agents, global
  ./sync-skills.sh --agent claude --skill platform           # platform skills to Claude only
  ./sync-skills.sh --skill diagnosis.tikv,platform.o11y      # tikv + o11y skills
  ./sync-skills.sh --skill platform --project ~/lc/tidb      # platform skills, project-level
EOF
}

list_skills() {
    local current_category=""
    for skill_path in "${SKILLS_DIR}"/*/ "${SKILLS_DIR}"/*/*/ "${SKILLS_DIR}"/*/*/*/; do
        [ -d "$skill_path" ] || continue
        [ -f "${skill_path}/SKILL.md" ] || continue
        local rel="${skill_path#"${SKILLS_DIR}"/}"
        rel="${rel%/}"
        local category="${rel%/*}"
        local name="${rel##*/}"
        if [[ "$category" != "$current_category" ]]; then
            [[ -n "$current_category" ]] && echo ""
            echo "${category}/"
            current_category="$category"
        fi
        echo "  ${category}.${name}"
    done
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --agent)   AGENTS="${2:-}";       shift 2 ;;
        --skill)   SKILL_FILTER="${2:-}"; shift 2 ;;
        --project) PROJECT_DIR="${2:-}";  shift 2 ;;
        --list)    list_skills; exit 0 ;;
        -h|--help) usage; exit 0 ;;
        *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
    esac
done

AGENTS="${AGENTS:-${ALL_AGENTS}}"

# Validate agent names
IFS=',' read -ra AGENT_LIST <<< "$AGENTS"
for agent in "${AGENT_LIST[@]}"; do
    case "$agent" in
        claude|opencode|codex|pi) ;;
        *) echo "ERROR: unknown agent '${agent}' (supported: claude, opencode, codex, pi)" >&2; exit 1 ;;
    esac
done

# Parse skill filters into an array
SKILL_FILTERS=()
if [[ -n "$SKILL_FILTER" ]]; then
    IFS=',' read -ra SKILL_FILTERS <<< "$SKILL_FILTER"
fi

# Check if a skill (category + name) matches the filter list.
# Args: $1=category (e.g. "platform"), $2=skill_name (e.g. "clinic-api")
# With no filters, everything matches.
matches_filter() {
    [[ ${#SKILL_FILTERS[@]} -eq 0 ]] && return 0
    local category="$1"
    local skill_name="$2"
    local filter
    for filter in "${SKILL_FILTERS[@]}"; do
        # "category" or "category." -> match whole category
        if [[ "$filter" == "$category" || "$filter" == "${category}." ]]; then
            return 0
        fi
        # "category.prefix" -> exact name or prefix match
        if [[ "$filter" == *"."* ]]; then
            local f_cat="${filter%%.*}"
            local f_prefix="${filter#*.}"
            if [[ "$category" == *"$f_cat"* && "$skill_name" == "${f_prefix}"* ]]; then
                return 0
            fi
        fi
    done
    return 1
}

# Resolve skill directories per agent
resolve_skills_dir() {
    local agent="$1"
    if [[ -n "$PROJECT_DIR" ]]; then
        case "$agent" in
            claude)   echo "${PROJECT_DIR}/.claude/skills" ;;
            opencode) echo "${PROJECT_DIR}/.opencode/skills" ;;
            codex)    echo "${PROJECT_DIR}/.codex/skills" ;;
            pi)       echo "${PROJECT_DIR}/.pi/skills" ;;
        esac
    else
        case "$agent" in
            claude)   echo "${HOME}/.claude/skills" ;;
            opencode) echo "${HOME}/.config/opencode/skills" ;;
            codex)    echo "${CODEX_HOME:-${HOME}/.codex}/skills" ;;
            pi)       echo "${HOME}/.pi/skills" ;;
        esac
    fi
}

# Pretty-print a path, replacing $HOME with ~
prettify() {
    echo "${1/#$HOME/~}"
}

# Returns 0 if $1 is a symlink whose resolved target is inside $SKILLS_DIR.
is_our_link() {
    local link="$1"
    [ -L "$link" ] || return 1
    local target
    target="$(readlink "$link")"
    if [[ "$target" != /* ]]; then
        target="$(cd "$(dirname "$link")" && cd "$target" && pwd 2>/dev/null)" || return 1
    fi
    [[ "$target" == "${SKILLS_DIR}"/* || "$target" == "${SKILLS_DIR}" ]]
}

# Sync one skill to one agent's skill directory.
# Args: $1=agent $2=skill_path $3=skill_name
sync_one() {
    local agent="$1"
    local skill_path="$2"
    local skill_name="$3"
    local skills_dir
    skills_dir="$(resolve_skills_dir "$agent")"
    local link="${skills_dir}/${skill_name}"
    local pretty="$(prettify "$skills_dir")/${skill_name}"

    # OpenCode: clean up legacy single-file symlinks
    if [[ "$agent" == "opencode" ]]; then
        if [ -L "${link}.md" ] && is_our_link "${link}.md"; then
            rm -f "${link}.md"
        fi
    fi

    if [ -L "$link" ]; then
        if is_our_link "$link"; then
            rm "$link"
        else
            echo "  SKIP (foreign link): ${pretty}"
            return 0
        fi
    elif [ -e "$link" ]; then
        echo "  SKIP (not a symlink): ${pretty}"
        return 0
    fi

    ln -sf "${skill_path%/}" "$link"
    echo "  ${pretty}"
}

# Ensure target directories exist
for agent in "${AGENT_LIST[@]}"; do
    mkdir -p "$(resolve_skills_dir "$agent")"
done

install_mode="global"
[[ -n "$PROJECT_DIR" ]] && install_mode="project (${PROJECT_DIR})"

echo "Syncing skills from: ${SKILLS_DIR}"
echo "Agents: ${AGENTS}"
[[ -n "$SKILL_FILTER" ]] && echo "Filter: ${SKILL_FILTER}"
echo "Mode: ${install_mode}"
echo ""

synced=0

# Find all skill directories (those containing SKILL.md)
# Support flat (skills/*/), two-level (skills/*/*/), and three-level (skills/*/*/*/) structures
for skill_path in "${SKILLS_DIR}"/*/ "${SKILLS_DIR}"/*/*/ "${SKILLS_DIR}"/*/*/*/; do
    [ -d "$skill_path" ] || continue
    [ -f "${skill_path}/SKILL.md" ] || continue

    skill_name=$(basename "$skill_path")

    # Extract category: the path between skills/ and the skill directory
    rel="${skill_path#"${SKILLS_DIR}"/}"
    rel="${rel%/}"
    category="${rel%/*}"

    if ! matches_filter "$category" "$skill_name"; then
        continue
    fi

    for agent in "${AGENT_LIST[@]}"; do
        sync_one "$agent" "$skill_path" "$skill_name"
    done
    synced=$((synced + 1))
done

echo ""
if [[ $synced -eq 0 && -n "$SKILL_FILTER" ]]; then
    echo "No skills matched filter: ${SKILL_FILTER}"
    echo "Run ./sync-skills.sh --list to see available skills."
    exit 1
fi
echo "Done! Synced ${synced} skills."
for agent in "${AGENT_LIST[@]}"; do
    echo "  $(prettify "$(resolve_skills_dir "$agent")")/"
done
