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

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$SCRIPT_DIR"
DRY_RUN=0
TARGET_USER=""
TARGET_HOME=""
ENABLE_CODEX=1
ENABLE_COPILOT=1

usage() {
  cat <<'EOF'
Uso: ./link-user-runtime.sh [opzioni]

Crea link nel profilo dell'utente che lancia lo script per skill e agenti:
  Codex:
    ~/.agents/skills  -> <repo>/skills
    ~/.codex/skills   -> <repo>/skills
    ~/.codex/agents   -> <repo>/.codex/agents
  Copilot:
    ~/.copilot/skills -> <repo>/skills
    ~/.copilot/agents -> <repo>/.copilot/agents

Se un target esiste gia' come directory reale, lo script crea link interni
senza sovrascrivere file o directory esistenti.

Opzioni:
  --dry-run           Mostra le operazioni senza scrivere
  --root <path>       Root del repo mcp-servers (default: root dello script)
  --home <path>       Home target esplicita, utile per test
  --user <name>       Utente target esplicito (default: SUDO_USER o id -un)
  --codex-only        Crea solo link Codex
  --copilot-only      Crea solo link Copilot
  --all               Crea link Codex e Copilot (default)
  -h, --help          Mostra aiuto
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    --root)
      ROOT_DIR="$2"
      shift 2
      ;;
    --home)
      TARGET_HOME="$2"
      shift 2
      ;;
    --user)
      TARGET_USER="$2"
      shift 2
      ;;
    --codex-only)
      ENABLE_CODEX=1
      ENABLE_COPILOT=0
      shift
      ;;
    --copilot-only)
      ENABLE_CODEX=0
      ENABLE_COPILOT=1
      shift
      ;;
    --all)
      ENABLE_CODEX=1
      ENABLE_COPILOT=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Argomento non riconosciuto: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

if [[ ! -d "$ROOT_DIR" ]]; then
  echo "Root non valida: $ROOT_DIR" >&2
  exit 1
fi
ROOT_DIR="$(cd "$ROOT_DIR" && pwd)"

SKILLS_SRC="$ROOT_DIR/skills"
CODEX_AGENTS_SRC="$ROOT_DIR/.codex/agents"
COPILOT_AGENTS_SRC="$ROOT_DIR/.copilot/agents"

detect_user() {
  if [[ -n "$TARGET_USER" ]]; then
    printf '%s\n' "$TARGET_USER"
    return
  fi
  if [[ -n "${SUDO_USER:-}" && "${SUDO_USER:-}" != "root" ]]; then
    printf '%s\n' "$SUDO_USER"
    return
  fi
  id -un
}

resolve_home() {
  local user_name="$1"
  local resolved=""

  if [[ -n "$TARGET_HOME" ]]; then
    printf '%s\n' "$TARGET_HOME"
    return
  fi

  if command -v getent >/dev/null 2>&1; then
    resolved="$(getent passwd "$user_name" | awk -F: '{print $6}')"
  fi

  if [[ -z "$resolved" && "$user_name" == "$(id -un)" && -n "${HOME:-}" ]]; then
    resolved="$HOME"
  fi

  if [[ -z "$resolved" ]]; then
    resolved="$(eval "printf '%s' ~"$user_name"")"
  fi

  if [[ -z "$resolved" || "$resolved" == "~$user_name" ]]; then
    echo "Impossibile ricavare la home per l'utente: $user_name" >&2
    exit 1
  fi

  printf '%s\n' "$resolved"
}

print_cmd() {
  printf 'DRY-RUN:'
  printf ' %q' "$@"
  printf '\n'
}

run_cmd() {
  if [[ "$DRY_RUN" -eq 1 ]]; then
    print_cmd "$@"
  else
    "$@"
  fi
}

warn() {
  printf 'WARN: %s\n' "$*" >&2
}

