#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
# =============================================================================
# Skills Discovery & Management Helper
# =============================================================================
# Interactive discovery, description, and management of installed skills,
# native subagents, and importable community skills.
#
# Usage:
#   skills-helper.sh search <query>       # Search installed skills by keyword
#   skills-helper.sh browse [category]    # Browse skills by category
#   skills-helper.sh describe <name>      # Show detailed description of a skill
#   skills-helper.sh info <name>          # Show metadata (path, source, model tier)
#   skills-helper.sh list [--imported|--native|--all]  # List skills
#   skills-helper.sh categories           # List all skill categories
#   skills-helper.sh recommend <task>     # Suggest skills for a task description
#   skills-helper.sh help                 # Show this help
#
# Examples:
#   skills-helper.sh search "browser automation"
#   skills-helper.sh browse tools/browser
#   skills-helper.sh describe playwright
#   skills-helper.sh info seo-audit-skill
#   skills-helper.sh recommend "scrape a website and extract product data"
# =============================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || exit
source "${SCRIPT_DIR}/shared-constants.sh"

set -euo pipefail

# Bold not in shared-constants.sh — define locally
BOLD='\033[1m'

# Configuration
AGENTS_DIR="${AIDEVOPS_AGENTS_DIR:-$HOME/.aidevops/agents}"
SKILL_SOURCES="${AGENTS_DIR}/configs/skill-sources.json"

# =============================================================================
# Helper Functions
# =============================================================================

# Logging: uses shared log_* from shared-constants.sh with skills prefix
# shellcheck disable=SC2034  # Used by shared-constants.sh log_* functions
LOG_PREFIX="skills"

