#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SKILLS_DIR="$ROOT_DIR/skills"
PRIVATE_SKILLS_DIR="$ROOT_DIR/private-skills"
VENDOR_DIR="$ROOT_DIR/vendor"
CATALOG_FILE="$ROOT_DIR/catalog.yaml"
ENTRY_FILES=("SKILL.md" "AGENT.md" "AGENTS.md")
DEFAULT_SYNC_EXCLUDE=("example-skill")

usage() {
  cat <<USAGE
Usage:
  agent-skills list
  agent-skills validate
  agent-skills sync [--profile codex,cursor] [--mode symlink|copy] [--dry-run]
  agent-skills import --source <name> --repo <url> [--ref <git-ref>] [--subdir <path>] [--skills a,b] [--dry-run]

Supported skill entry files:
  SKILL.md, AGENT.md, AGENTS.md

Profiles:
  codex  -> \
    \\${CODEX_HOME:-$HOME/.codex}/skills
    (also syncs slash commands to \\${CODEX_HOME:-$HOME/.codex}/prompts)
  cursor -> \
    \\${CURSOR_SKILLS_DIR:-$HOME/.cursor/skills}
USAGE
}

codex_target() {
  echo "${CODEX_HOME:-$HOME/.codex}/skills"
}

codex_prompts_target() {
  echo "${CODEX_HOME:-$HOME/.codex}/prompts"
}

cursor_target() {
  echo "${CURSOR_SKILLS_DIR:-$HOME/.cursor/skills}"
}

detect_entry_file() {
  local dir="$1"
  for entry in "${ENTRY_FILES[@]}"; do
    if [[ -f "$dir/$entry" ]]; then
      echo "$entry"
      return 0
    fi
  done
  return 1
}

detect_format_from_dir() {
  local dir="$1"
  local entry=""
  entry="$(detect_entry_file "$dir" || true)"
  case "$entry" in
    SKILL.md) echo "skill_md" ;;
    AGENT.md) echo "agent_md" ;;
    AGENTS.md) echo "agents_md" ;;
    *) echo "unknown" ;;
  esac
}

find_skill_dirs_under() {
  local base="$1"
  if [[ ! -d "$base" ]]; then
    return 0
  fi

  find "$base" -type f \( -name "SKILL.md" -o -name "AGENT.md" -o -name "AGENTS.md" \) -print \
    | sed -E 's#/(SKILL|AGENT|AGENTS)\.md$##' \
    | sort -u
}

list_slash_command_files() {
  {
    if [[ -d "$ROOT_DIR/slash-commands" ]]; then
      find "$ROOT_DIR/slash-commands" -type f -name "*.md" -print
    fi

    if [[ -d "$VENDOR_DIR" ]]; then
      find "$VENDOR_DIR" -type f -path "*/docs/slash-commands/*.md" -print
      find "$VENDOR_DIR" -type f -path "*/slash-commands/*.md" -print
    fi
  } | sort -u
}

list_skills() {
  for base in "$SKILLS_DIR" "$PRIVATE_SKILLS_DIR"; do
    [[ -d "$base" ]] || continue
    while IFS= read -r dir; do
      if detect_entry_file "$dir" >/dev/null; then
        echo "$dir"
      fi
    done < <(find "$base" -mindepth 1 -maxdepth 1 -type d -print | sort)
  done

  find_skill_dirs_under "$VENDOR_DIR"
}

