#!/usr/bin/env bash
# ai-hats launcher — universal host-level entry-point (HATS-339).
#
# Installed once per host at ~/.local/bin/ai-hats by scripts/install-launcher.sh.
# Resolves the project venv (AI_HATS_VENV env > ai-hats.yaml venv_path >
# default <pwd>/.agent/ai-hats/.venv), heals the default venv if it's
# missing/broken, then exec's into <venv>/bin/ai-hats. Self update flows
# through the same path: heal-if-needed → delegate to python ai-hats
# (which renders the rich diff + auto-bumps).
set -euo pipefail

PROJECT="$(pwd)"
# HATS-766: anonymous git+https default (override still accepts ssh/local).
REPO_URL="${AI_HATS_REPO_URL:-git+https://github.com/muratovv/ai-hats.git}"
AH_DIR="$PROJECT/.agent/ai-hats"
DEFAULT_VENV="$AH_DIR/.venv"
VERSIONS_DIR="$AH_DIR/versions"   # HATS-647: blue-green versioned venvs

# pip install target — PEP 508 `name @ url` requires a URL scheme. For local
# directory paths we pass the path directly (pip detects pyproject.toml).
# Detection: presence of `://` signals a URL; anything else is treated as a
# local path or already a pip-acceptable spec.
if [[ "$REPO_URL" == *"://"* ]]; then
    PIP_TARGET="ai-hats @ $REPO_URL"
else
    PIP_TARGET="$REPO_URL"
fi

# --- HATS-766: channel-aware heal source ---
# Read the nested harness.channel / harness.path so a `channel: local` project
# heals EDITABLE (see heal_if_needed). Bounded reader for the indented `harness:`
# block, reusing the venv_path strip chain (comments/quotes/tilde).
read_harness_key() {
    local key="$1"
    [[ -f "$PROJECT/ai-hats.yaml" ]] || return 0
    awk '
        /^[^[:space:]]/ { inblock = ($0 ~ /^harness:[[:space:]]*$/) ? 1 : 0; next }
        inblock { print }
    ' "$PROJECT/ai-hats.yaml" \
        | grep -E "^[[:space:]]+${key}:[[:space:]]" \
        | head -1 \
        | sed -E "s/^[[:space:]]+${key}:[[:space:]]+//; s/[[:space:]]+#.*//; s/^\"//; s/\"\$//; s/^'//; s/'\$//" \
        || true
}

HARNESS_CHANNEL="$(read_harness_key channel)"
# Editable source for channel:local (default project root, resolved like venv_path).
LOCAL_SRC=""
if [[ "$HARNESS_CHANNEL" == "local" ]]; then
    LOCAL_SRC="$(read_harness_key path)"
    LOCAL_SRC="${LOCAL_SRC:-.}"
    LOCAL_SRC="${LOCAL_SRC/#\~/$HOME}"
    [[ "${LOCAL_SRC:0:1}" != "/" ]] && LOCAL_SRC="$PROJECT/$LOCAL_SRC"
fi

# --- Resolve venv (mirrors paths.venv_path precedence) ---
VENV=""
if [[ -n "${AI_HATS_VENV:-}" ]]; then
    VENV="${AI_HATS_VENV/#\~/$HOME}"
elif [[ -f "$PROJECT/ai-hats.yaml" ]]; then
    # Single-line scalar grep: `venv_path: <value>` (no multi-line / no
    # nested support — out of scope per HATS-339).
    val=$(grep -E '^venv_path:[[:space:]]' "$PROJECT/ai-hats.yaml" 2>/dev/null \
        | head -1 \
        | sed -E 's/^venv_path:[[:space:]]+//; s/[[:space:]]+#.*//; s/^"//; s/"$//; s/^'\''//; s/'\''$//' \
        || true)
    if [[ -n "$val" ]]; then
        val="${val/#\~/$HOME}"
        if [[ "${val:0:1}" == "/" ]]; then
            VENV="$val"
        else
            VENV="$PROJECT/$val"
        fi
    fi
fi
# HATS-647: managed blue-green resolution. When neither an explicit
# AI_HATS_VENV nor a yaml venv_path was set, prefer a valid versions/current
# pointer (a single line of text holding the active sha); else the legacy
# default .venv (lazy migration — pre-versioning installs keep working until
# the first managed `self update` populates versions/).
if [[ -z "$VENV" && -f "$VERSIONS_DIR/current" ]]; then
    # First line only + strip surrounding whitespace (matches paths.read_current_sha;
    # never squash a multi-line file into a fake single sha).
    sha="$(head -1 "$VERSIONS_DIR/current" 2>/dev/null | tr -d '[:space:]' || true)"
    # Require a USABLE versioned venv — both bin/ai-hats AND bin/python
    # executable. A present-but-broken versions/<sha>/ falls through to the
    # legacy default .venv rather than dead-ending, preserving self-heal
    # (HATS-656). The bin/python check is load-bearing: a host python upgrade
    # leaves bin/ai-hats present yet its shebang interpreter (bin/python) gone,
    # so an ai-hats-only check would select a venv that cannot actually run, and
    # heal_if_needed would refuse it as a non-default ("user-owned") venv. With
    # the python check, that broken venv routes to .venv, which heal recreates
    # and the managed self update then rebuilds versions/<sha>.
    if [[ -n "$sha" && "$sha" != *"/"* && "$sha" != "." && "$sha" != ".." \
          && -x "$VERSIONS_DIR/$sha/bin/ai-hats" \
          && -x "$VERSIONS_DIR/$sha/bin/python" ]]; then
        VENV="$VERSIONS_DIR/$sha"
    fi