# Return 0 (true) if rel_path should be skipped (non-skill file), 1 otherwise.
# Pass allow_custom=1 to let custom/skills/* through.
_is_skipped_path() {
	local rel_path="$1"
	local allow_custom="${2:-0}"

	if [[ "$allow_custom" == "1" ]]; then
		case "$rel_path" in
		custom/skills/*) return 1 ;;
		esac
	fi

	case "$rel_path" in
	scripts/* | templates/* | memory/* | configs/* | custom/* | draft/* | AGENTS.md | VERSION | subagent-index.toon)
		return 0
		;;
	esac
	return 1
}

# Print a single skill entry line (used by search, browse, recommend).
_print_skill_entry() {
	local filename="$1"
	local category="$2"
	local desc="$3"
	local is_imported="$4"

	local type_label="native"
	if [[ "$is_imported" == "true" ]]; then
		type_label="imported"
	fi
	echo -e "  ${BOLD}${filename}${NC} ${CYAN}[$category]${NC} ${YELLOW}($type_label)${NC}"
	if [[ -n "$desc" ]]; then
		echo "    $desc"
	fi
	return 0
}

_push_temp_file_cleanup() {
	local temp_path="$1"

	push_cleanup "rm -f '${temp_path}'"
	return 0
}

_read_count_file_and_remove() {
	local count_path="$1"
	local precomputed="${2:-}"
	local found=""

	if [[ -n "$precomputed" ]]; then
		found="$precomputed"
		if [[ -n "$count_path" && -f "$count_path" ]]; then
			rm -f "$count_path"
		fi
	elif [[ -f "$count_path" ]]; then
		found=$(<"$count_path")
		rm -f "$count_path"
	fi

	[[ "$found" =~ ^[0-9]+$ ]] || found=0
	printf '%s\n' "$found"
	return 0
}

_is_search_stopword() {
	local word="$1"

	case "$word" in
	a | an | and | are | as | at | be | by | can | do | does | for | from | have | how | i | in | into | is | it | my | of | on | or | our | the | their | this | to | use | using | what | with | you | your)
		return 0
		;;
	agent | agents | aidevops | capability | capabilities | external | framework | model | models | review | reviews | tool | tools)
		return 0
		;;
	esac
	return 1
}

_query_terms() {
	local query_lower="$1"
	local raw_word

	while IFS= read -r raw_word; do
		if [[ -z "$raw_word" ]]; then
			continue
		fi
		if [[ ${#raw_word} -lt 2 ]]; then
			continue
		fi
		if _is_search_stopword "$raw_word"; then
			continue
		fi
		printf '%s\n' "$raw_word"
	done < <(printf '%s\n' "$query_lower" | tr -cs '[:alnum:]_-' '\n')
	return 0
}

# Find a skill file by name.  Sets the caller's skill_file variable via stdout.
# Prints the matched path, or empty string if not found.
# Pass allow_partial=1 to fall back to partial-name matches.
_find_skill_file() {
	local name="$1"
	local allow_partial="${2:-0}"

	local exact_match=""
	local candidates=()

	while IFS= read -r md_file; do
		local filename
		filename=$(basename "$md_file" .md)
		local rel_path="${md_file#"$AGENTS_DIR/"}"

		case "$rel_path" in
		scripts/* | templates/* | memory/* | configs/* | AGENTS.md | VERSION | subagent-index.toon)
			continue
			;;
		esac

		if [[ "$filename" == "$name" ]]; then
			exact_match="$md_file"
			break
		elif [[ "$allow_partial" == "1" && "$filename" == *"$name"* ]]; then
			candidates+=("$md_file")
		fi
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f | sort)

	if [[ -n "$exact_match" ]]; then
		echo "$exact_match"
	elif [[ "$allow_partial" == "1" && ${#candidates[@]} -gt 0 ]]; then
		echo "${candidates[0]}"
	fi
	return 0
}

# Scan AGENTS_DIR for skills matching query_lower; emit JSON entries or display lines.
# Arguments: query_lower json_output [count_file] [max_results]
_search_local_skills() {
	local query_lower="$1"
	local json_output="$2"
	local count_file="${3:-}"
	local max_results="${4:-0}"

	local found=0
	local printed=0
	local results=()
	local query_terms=()
	local term
	while IFS= read -r term; do
		query_terms+=("$term")
	done < <(_query_terms "$query_lower")

	if [[ ${#query_terms[@]} -eq 0 ]]; then
		if [[ "$json_output" == true ]]; then
			printf '0\t'
		elif [[ -n "$count_file" ]]; then
			printf '0\n' >"$count_file"
		else
			printf '0\n'
		fi
		return 0
	fi

	local required_matches=${#query_terms[@]}

	while IFS= read -r md_file; do
		local rel_path="${md_file#"$AGENTS_DIR/"}"
		local filename
		filename=$(basename "$md_file" .md)
		local category
		category=$(path_to_category "$rel_path")

		# Skip non-skill files (custom/skills/* allowed through)
		if _is_skipped_path "$rel_path" "1"; then
			continue
		fi

		local desc
		desc=$(extract_description "$md_file")
		local title
		title=$(extract_title "$md_file")

		local match_text="${filename} ${desc} ${title} ${category}"
		local match_lower
		match_lower=$(echo "$match_text" | tr '[:upper:]' '[:lower:]')

		local match_score=0
		local word
		for word in "${query_terms[@]+"${query_terms[@]}"}"; do
			if [[ "$match_lower" == *"$word"* ]]; then
				match_score=$((match_score + 1))
			fi
		done

		if [[ "$match_score" -ge "$required_matches" ]]; then
			((++found))
			local is_imported="false"
			if [[ "$filename" == *-skill ]]; then
				is_imported="true"
			fi

			if [[ "$json_output" == true ]]; then
				results+=("{\"name\":\"$filename\",\"category\":\"$category\",\"description\":\"${desc//\"/\\\"}\",\"imported\":$is_imported,\"path\":\"$rel_path\"}")
			else
				if [[ "$max_results" -eq 0 || "$printed" -lt "$max_results" ]]; then
					_print_skill_entry "$filename" "$category" "$desc" "$is_imported"
					printed=$((printed + 1))
				fi
			fi
		fi
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f | sort)

	if [[ "$json_output" == true ]]; then
		local results_json
		results_json=$(printf '%s,' "${results[@]}" || true)
		results_json="${results_json%,}"
		printf '%s\t%s' "$found" "$results_json"
	elif [[ -n "$count_file" ]]; then
		printf '%s\n' "$found" >"$count_file"
		if [[ "$max_results" -gt 0 && "$found" -gt "$printed" ]]; then
			printf '  ... %s more; narrow the query or browse a category.\n' "$((found - printed))"
		fi
	else
		echo "$found"
	fi
	return 0
}

# Display top-level category listing (no-arg branch of cmd_browse).
_browse_categories() {
	_save_cleanup_scope
	trap '_run_cleanups' RETURN

	echo ""
	echo -e "${BOLD}Skill Categories${NC}"
	echo "================"
	echo ""

	local cat_counts_file
	cat_counts_file=$(mktemp)
	_push_temp_file_cleanup "$cat_counts_file"

	while IFS= read -r md_file; do
		local rel_path="${md_file#"$AGENTS_DIR/"}"
		if _is_skipped_path "$rel_path" "0"; then
			continue
		fi
		local cat
		cat=$(path_to_category "$rel_path")
		local top_cat="${cat%%/*}"
		if [[ -n "$top_cat" && "$top_cat" != "root" ]]; then
			echo "$top_cat" >>"$cat_counts_file"
		fi
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f)

	if [[ -s "$cat_counts_file" ]]; then
		sort "$cat_counts_file" | uniq -c | sort -rn | while read -r count cat_name; do
			printf "  %-25s %s skill(s)\n" "$cat_name" "$count"
		done
	fi
	rm -f "$cat_counts_file"

	echo ""
	echo "Usage: skills-helper.sh browse <category>"
	echo "  e.g., skills-helper.sh browse tools"
	echo "        skills-helper.sh browse services"
	echo "        skills-helper.sh browse tools/browser"
	return 0
}

# Print the header and metadata block for cmd_describe.
# Arguments: skill_file filename rel_path category desc title model_tier
_describe_print_header() {
	local skill_file="$1"
	local filename="$2"
	local rel_path="$3"
	local category="$4"
	local desc="$5"
	local title="$6"
	local model_tier="$7"

	echo ""
	echo -e "${BOLD}${title:-$filename}${NC}"
	printf '=%.0s' $(seq 1 ${#filename})
	echo
	echo ""

	if [[ -n "$desc" ]]; then
		echo -e "  ${CYAN}Description:${NC} $desc"
	fi
	echo -e "  ${CYAN}Category:${NC}    $category"
	echo -e "  ${CYAN}Path:${NC}        $rel_path"

	if [[ -n "$model_tier" ]]; then
		echo -e "  ${CYAN}Model tier:${NC}  $model_tier"
	fi

	if [[ "$filename" == *-skill ]]; then
		echo -e "  ${CYAN}Type:${NC}        imported (community skill)"
		if [[ -f "$SKILL_SOURCES" ]] && command -v jq &>/dev/null; then
			local base_name="${filename%-skill}"
			local upstream
			upstream=$(jq -r --arg n "$base_name" '.skills[] | select(.name == $n) | .upstream_url // empty' "$SKILL_SOURCES" || true)
			if [[ -n "$upstream" ]]; then
				echo -e "  ${CYAN}Upstream:${NC}    $upstream"
			fi
			local imported_at
			imported_at=$(jq -r --arg n "$base_name" '.skills[] | select(.name == $n) | .imported_at // empty' "$SKILL_SOURCES" || true)
			if [[ -n "$imported_at" ]]; then
				echo -e "  ${CYAN}Imported:${NC}    $imported_at"
			fi
		fi
	else
		echo -e "  ${CYAN}Type:${NC}        native (aidevops built-in)"
	fi
	return 0
}

# Print the subagents block and content preview for cmd_describe.
# Arguments: skill_file
_describe_print_subagents_and_preview() {
	local skill_file="$1"

	local companion_dir="${skill_file%.md}"
	if [[ -d "$companion_dir" ]]; then
		local sub_count
		sub_count=$(find "$companion_dir" -maxdepth 1 -name "*.md" -type f | wc -l | tr -d ' ')
		if [[ "$sub_count" -gt 0 ]]; then
			echo ""
			echo -e "  ${CYAN}Subagents ($sub_count):${NC}"
			while IFS= read -r sub_file; do
				local sub_name
				sub_name=$(basename "$sub_file" .md)
				local sub_desc
				sub_desc=$(extract_description "$sub_file")
				if [[ -n "$sub_desc" ]]; then
					echo "    - $sub_name: $sub_desc"
				else
					echo "    - $sub_name"
				fi
			done < <(find "$companion_dir" -maxdepth 1 -name "*.md" -type f | sort)
		fi
	fi

	echo ""
	echo -e "  ${CYAN}Preview:${NC}"
	awk '
		/^---$/ { in_fm = !in_fm; next }
		in_fm { next }
		/^#/ { next }
		/^$/ { if (found) exit; next }
		{ found = 1; print "    " $0 }
	' "$skill_file" | head -5

	echo ""
	echo "Full content: $skill_file"
	return 0
}

# Match task description against keyword map; print matched categories one per line.
# Arguments: task_lower
_match_categories_from_task() {
	local task_lower="$1"

	local keyword_map
	keyword_map="browser=tools/browser
scrape=tools/browser
crawl=tools/browser
playwright=tools/browser
seo=seo
search engine=seo
keyword=seo
ranking=seo
deploy=tools/deployment
vercel=tools/deployment
coolify=tools/deployment
cloudron=tools/deployment
docker=tools/containers
container=tools/containers
wordpress=tools/wordpress
wp=tools/wordpress
agent=tools/build-agent
subagent=tools/build-agent
catalog=reference
catalogue=reference
routing=reference
route=reference
memory=reference
architecture=tools/architecture
litellm=tools/ai-assistants
gateway=services/hosting
model=tools/ai-assistants
product=product
onboarding=product
monetisation=product
growth=product
business=business
finance=business
calendar=tools/productivity
reminder=tools/productivity
git=tools/git
github=tools/git
pr=tools/git
pull request=tools/git
email=services/email
video=tools/video
image=tools/vision
pdf=tools/pdf
database=services/database
postgres=services/database
security=tools/security
secret=tools/credentials
api key=tools/credentials
voice=tools/voice
speech=tools/voice
mobile=tools/mobile
ios=tools/mobile
accessibility=tools/accessibility
wcag=tools/accessibility
content=content
blog=content
article=content
youtube=content
code review=tools/code-review
lint=tools/code-review
quality=tools/code-review
hosting=services/hosting
cloudflare=services/hosting
dns=services/hosting
monitor=services/monitoring
sentry=services/monitoring
document=tools/document
extract=tools/document
ocr=tools/ocr
receipt=accounts"

	local matched_categories=()
	local line
	while IFS= read -r line; do
		local kw="${line%%=*}"
		local cat="${line#*=}"
		if [[ "$task_lower" == *"$kw"* ]]; then
			local already=false
			local existing
			for existing in "${matched_categories[@]+"${matched_categories[@]}"}"; do
				if [[ "$existing" == "$cat" ]]; then
					already=true
					break
				fi
			done
			if [[ "$already" == false ]]; then
				matched_categories+=("$cat")
			fi
		fi
	done <<<"$keyword_map"

	local c
	for c in "${matched_categories[@]+"${matched_categories[@]}"}"; do
		echo "$c"
	done
	return 0
}

# List skills in a specific category (used by cmd_browse and cmd_recommend).
# Arguments: category [count_file] [max_results]
_list_skills_in_category() {
	local category="$1"
	local count_file="${2:-}"
	local max_results="${3:-0}"

	local found=0
	local printed=0
	while IFS= read -r md_file; do
		local rel_path="${md_file#"$AGENTS_DIR/"}"

		# Skip non-skill files (custom/skills/* allowed through)
		if _is_skipped_path "$rel_path" "1"; then
			continue
		fi

		local cat
		cat=$(path_to_category "$rel_path")

		if [[ "$cat" == "$category" || "$cat" == "$category/"* ]]; then
			local filename
			filename=$(basename "$md_file" .md)
			local desc
			desc=$(extract_description "$md_file")
			local is_imported="false"
			if [[ "$filename" == *-skill ]]; then
				is_imported="true"
			fi
			if [[ "$max_results" -eq 0 || "$printed" -lt "$max_results" ]]; then
				_print_skill_entry "$filename" "$category" "$desc" "$is_imported"
				printed=$((printed + 1))
			fi
			((++found))
		fi
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f | sort)

	if [[ -n "$count_file" ]]; then
		printf '%s\n' "$found" >"$count_file"
		if [[ "$max_results" -gt 0 && "$found" -gt "$printed" ]]; then
			printf '    ... %s more; run skills-helper.sh browse %s\n' "$((found - printed))" "$category"
		fi
	else
		echo "$found"
	fi
	return 0
}

show_help() {
	cat <<'EOF'
Skills Discovery & Management - Find, explore, and manage AI agent skills

USAGE:
    skills-helper.sh <command> [options]

COMMANDS:
    search <query>          Search installed skills by keyword
    search --registry <q>   Search the public skills.sh registry (online)
    browse [category]       Browse skills by category (interactive)
    describe <name>         Show detailed description of a skill/subagent
    info <name>             Show metadata (path, source, model tier, format)
    list [filter]           List skills (--imported, --native, --all)
    categories              List all skill categories with counts
    recommend <task>        Suggest relevant skills for a task description
    install <owner/repo@s>  Install a skill from the public registry
    help                    Show this help message

OPTIONS:
    --json                  Output in JSON format (for scripting)
    --quiet                 Suppress decorative output
    --registry              Search the public skills.sh registry (with search)
    --online                Alias for --registry

EXAMPLES:
    # Find skills related to browser automation
    skills-helper.sh search "browser automation"

    # Search the public skills.sh registry
    skills-helper.sh search --registry "browser automation"
    skills-helper.sh search --online "seo"

    # Browse all tools
    skills-helper.sh browse tools

    # Get details about a specific skill
    skills-helper.sh describe playwright

    # See metadata for an imported skill
    skills-helper.sh info seo-audit-skill

    # Get skill recommendations for a task
    skills-helper.sh recommend "deploy a Next.js app to Vercel"

    # List only imported community skills
    skills-helper.sh list --imported

    # List all categories
    skills-helper.sh categories

    # Install a skill from the public registry
    skills-helper.sh install vercel-labs/agent-browser@agent-browser
EOF
	return 0
}

# Extract description from a markdown file's YAML frontmatter
extract_description() {
	local file="$1"

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

	awk '
		/^---$/ { in_fm = !in_fm; next }
		in_fm && /^description:/ {
			sub(/^description: */, "")
			gsub(/^["'"'"']|["'"'"']$/, "")
			print
			exit
		}
	' "$file"
	return 0
}