skill_dest_name() {
  local src="$1"
  if [[ "$src" == "$VENDOR_DIR"/*/* ]]; then
    local rel="${src#"$VENDOR_DIR"/}"
    local source_name="${rel%%/*}"
    local skill_name="${rel#*/}"
    skill_name="${skill_name%%/*}"
    echo "${source_name}--${skill_name}"
  else
    basename "$src"
  fi
}

link_or_copy_skill() {
  local src="$1"
  local dest_root="$2"
  local mode="$3"
  local dry_run="$4"
  local name
  name="$(skill_dest_name "$src")"
  local dest="$dest_root/$name"

  if [[ "$dry_run" == "true" ]]; then
    echo "[dry-run] $mode $src -> $dest"
    return 0
  fi

  mkdir -p "$dest_root"
  rm -rf "$dest"

  if [[ "$mode" == "symlink" ]]; then
    ln -s "$src" "$dest"
  else
    cp -R "$src" "$dest"
  fi

  echo "$mode $src -> $dest"
}

link_or_copy_file() {
  local src="$1"
  local dest_root="$2"
  local mode="$3"
  local dry_run="$4"
  local name
  name="$(basename "$src")"
  local dest="$dest_root/$name"

  if [[ "$dry_run" == "true" ]]; then
    echo "[dry-run] $mode $src -> $dest"
    return 0
  fi

  mkdir -p "$dest_root"
  rm -f "$dest"

  if [[ "$mode" == "symlink" ]]; then
    ln -s "$src" "$dest"
  else
    cp "$src" "$dest"
  fi

  echo "$mode $src -> $dest"
}

sync_codex_slash_commands() {
  local mode="$1"
  local dry_run="$2"
  local target
  target="$(codex_prompts_target)"

  while IFS= read -r cmd_path; do
    [[ -f "$cmd_path" ]] || continue
    link_or_copy_file "$cmd_path" "$target" "$mode" "$dry_run"
  done < <(list_slash_command_files)
}

sync_profile() {
  local profile="$1"
  local mode="$2"
  local dry_run="$3"
  local target

  case "$profile" in
    codex) target="$(codex_target)" ;;
    cursor) target="$(cursor_target)" ;;
    *)
      echo "Unknown profile: $profile" >&2
      return 1
      ;;
  esac

  while IFS= read -r skill_path; do
    detect_entry_file "$skill_path" >/dev/null || continue
    local skill_name
    skill_name="$(basename "$skill_path")"
    if [[ " ${DEFAULT_SYNC_EXCLUDE[*]} " == *" ${skill_name} "* ]]; then
      echo "[skip] excluded from sync: $skill_path"
      continue
    fi
    link_or_copy_skill "$skill_path" "$target" "$mode" "$dry_run"
  done < <(list_skills)

  if [[ "$profile" == "codex" ]]; then
    sync_codex_slash_commands "$mode" "$dry_run"
  fi
}

catalog_has_id() {
  local id="$1"
  rg -q "^[[:space:]]*- id: ${id}$" "$CATALOG_FILE"
}

append_catalog_entry() {
  local id="$1"
  local name="$2"
  local path="$3"
  local repo="$4"
  local ref="$5"
  local commit="$6"
  local format="$7"

  if catalog_has_id "$id"; then
    echo "[catalog] id '$id' already exists, skipped"
    return 0
  fi

  cat >> "$CATALOG_FILE" <<__CATALOG__

  - id: $id
    name: $name
    path: $path
    format: $format
    source: upstream
    upstream:
      type: git
      repo: $repo
      ref: $ref
      commit: $commit
    tags: [upstream, imported]
__CATALOG__

  echo "[catalog] added id '$id'"
}