fi
[[ -z "$VENV" ]] && VENV="$DEFAULT_VENV"

# HATS-647: pin-at-spawn. Export the resolved venv so every descendant
# (hooks, sub-agent child sessions, bash-chained `ai-hats … | ai-hats …`)
# stays on this exact venv for the whole run — even if versions/current
# flips mid-run. A child launcher sees AI_HATS_VENV set and honors it
# verbatim instead of re-resolving the pointer.
export AI_HATS_VENV="$VENV"

is_default_venv() { [[ "$VENV" == "$DEFAULT_VENV" ]]; }

# --- Idempotent heal: no-op if healthy, recreate if default broken,
#     refuse if override broken (user-owned). Called from `self update`
#     and `self init` before delegating, so the user only ever needs one
#     command for install + heal + (update|init). (HATS-337, HATS-612)
heal_if_needed() {
    if [[ -x "$VENV/bin/python" && -x "$VENV/bin/ai-hats" ]]; then
        return 0
    fi
    if ! is_default_venv; then
        echo "ai-hats: override venv $VENV is missing or broken." >&2
        echo "  Override venv is user-owned; ai-hats will not auto-recreate it." >&2
        if [[ "$HARNESS_CHANNEL" == "local" ]]; then
            echo "  Fix manually:  uv venv --python 3.11 $VENV && uv pip install --python $VENV/bin/python -e '$LOCAL_SRC'" >&2
        else
            echo "  Fix manually:  uv venv --python 3.11 $VENV && uv pip install --python $VENV/bin/python '$PIP_TARGET'" >&2
        fi
        exit 1
    fi
    echo "  · ai-hats venv missing or broken — recreating $VENV" >&2
    # Host-global launcher never auto-installs uv (that's bootstrap.sh's job) — fail loud.
    if ! command -v uv >/dev/null 2>&1; then
        echo "ai-hats: uv is required but not found on PATH." >&2
        echo "  Install: curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
        echo "  then re-run. (uv is the single host prerequisite — HATS-763.)" >&2
        exit 1
    fi
    rm -rf "$VENV"
    uv venv --python 3.11 "$VENV"
    # Bare install so the subsequent exec into <venv>/bin/ai-hats succeeds.
    # The python self update (called next via delegate) will then re-install
    # with --reinstall and render the rich diff + auto-bump.
    # HATS-766: channel:local heals EDITABLE (-e), else the PIP_TARGET rebuild.
    if [[ "$HARNESS_CHANNEL" == "local" ]]; then
        uv pip install --quiet --python "$VENV/bin/python" -e "$LOCAL_SRC"
    else
        uv pip install --quiet --python "$VENV/bin/python" "$PIP_TARGET"
    fi
}

# --- Self update / self init: heal-then-delegate. Never bypass the python
#     command — `self update` owns snapshot/diff/auto-bump/changelog
#     rendering (HATS-337); `self init` needs the venv to exist before it
#     can configure the project, so a fresh-project `self init` heals the
#     default venv here instead of forcing a separate `self update` first
#     (HATS-612). ---
if [[ "${1:-}" == "self" && ( "${2:-}" == "update" || "${2:-}" == "init" ) ]]; then
    heal_if_needed
    # Fall through to default exec below — python self update / init runs there.
fi

# --- Missing/broken-venv hint: bootstrap vs heal. A fresh project (no
#     ai-hats.yaml) is told to run `self init` — which now creates the venv
#     and configures in one step. An already-initialized project whose venv
#     broke (e.g. a host python upgrade) keeps `self update` for heal-
#     recovery, not a re-init. (HATS-612)
missing_venv_hint() {
    if [[ -f "$PROJECT/ai-hats.yaml" ]]; then
        echo "  Run: ai-hats self update" >&2
    else
        echo "  Run: ai-hats self init -r <role> -p <provider>" >&2
    fi
}

# --- Default fall-through: exec into venv-installed ai-hats ---
if [[ ! -x "$VENV/bin/python" ]]; then
    echo "ai-hats: venv missing at $VENV" >&2
    missing_venv_hint
    exit 1
fi
if [[ ! -x "$VENV/bin/ai-hats" ]]; then
    echo "ai-hats: venv exists at $VENV but ai-hats binary is missing." >&2
    missing_venv_hint
    exit 1
fi
exec "$VENV/bin/ai-hats" "$@"
