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

MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
SKILLS_REPO_URL="${MOSAIC_SKILLS_REPO_URL:-https://git.mosaicstack.dev/mosaic/agent-skills.git}"
SKILLS_REPO_DIR="${MOSAIC_SKILLS_REPO_DIR:-$MOSAIC_HOME/sources/agent-skills}"
MOSAIC_SKILLS_DIR="$MOSAIC_HOME/skills"
MOSAIC_LOCAL_SKILLS_DIR="$MOSAIC_HOME/skills-local"

# Colon-separated list of skill names to install.  When set, only these skills
# are linked into runtime skill directories.  Empty/unset = link all skills
# (the legacy "mosaic sync" full-catalog behavior).
MOSAIC_INSTALL_SKILLS="${MOSAIC_INSTALL_SKILLS:-}"

fetch=1
link_only=0

usage() {
  cat <<USAGE
Usage: $(basename "$0") [options]

Sync canonical skills into ~/.config/mosaic/skills and link all Mosaic skills into runtime skill directories.

Options:
  --link-only     Skip git clone/pull and only relink from ~/.config/mosaic/{skills,skills-local}
  --no-link       Sync canonical skills but do not update runtime links
  -h, --help      Show help

Env:
  MOSAIC_HOME               Default: ~/.config/mosaic
  MOSAIC_SKILLS_REPO_URL    Default: https://git.mosaicstack.dev/mosaic/agent-skills.git
  MOSAIC_SKILLS_REPO_DIR    Default: ~/.config/mosaic/sources/agent-skills
  MOSAIC_INSTALL_SKILLS     Colon-separated list of skills to link (default: all)
USAGE
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --link-only)
      fetch=0
      shift
      ;;
    --no-link)
      link_only=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown argument: $1" >&2
      usage >&2
      exit 1
      ;;
  esac
done

mkdir -p "$MOSAIC_HOME" "$MOSAIC_SKILLS_DIR" "$MOSAIC_LOCAL_SKILLS_DIR"