import_repo_skills() {
  local source_name="$1"
  local repo="$2"
  local ref="$3"
  local subdir="$4"
  local skills_csv="$5"
  local dry_run="$6"

  command -v git >/dev/null || {
    echo "git is required for import" >&2
    exit 1
  }

  local tmp_dir
  tmp_dir="$(mktemp -d)"
  trap "rm -rf '$tmp_dir'" EXIT

  echo "Cloning $repo@$ref ..."
  git clone --depth 1 --branch "$ref" "$repo" "$tmp_dir/repo" >/dev/null

  local commit
  commit="$(git -C "$tmp_dir/repo" rev-parse HEAD)"

  local import_base="$tmp_dir/repo"
  if [[ -n "$subdir" ]]; then
    import_base="$import_base/$subdir"
  fi

  if [[ ! -d "$import_base" ]]; then
    echo "Import base does not exist: $import_base" >&2
    exit 1
  fi

  mapfile -t candidates < <(find_skill_dirs_under "$import_base")
  if [[ "${#candidates[@]}" -eq 0 ]]; then
    echo "No skills found under: $import_base" >&2
    exit 1
  fi

  local selected=()
  if [[ -n "$skills_csv" ]]; then
    IFS=',' read -r -a wanted_skills <<< "$skills_csv"
    for wanted in "${wanted_skills[@]}"; do
      local matched=""
      for candidate in "${candidates[@]}"; do
        if [[ "$(basename "$candidate")" == "$wanted" ]]; then
          matched="$candidate"
          break
        fi
      done
      if [[ -z "$matched" ]]; then
        echo "Skill '$wanted' not found in repo import set" >&2
        exit 1
      fi
      selected+=("$matched")
    done
  else
    selected=("${candidates[@]}")
  fi

  local target_root="$VENDOR_DIR/$source_name"
  mkdir -p "$target_root"

  for src in "${selected[@]}"; do
    local skill_name
    skill_name="$(basename "$src")"
    local target_dir="$target_root/$skill_name"
    local catalog_id="${source_name}-${skill_name}"
    local format
    format="$(detect_format_from_dir "$src")"

    if [[ "$dry_run" == "true" ]]; then
      echo "[dry-run] import $src -> $target_dir"
      echo "[dry-run] format: $format"
      echo "[dry-run] catalog id: $catalog_id"
      continue
    fi

    rm -rf "$target_dir"
    mkdir -p "$target_dir"
    cp -R "$src"/. "$target_dir"
    echo "imported $src -> $target_dir"

    append_catalog_entry \
      "$catalog_id" \
      "$source_name/$skill_name" \
      "vendor/$source_name/$skill_name" \
      "$repo" \
      "$ref" \
      "$commit" \
      "$format"
  done
}

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

case "$command" in
  list)
    list_skills
    ;;
  validate)
    "$ROOT_DIR/scripts/validate.sh"
    ;;
  sync)
    profiles="codex"
    mode="symlink"
    dry_run="false"

    while [[ $# -gt 0 ]]; do
      case "$1" in
        --profile)
          profiles="${2:-}"
          shift 2
          ;;
        --mode)
          mode="${2:-}"
          shift 2
          ;;
        --dry-run)
          dry_run="true"
          shift
          ;;
        *)
          echo "Unknown option: $1" >&2
          usage
          exit 1
          ;;
      esac
    done

    if [[ "$mode" != "symlink" && "$mode" != "copy" ]]; then
      echo "Invalid mode: $mode (expected symlink|copy)" >&2
      exit 1
    fi

    IFS=',' read -r -a profile_arr <<< "$profiles"
    for p in "${profile_arr[@]}"; do
      sync_profile "$p" "$mode" "$dry_run"
    done
    ;;
  import)
    source_name=""
    repo=""
    ref="main"
    subdir=""
    skills_csv=""
    dry_run="false"

    while [[ $# -gt 0 ]]; do
      case "$1" in
        --source)
          source_name="${2:-}"
          shift 2
          ;;
        --repo)
          repo="${2:-}"
          shift 2
          ;;
        --ref)
          ref="${2:-}"
          shift 2
          ;;
        --subdir)
          subdir="${2:-}"
          shift 2
          ;;
        --skills)
          skills_csv="${2:-}"
          shift 2
          ;;
        --dry-run)
          dry_run="true"
          shift
          ;;
        *)
          echo "Unknown option: $1" >&2
          usage
          exit 1
          ;;
      esac
    done

    if [[ -z "$source_name" || -z "$repo" ]]; then
      echo "import requires --source and --repo" >&2
      usage
      exit 1
    fi

    import_repo_skills "$source_name" "$repo" "$ref" "$subdir" "$skills_csv" "$dry_run"
    ;;
  *)
    usage
    exit 1
    ;;
esac