path_real() {
  local path_value="$1"
  if command -v realpath >/dev/null 2>&1; then
    realpath -m "$path_value"
    return
  fi
  if readlink -f "$path_value" >/dev/null 2>&1; then
    readlink -f "$path_value"
    return
  fi
  local dir_name
  local base_name
  dir_name="$(dirname "$path_value")"
  base_name="$(basename "$path_value")"
  (cd "$dir_name" && printf '%s/%s\n' "$(pwd)" "$base_name")
}

same_path() {
  [[ "$(path_real "$1")" == "$(path_real "$2")" ]]
}

path_contains_path() {
  local parent_path child_path
  parent_path="$(path_real "$1")"
  child_path="$(path_real "$2")"

  [[ "$child_path" == "$parent_path" || "$child_path" == "$parent_path"/* ]]
}

covers_source_via_skills_root() {
  local candidate_link="$1"
  local source_path="$2"
  local candidate_real source_real source_leaf source_parent_leaf

  candidate_real="$(path_real "$candidate_link")"
  source_real="$(path_real "$source_path")"

  [[ "$(basename "$candidate_real")" == "skills" ]] || return 1

  source_leaf="$(basename "$source_real")"
  if [[ "$source_leaf" == "skills" ]]; then
    return 0
  fi

  source_parent_leaf="$(basename "$(dirname "$source_real")")"
  [[ "$source_parent_leaf" == "skills" ]] || return 1

  [[ -e "$candidate_link/$source_leaf" ]]
}

find_recursive_matching_link() {
  local src_path="$1"
  local search_root="$2"
  local candidate

  [[ -d "$search_root" ]] || return 1

  while IFS= read -r candidate; do
    if same_path "$src_path" "$candidate" || \
       path_contains_path "$candidate" "$src_path" || \
       covers_source_via_skills_root "$candidate" "$src_path"; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done < <(find "$search_root" -mindepth 1 -type l -print 2>/dev/null)

  return 1
}

ensure_source_dir() {
  local src_dir="$1"
  if [[ ! -d "$src_dir" ]]; then
    echo "Directory sorgente mancante: $src_dir" >&2
    exit 1
  fi
}

ensure_parent_dir() {
  local target_path="$1"
  local parent_dir
  parent_dir="$(dirname "$target_path")"

  if [[ -L "$parent_dir" ]]; then
    if [[ -d "$parent_dir" ]]; then
      return
    fi
    warn "Parent symlink non valido, salto: $parent_dir"
    return 1
  fi

  if [[ -e "$parent_dir" && ! -d "$parent_dir" ]]; then
    warn "Parent esiste ma non e' una directory, salto: $parent_dir"
    return 1
  fi

  if [[ ! -d "$parent_dir" ]]; then
    run_cmd mkdir -p "$parent_dir"
  fi
}

ensure_dir() {
  local dir_path="$1"

  if [[ -L "$dir_path" ]]; then
    if [[ -d "$dir_path" ]]; then
      return 0
    fi
    warn "Symlink directory non valido, salto: $dir_path"
    return 1
  fi

  if [[ -e "$dir_path" && ! -d "$dir_path" ]]; then
    warn "Target esiste ma non e' una directory, salto: $dir_path"
    return 1
  fi

  if [[ ! -d "$dir_path" ]]; then
    run_cmd mkdir -p "$dir_path"
  fi
}

link_one() {
  local src_path="$1"
  local dst_path="$2"
  local parent_dir
  local nested_match=""

  if [[ ! -e "$src_path" ]]; then
    warn "Sorgente mancante, salto: $src_path"
    return
  fi

  ensure_parent_dir "$dst_path" || return

  if [[ -L "$dst_path" ]]; then
    if same_path "$src_path" "$dst_path"; then
      printf 'SKIP: %s punta gia alla sorgente\n' "$dst_path"
    else
      warn "Target symlink diverso, salto: $dst_path -> $(readlink "$dst_path")"
    fi
    return
  fi

  if [[ -e "$dst_path" ]]; then
    warn "Target esistente non modificato: $dst_path"
    return
  fi

  parent_dir="$(dirname "$dst_path")"
  nested_match="$(find_recursive_matching_link "$src_path" "$parent_dir" || true)"
  if [[ -n "$nested_match" && "$nested_match" != "$dst_path" ]]; then
    printf 'SKIP: %s gia presente ricorsivamente in %s\n' "$src_path" "$nested_match"
    return
  fi

  run_cmd ln -s "$src_path" "$dst_path"
  printf 'LINK: %s -> %s\n' "$dst_path" "$src_path"
}

link_children() {
  local src_dir="$1"
  local dst_dir="$2"
  local child_kind="$3"
  local src_child
  local child_name

  ensure_dir "$dst_dir" || return

  for src_child in "$src_dir"/*; do
    [[ -e "$src_child" ]] || continue

    case "$child_kind" in
      dirs)
        [[ -d "$src_child" ]] || continue
        ;;
      files)
        [[ -f "$src_child" ]] || continue
        ;;
      *)
        echo "Tipo child non valido: $child_kind" >&2
        exit 2
        ;;
    esac

    child_name="$(basename "$src_child")"
    link_one "$src_child" "$dst_dir/$child_name"
  done
}

link_collection() {
  local label="$1"
  local src_dir="$2"
  local dst_dir="$3"
  local child_kind="$4"
  local parent_dir
  local nested_match=""

  ensure_source_dir "$src_dir"

  printf '\n> %s\n' "$label"

  if [[ -L "$dst_dir" ]]; then
    if same_path "$src_dir" "$dst_dir"; then
      printf 'SKIP: %s punta gia alla sorgente\n' "$dst_dir"
    else
      warn "Directory target symlink diversa, salto: $dst_dir -> $(readlink "$dst_dir")"
    fi
    return
  fi

  if [[ ! -e "$dst_dir" ]]; then
    ensure_parent_dir "$dst_dir" || return
    parent_dir="$(dirname "$dst_dir")"
    nested_match="$(find_recursive_matching_link "$src_dir" "$parent_dir" || true)"
    if [[ -n "$nested_match" && "$nested_match" != "$dst_dir" ]]; then
      printf 'SKIP: %s gia presente ricorsivamente in %s\n' "$src_dir" "$nested_match"
      return
    fi
    run_cmd ln -s "$src_dir" "$dst_dir"
    printf 'LINK: %s -> %s\n' "$dst_dir" "$src_dir"
    return
  fi

  if [[ -d "$dst_dir" ]]; then
    link_children "$src_dir" "$dst_dir" "$child_kind"
    return
  fi

  warn "Target esiste ma non e' una directory, salto: $dst_dir"
}

TARGET_USER="$(detect_user)"
TARGET_HOME="$(resolve_home "$TARGET_USER")"

printf 'Repo: %s\n' "$ROOT_DIR"
printf 'Utente target: %s\n' "$TARGET_USER"
printf 'Home target: %s\n' "$TARGET_HOME"

if [[ "$ENABLE_CODEX" -eq 1 ]]; then
  link_collection "Codex skills canoniche" "$SKILLS_SRC" "$TARGET_HOME/.agents/skills" dirs
  link_collection "Codex skills legacy/convenienza" "$SKILLS_SRC" "$TARGET_HOME/.codex/skills" dirs
  link_collection "Codex agents" "$CODEX_AGENTS_SRC" "$TARGET_HOME/.codex/agents" files
fi

if [[ "$ENABLE_COPILOT" -eq 1 ]]; then
  link_collection "Copilot skills" "$SKILLS_SRC" "$TARGET_HOME/.copilot/skills" dirs
  link_collection "Copilot agents" "$COPILOT_AGENTS_SRC" "$TARGET_HOME/.copilot/agents" files
fi

printf '\nCompletato.\n'