if [[ $fetch -eq 1 ]]; then
  if [[ -d "$SKILLS_REPO_DIR/.git" ]]; then
    echo "[mosaic-skills] Updating skills source: $SKILLS_REPO_DIR"

    # ── Detect dirty state ──────────────────────────────────────────────
    dirty=""
    dirty="$(git -C "$SKILLS_REPO_DIR" status --porcelain 2>/dev/null || true)"

    if [[ -n "$dirty" ]]; then
      # ── Auto-migrate customized skills to skills-local/ ─────────────
      # Instead of stash/pop (fragile, merge conflicts), we:
      #   1. Identify which skill dirs contain user edits
      #   2. Copy those full skill dirs into skills-local/ (preserving edits)
      #   3. Reset the repo clean so pull always succeeds
      #   4. skills-local/ takes precedence during linking, so edits win

      SOURCE_SKILLS_SUBDIR="$SKILLS_REPO_DIR/skills"
      migrated=()

      while IFS= read -r line; do
        # porcelain format: XY <path>  — extract the file path
        file="${line:3}"
        # Only migrate files under skills/ subdir in the repo
        if [[ "$file" == skills/* ]]; then
          # Extract the skill directory name (first path component after skills/)
          skill_name="${file#skills/}"
          skill_name="${skill_name%%/*}"

          # Skip if already migrated this skill in this run
          local_skill_dir="$MOSAIC_LOCAL_SKILLS_DIR/$skill_name"
          if [[ -d "$local_skill_dir" ]]; then
            continue
          fi

          # Skip if skill_name is empty or hidden
          if [[ -z "$skill_name" || "$skill_name" == .* ]]; then
            continue
          fi

          # Copy the skill (with user's edits) from repo working tree to skills-local/
          if [[ -d "$SOURCE_SKILLS_SUBDIR/$skill_name" ]]; then
            cp -R "$SOURCE_SKILLS_SUBDIR/$skill_name" "$local_skill_dir"
            migrated+=("$skill_name")
          fi
        fi
      done <<< "$dirty"

      if [[ ${#migrated[@]} -gt 0 ]]; then
        echo "[mosaic-skills] Migrated ${#migrated[@]} customized skill(s) to skills-local/:"
        for s in "${migrated[@]}"; do
          echo "  → $MOSAIC_LOCAL_SKILLS_DIR/$s"
        done
        echo "[mosaic-skills] Your edits are preserved there and take precedence over canonical."
      fi

      # Reset repo to clean state so pull always works
      echo "[mosaic-skills] Resetting source repo to clean state..."
      git -C "$SKILLS_REPO_DIR" checkout . 2>/dev/null || true
      git -C "$SKILLS_REPO_DIR" clean -fd 2>/dev/null || true
    fi

    if ! git -C "$SKILLS_REPO_DIR" pull --rebase 2>/dev/null; then
      echo "[mosaic-skills] WARN: pull failed — continuing with existing checkout" >&2
      git -C "$SKILLS_REPO_DIR" rebase --abort 2>/dev/null || true
    fi
  else
    echo "[mosaic-skills] Cloning skills source to: $SKILLS_REPO_DIR"
    mkdir -p "$(dirname "$SKILLS_REPO_DIR")"
    git clone "$SKILLS_REPO_URL" "$SKILLS_REPO_DIR"
  fi

  SOURCE_SKILLS_DIR="$SKILLS_REPO_DIR/skills"
  if [[ ! -d "$SOURCE_SKILLS_DIR" ]]; then
    echo "[mosaic-skills] Missing source skills dir: $SOURCE_SKILLS_DIR" >&2
    exit 1
  fi

  if command -v rsync >/dev/null 2>&1; then
    rsync -a --delete "$SOURCE_SKILLS_DIR/" "$MOSAIC_SKILLS_DIR/"
  else
    rm -rf "$MOSAIC_SKILLS_DIR"/*
    cp -R "$SOURCE_SKILLS_DIR"/* "$MOSAIC_SKILLS_DIR"/
  fi
fi

if [[ ! -d "$MOSAIC_SKILLS_DIR" ]]; then
  echo "[mosaic-skills] Canonical skills dir missing: $MOSAIC_SKILLS_DIR" >&2
  exit 1
fi

if [[ $link_only -eq 1 ]]; then
  echo "[mosaic-skills] Canonical sync completed (link update skipped)"
  exit 0
fi

link_targets=(
  "$HOME/.claude/skills"
  "$HOME/.codex/skills"
  "$HOME/.config/opencode/skills"
  "$HOME/.pi/agent/skills"
)

canonical_real="$(readlink -f "$MOSAIC_SKILLS_DIR")"

# Build an associative array from the colon-separated whitelist for O(1) lookup.
# When MOSAIC_INSTALL_SKILLS is empty, all skills are allowed.
declare -A _skill_whitelist=()
_whitelist_active=0
if [[ -n "$MOSAIC_INSTALL_SKILLS" ]]; then
  _whitelist_active=1
  IFS=':' read -ra _wl_items <<< "$MOSAIC_INSTALL_SKILLS"
  for _item in "${_wl_items[@]}"; do
    [[ -n "$_item" ]] && _skill_whitelist["$_item"]=1
  done
fi

is_skill_selected() {
  local name="$1"
  if [[ $_whitelist_active -eq 0 ]]; then
    return 0
  fi
  [[ -n "${_skill_whitelist[$name]:-}" ]] && return 0
  return 1
}

link_skill_into_target() {
  local skill_path="$1"
  local target_dir="$2"

  local name link_path
  name="$(basename "$skill_path")"

  # Do not distribute hidden/system skill directories globally.
  if [[ "$name" == .* ]]; then
    return
  fi

  # Respect the install whitelist (set during first-run wizard).
  if ! is_skill_selected "$name"; then
    return
  fi

  link_path="$target_dir/$name"

  if [[ -L "$link_path" ]]; then
    ln -sfn "$skill_path" "$link_path"
    return
  fi

  if [[ -e "$link_path" ]]; then
    echo "[mosaic-skills] Preserve existing runtime-specific entry: $link_path"
    return
  fi

  ln -s "$skill_path" "$link_path"
}

is_mosaic_skill_name() {
  local name="$1"
  # -d follows symlinks; -L catches broken symlinks that still indicate ownership
  [[ -d "$MOSAIC_SKILLS_DIR/$name" || -L "$MOSAIC_SKILLS_DIR/$name" ]] && return 0
  [[ -d "$MOSAIC_LOCAL_SKILLS_DIR/$name" || -L "$MOSAIC_LOCAL_SKILLS_DIR/$name" ]] && return 0
  return 1
}

prune_stale_links_in_target() {
  local target_dir="$1"

  while IFS= read -r -d '' link_path; do
    local name resolved
    name="$(basename "$link_path")"

    if is_mosaic_skill_name "$name"; then
      continue
    fi

    resolved="$(readlink -f "$link_path" 2>/dev/null || true)"
    if [[ -z "$resolved" ]]; then
      rm -f "$link_path"
      echo "[mosaic-skills] Removed stale broken skill link: $link_path"
      continue
    fi

    if [[ "$resolved" == "$MOSAIC_HOME/"* ]]; then
      rm -f "$link_path"
      echo "[mosaic-skills] Removed stale retired skill link: $link_path"
    fi
  done < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type l -print0)
}

for target in "${link_targets[@]}"; do
  mkdir -p "$target"

  # If target already resolves to canonical dir, skip to avoid self-link recursion/corruption.
  target_real="$(readlink -f "$target" 2>/dev/null || true)"
  if [[ -n "$target_real" && "$target_real" == "$canonical_real" ]]; then
    echo "[mosaic-skills] Skip target (already canonical): $target"
    continue
  fi

  prune_stale_links_in_target "$target"

  while IFS= read -r -d '' skill; do
    link_skill_into_target "$skill" "$target"
  done < <(find "$MOSAIC_SKILLS_DIR" -mindepth 1 -maxdepth 1 -type d -print0)

  if [[ -d "$MOSAIC_LOCAL_SKILLS_DIR" ]]; then
    while IFS= read -r -d '' skill; do
      link_skill_into_target "$skill" "$target"
    done < <(find "$MOSAIC_LOCAL_SKILLS_DIR" -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -print0)
  fi

  echo "[mosaic-skills] Linked skills into: $target"
done

echo "[mosaic-skills] Complete"