# Extract model tier from frontmatter
extract_model_tier() {
	local file="$1"

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

	awk '
		/^---$/ { in_fm = !in_fm; next }
		in_fm && /^model:/ {
			sub(/^model: */, "")
			gsub(/^["'"'"']|["'"'"']$/, "")
			print
			exit
		}
	' "$file"
	return 0
}

# Get the first heading from a markdown file
extract_title() {
	local file="$1"

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

	grep -m1 "^# " "$file" | sed 's/^# //' || true
	return 0
}

# Derive a human-friendly category from a file path relative to AGENTS_DIR
path_to_category() {
	local rel_path="$1"
	local dir
	dir=$(dirname "$rel_path")

	# Strip leading ./ if present
	dir="${dir#./}"

	# Return the directory as category
	if [[ "$dir" == "." || -z "$dir" ]]; then
		echo "root"
	else
		echo "$dir"
	fi
	return 0
}

# =============================================================================
# Commands
# =============================================================================

cmd_search_registry() {
	local query="$1"

	if [[ -z "$query" ]]; then
		log_error "Search query required"
		echo "Usage: skills-helper.sh search --registry <query>"
		return 1
	fi

	if ! command -v npx &>/dev/null; then
		log_error "npx not found — install Node.js to use registry search"
		return 1
	fi

	log_info "Searching skills.sh registry for '$query'..."
	echo ""

	local raw_output
	# Strip ANSI escape codes for parsing, but capture raw for display
	raw_output=$(npx --yes skills find "$query" || true)

	if [[ -z "$raw_output" ]]; then
		log_warning "No results from skills.sh registry for '$query'"
		echo ""
		echo "Browse the registry at: https://skills.sh/"
		return 0
	fi

	# Display the raw output (already formatted by skills CLI)
	echo "$raw_output"
	echo ""

	# Parse and show install hint
	local pkg_count
	pkg_count=$(echo "$raw_output" | grep -cE '@[a-zA-Z0-9_-]+[[:space:]]' || true)
	if [[ "$pkg_count" -gt 0 ]]; then
		echo -e "  ${CYAN}Tip:${NC} Install with: skills-helper.sh install <owner/repo@skill>"
		echo -e "       Or:         aidevops skills install <owner/repo@skill>"
	fi

	return 0
}

cmd_install() {
	local pkg="$1"

	if [[ -z "$pkg" ]]; then
		log_error "Package required"
		echo "Usage: skills-helper.sh install <owner/repo@skill>"
		echo "Example: skills-helper.sh install vercel-labs/agent-browser@agent-browser"
		return 1
	fi

	if ! command -v npx &>/dev/null; then
		log_error "npx not found — install Node.js to use registry install"
		return 1
	fi

	log_info "Installing '$pkg' from skills.sh registry..."
	echo ""

	npx --yes skills add "$pkg" -g -y
	local exit_code=$?

	if [[ $exit_code -eq 0 ]]; then
		log_success "Installed '$pkg'"
		echo ""
		echo -e "  ${CYAN}Tip:${NC} Run 'aidevops update' to deploy to ~/.aidevops/agents/"
	else
		log_error "Install failed for '$pkg'"
		echo ""
		echo "Search for available skills: skills-helper.sh search --registry \"<query>\""
	fi

	return $exit_code
}

cmd_search() {
	local query="$1"
	local json_output="${2:-false}"
	local registry_search="${3:-false}"
	_save_cleanup_scope
	trap '_run_cleanups' RETURN

	# Handle --registry / --online flag embedded in query
	if [[ "$query" == "--registry" || "$query" == "--online" ]]; then
		log_error "Query required after --registry/--online flag"
		echo "Usage: skills-helper.sh search --registry <query>"
		return 1
	fi

	if [[ -z "$query" ]]; then
		log_error "Search query required"
		echo "Usage: skills-helper.sh search <query>"
		return 1
	fi

	# If registry search requested, delegate directly
	if [[ "$registry_search" == "true" ]]; then
		cmd_search_registry "$query"
		return $?
	fi

	local query_lower
	query_lower=$(echo "$query" | tr '[:upper:]' '[:lower:]')

	if [[ "$json_output" == true ]]; then
		local scan_result
		scan_result=$(_search_local_skills "$query_lower" "$json_output")
		local found results_json
		found=$(_read_count_file_and_remove "" "${scan_result%%	*}")
		results_json="${scan_result#*	}"
		echo "{\"query\":\"${query//\"/\\\"}\",\"count\":$found,\"results\":[$results_json]}"
	else
		local count_file found
		count_file=$(mktemp)
		_push_temp_file_cleanup "$count_file"
		echo ""
		_search_local_skills "$query_lower" "$json_output" "$count_file" "50"
		found=$(_read_count_file_and_remove "$count_file")
		if [[ "$found" -eq 0 ]]; then
			log_warning "No local skills found matching '$query'"
			echo ""
			echo "Try:"
			echo "  skills-helper.sh browse                    # Browse all categories"
			echo "  skills-helper.sh categories                # List categories"
			echo "  skills-helper.sh search --registry '$query'  # Search public registry"
			echo ""
			if command -v npx &>/dev/null; then
				echo -e "  ${CYAN}Search the public skills.sh registry?${NC}"
				echo "  Run: skills-helper.sh search --registry \"$query\""
			fi
		else
			log_info "Found $found skill(s) matching '$query'"
		fi
	fi

	return 0
}

cmd_browse() {
	local category="${1:-}"
	local json_output="${2:-false}"
	_save_cleanup_scope
	trap '_run_cleanups' RETURN

	if [[ -z "$category" ]]; then
		_browse_categories
		return $?
	fi

	# Browse specific category
	echo ""
	echo -e "${BOLD}Skills in: $category${NC}"
	printf '=%.0s' $(seq 1 $((${#category} + 12)))
	echo
	echo ""

	local count_file found
	count_file=$(mktemp)
	_push_temp_file_cleanup "$count_file"
	_list_skills_in_category "$category" "$count_file"
	found=$(_read_count_file_and_remove "$count_file")

	echo ""
	if [[ "$found" -eq 0 ]]; then
		log_warning "No skills found in category '$category'"
		echo ""
		echo "Available categories:"
		cmd_categories "false"
	else
		log_info "Found $found skill(s) in '$category'"
	fi

	return 0
}

cmd_describe() {
	local name="$1"

	if [[ -z "$name" ]]; then
		log_error "Skill name required"
		echo "Usage: skills-helper.sh describe <name>"
		return 1
	fi

	local skill_file
	skill_file=$(_find_skill_file "$name" "1")

	if [[ -z "$skill_file" || ! -f "$skill_file" ]]; then
		log_error "Skill not found: $name"
		echo ""
		echo "Try: skills-helper.sh search '$name'"
		return 1
	fi

	local filename
	filename=$(basename "$skill_file" .md)
	local rel_path="${skill_file#"$AGENTS_DIR/"}"
	local category
	category=$(path_to_category "$rel_path")
	local desc
	desc=$(extract_description "$skill_file")
	local title
	title=$(extract_title "$skill_file")
	local model_tier
	model_tier=$(extract_model_tier "$skill_file")

	_describe_print_header "$skill_file" "$filename" "$rel_path" "$category" "$desc" "$title" "$model_tier"
	_describe_print_subagents_and_preview "$skill_file"

	return 0
}

cmd_info() {
	local name="$1"
	local json_output="${2:-false}"

	if [[ -z "$name" ]]; then
		log_error "Skill name required"
		echo "Usage: skills-helper.sh info <name>"
		return 1
	fi

	local skill_file
	skill_file=$(_find_skill_file "$name" "0")

	if [[ -z "$skill_file" || ! -f "$skill_file" ]]; then
		log_error "Skill not found: $name"
		return 1
	fi

	local filename
	filename=$(basename "$skill_file" .md)
	local rel_path="${skill_file#"$AGENTS_DIR/"}"
	local category
	category=$(path_to_category "$rel_path")
	local desc
	desc=$(extract_description "$skill_file")
	local model_tier
	model_tier=$(extract_model_tier "$skill_file")
	local file_size
	file_size=$(wc -c <"$skill_file" | tr -d ' ')
	local line_count
	line_count=$(wc -l <"$skill_file" | tr -d ' ')

	local is_imported="false"
	local upstream_url=""
	local imported_at=""
	local format_detected=""

	if [[ "$filename" == *-skill ]]; then
		is_imported="true"
		if [[ -f "$SKILL_SOURCES" ]] && command -v jq &>/dev/null; then
			local base_name="${filename%-skill}"
			upstream_url=$(jq -r --arg n "$base_name" '.skills[] | select(.name == $n) | .upstream_url // empty' "$SKILL_SOURCES" || true)
			imported_at=$(jq -r --arg n "$base_name" '.skills[] | select(.name == $n) | .imported_at // empty' "$SKILL_SOURCES" || true)
			format_detected=$(jq -r --arg n "$base_name" '.skills[] | select(.name == $n) | .format_detected // empty' "$SKILL_SOURCES" || true)
		fi
	fi

	if [[ "$json_output" == true ]]; then
		local json_desc="${desc//\"/\\\"}"
		echo "{"
		echo "  \"name\": \"$filename\","
		echo "  \"category\": \"$category\","
		echo "  \"description\": \"$json_desc\","
		echo "  \"path\": \"$rel_path\","
		echo "  \"full_path\": \"$skill_file\","
		echo "  \"model_tier\": \"${model_tier:-unspecified}\","
		echo "  \"imported\": $is_imported,"
		echo "  \"upstream_url\": \"$upstream_url\","
		echo "  \"imported_at\": \"$imported_at\","
		echo "  \"format\": \"$format_detected\","
		echo "  \"size_bytes\": $file_size,"
		echo "  \"lines\": $line_count"
		echo "}"
	else
		echo ""
		printf "  %-15s %s\n" "Name:" "$filename"
		printf "  %-15s %s\n" "Category:" "$category"
		printf "  %-15s %s\n" "Description:" "${desc:-<none>}"
		printf "  %-15s %s\n" "Path:" "$rel_path"
		printf "  %-15s %s\n" "Full path:" "$skill_file"
		printf "  %-15s %s\n" "Model tier:" "${model_tier:-unspecified}"
		printf "  %-15s %s\n" "Type:" "$(if [[ "$is_imported" == "true" ]]; then echo "imported"; else echo "native"; fi)"
		printf "  %-15s %s\n" "Size:" "${file_size} bytes (${line_count} lines)"
		if [[ -n "$upstream_url" ]]; then
			printf "  %-15s %s\n" "Upstream:" "$upstream_url"
		fi
		if [[ -n "$imported_at" ]]; then
			printf "  %-15s %s\n" "Imported:" "$imported_at"
		fi
		if [[ -n "$format_detected" ]]; then
			printf "  %-15s %s\n" "Format:" "$format_detected"
		fi
		echo ""
	fi

	return 0
}

cmd_list() {
	local filter="${1:-all}"
	local json_output="${2:-false}"

	echo ""
	local header="Installed Skills"
	case "$filter" in
	--imported | imported)
		header="Imported Skills"
		filter="imported"
		;;
	--native | native)
		header="Native Skills"
		filter="native"
		;;
	*)
		filter="all"
		;;
	esac

	if [[ "$json_output" != true ]]; then
		echo -e "${BOLD}${header}${NC}"
		printf '=%.0s' $(seq 1 ${#header})
		echo
		echo ""
	fi

	local count=0
	local results=()

	while IFS= read -r md_file; do
		local rel_path="${md_file#"$AGENTS_DIR/"}"
		local filename
		filename=$(basename "$md_file" .md)

		# Skip non-skill files (custom/skills/* is allowed through)
		case "$rel_path" in
		custom/skills/*) ;;
		scripts/* | templates/* | memory/* | configs/* | custom/* | draft/* | AGENTS.md | VERSION | subagent-index.toon)
			continue
			;;
		esac

		local is_imported="false"
		if [[ "$filename" == *-skill ]]; then
			is_imported="true"
		fi

		# Apply filter
		if [[ "$filter" == "imported" && "$is_imported" != "true" ]]; then
			continue
		fi
		if [[ "$filter" == "native" && "$is_imported" == "true" ]]; then
			continue
		fi

		local category
		category=$(path_to_category "$rel_path")
		local desc
		desc=$(extract_description "$md_file")

		if [[ "$json_output" == true ]]; then
			results+=("{\"name\":\"$filename\",\"category\":\"$category\",\"description\":\"${desc//\"/\\\"}\",\"imported\":$is_imported}")
		else
			local type_label="native"
			if [[ "$is_imported" == "true" ]]; then
				type_label="imported"
			fi
			printf "  %-35s %-25s %s\n" "$filename" "[$category]" "($type_label)"
		fi
		((++count))
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f | sort)

	if [[ "$json_output" == true ]]; then
		local results_json
		results_json=$(printf '%s,' "${results[@]}" || true)
		results_json="${results_json%,}"
		echo "{\"filter\":\"$filter\",\"count\":$count,\"skills\":[$results_json]}"
	else
		echo ""
		log_info "Total: $count skill(s)"
	fi

	return 0
}

cmd_categories() {
	local json_output="${1:-false}"
	_save_cleanup_scope
	trap '_run_cleanups' RETURN

	local cat_counts_file
	cat_counts_file=$(mktemp)
	_push_temp_file_cleanup "$cat_counts_file"

	while IFS= read -r md_file; do
		local rel_path="${md_file#"$AGENTS_DIR/"}"

		# Skip non-skill files (custom/skills/* is allowed through)
		case "$rel_path" in
		custom/skills/*) ;;
		scripts/* | templates/* | memory/* | configs/* | custom/* | draft/* | AGENTS.md | VERSION | subagent-index.toon)
			continue
			;;
		esac

		local cat
		cat=$(path_to_category "$rel_path")
		if [[ -n "$cat" ]]; then
			echo "$cat" >>"$cat_counts_file"
		fi
	done < <(find -L "$AGENTS_DIR" -name "*.md" -type f)

	if [[ "$json_output" == true ]]; then
		local entries=()
		if [[ -s "$cat_counts_file" ]]; then
			while read -r count cat_name; do
				entries+=("{\"category\":\"$cat_name\",\"count\":$count}")
			done < <(sort "$cat_counts_file" | uniq -c | sort -rn | awk '{print $1, $2}')
		fi
		local entries_json
		entries_json=$(printf '%s,' "${entries[@]}" || true)
		entries_json="${entries_json%,}"
		echo "{\"categories\":[$entries_json]}"
	else
		echo ""
		echo -e "${BOLD}Skill Categories${NC}"
		echo "================"
		echo ""
		printf "  %-40s %s\n" "CATEGORY" "COUNT"
		printf "  %-40s %s\n" "--------" "-----"

		if [[ -s "$cat_counts_file" ]]; then
			sort "$cat_counts_file" | uniq -c | sort -rn | while read -r count cat_name; do
				printf "  %-40s %s\n" "$cat_name" "$count"
			done
		fi

		echo ""
		local total=0
		local num_cats=0
		if [[ -s "$cat_counts_file" ]]; then
			total=$(wc -l <"$cat_counts_file" | tr -d ' ')
			num_cats=$(sort "$cat_counts_file" | uniq | wc -l | tr -d ' ')
		fi
		log_info "Total: $total skill(s) in $num_cats categories"
	fi

	rm -f "$cat_counts_file"
	return 0
}

cmd_recommend() {
	local task_desc="$1"
	_save_cleanup_scope
	trap '_run_cleanups' RETURN

	if [[ -z "$task_desc" ]]; then
		log_error "Task description required"
		echo "Usage: skills-helper.sh recommend <task description>"
		return 1
	fi

	echo ""
	echo -e "${BOLD}Skill Recommendations${NC}"
	echo "====================="
	echo ""
	echo -e "  ${CYAN}Task:${NC} $task_desc"
	echo ""

	local task_lower
	task_lower=$(echo "$task_desc" | tr '[:upper:]' '[:lower:]')

	local matched_categories=()
	while IFS= read -r cat_line; do
		matched_categories+=("$cat_line")
	done < <(_match_categories_from_task "$task_lower")

	if [[ ${#matched_categories[@]} -eq 0 ]]; then
		log_info "No specific category match. Running general search..."
		echo ""
		cmd_search "$task_desc" "false"
		return 0
	fi

	echo -e "  ${CYAN}Matched categories:${NC} ${matched_categories[*]}"
	echo ""

	local total_found=0
	local cat
	for cat in "${matched_categories[@]}"; do
		echo -e "  ${BOLD}$cat:${NC}"

		local count_file found_in_cat
		count_file=$(mktemp)
		_push_temp_file_cleanup "$count_file"
		_list_skills_in_category "$cat" "$count_file" "12"
		found_in_cat=$(_read_count_file_and_remove "$count_file")
		total_found=$((total_found + found_in_cat))

		if [[ "$found_in_cat" -eq 0 ]]; then
			echo "    (no skills in this category)"
		fi
		echo ""
	done

	echo -e "  ${CYAN}Tip:${NC} Use 'skills-helper.sh describe <name>' for details on any skill."
	echo ""

	return 0
}

# =============================================================================
# Main
# =============================================================================

main() {
	local command="${1:-help}"
	shift || true

	local json_output=false
	local registry_search=false

	# Extract global options
	local args=()
	local arg
	while [[ $# -gt 0 ]]; do
		arg="$1"
		case "$arg" in
		--json)
			json_output=true
			shift
			;;
		--quiet | -q)
			shift
			;;
		--registry | --online)
			registry_search=true
			shift
			;;
		*)
			args+=("$arg")
			shift
			;;
		esac
	done

	case "$command" in
	search | s | find | f)
		cmd_search "${args[*]:-}" "$json_output" "$registry_search"
		;;
	browse | b)
		cmd_browse "${args[0]:-}" "$json_output"
		;;
	describe | desc | d | show)
		cmd_describe "${args[0]:-}"
		;;
	info | i | meta)
		cmd_info "${args[0]:-}" "$json_output"
		;;
	list | ls | l)
		cmd_list "${args[0]:-all}" "$json_output"
		;;
	categories | cats | cat)
		cmd_categories "$json_output"
		;;
	recommend | rec | suggest)
		cmd_recommend "${args[*]:-}"
		;;
	install | add)
		cmd_install "${args[0]:-}"
		;;
	registry | online)
		cmd_search_registry "${args[*]:-}"
		;;
	help | --help | -h)
		show_help
		;;
	*)
		log_error "Unknown command: $command"
		echo ""
		show_help
		return 1
		;;
	esac
}

main "$@"
