#!/usr/bin/env bash

# Source this file after resolving the runtime root and before any Python
# probe. XDG_CACHE_HOME is intentionally ignored: callers must not be able to
# redirect generated bytecode into a signed application bundle.
agentlas_python_cache_prefix() {
  local selected_python="${1:-}"
  local home_real prefix

  [[ -n "${HOME:-}" && "$HOME" == /* && -d "$HOME" ]] || return 1
  home_real="$(cd -P "$HOME" 2>/dev/null && pwd)" || return 1
  case "$(uname -s 2>/dev/null || true)" in
    Darwin)
      prefix="$home_real/Library/Caches/Agentlas/python"
      ;;
    *)
      prefix="$home_real/.cache/agentlas/python"
      ;;
  esac
  [[ "$prefix" == /* && "$prefix" != *$'\n'* && "$prefix" != *$'\r'* ]] || return 1
  case "$prefix" in
    *.app/Contents/Resources|*.app/Contents/Resources/*|*/resources|*/resources/*)
      return 1
      ;;
  esac

  if [[ -n "$selected_python" && "$selected_python" != "py -3" ]]; then
    local executable="$selected_python"
    if [[ "$executable" != */* ]]; then
      executable="$(command -v "$executable" 2>/dev/null || true)"
    fi
    if [[ -n "$executable" && -e "$executable" ]]; then
      local executable_real
      executable_real="$(cd -P "$(dirname "$executable")" 2>/dev/null && printf '%s/%s\n' "$PWD" "$(basename "$executable")")" || return 1
      case "$executable_real" in
        *.app/Contents/Resources/*)
          local app_resources="${executable_real%%.app/Contents/Resources/*}.app/Contents/Resources"
          case "$prefix" in
            "$app_resources"|"$app_resources"/*) return 1 ;;
          esac
          ;;
      esac
    fi
  fi

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

agentlas_export_python_cache_boundary() {
  local prefix
  export PYTHONDONTWRITEBYTECODE=1
  prefix="$(agentlas_python_cache_prefix "${1:-}")" || return 1
  export PYTHONPYCACHEPREFIX="$prefix"
}

# Canonical interpreter resolution for the installer and its post-install
# gates. Return a shell command string because the Windows launcher is two
# words (`py -3`); callers intentionally invoke the result unquoted.
agentlas_python_candidate_ok() {
  "$@" -c 'import sys
if sys.version_info < (3, 9):
    raise SystemExit(1)
from jsonschema import Draft202012Validator
from referencing import Registry' >/dev/null 2>&1
}

agentlas_is_runtime_python_shim() {
  local candidate="$1" resolved=""
  if [[ "$candidate" == */* ]]; then
    resolved="$candidate"
  else
    resolved="$(command -v "$candidate" 2>/dev/null || true)"
  fi
  [[ -n "$resolved" ]] || return 1
  case "$resolved" in
    "$HOME/.agentlas/runtime/"*/bin/python3|"$HOME/.agentlas/runtime/current/bin/python3") return 0 ;;
  esac
  return 1
}

agentlas_resolve_python_cmd() {
  if [[ -n "${HEPHAESTUS_PYTHON:-}" ]] \
    && ! agentlas_is_runtime_python_shim "$HEPHAESTUS_PYTHON" \
    && agentlas_python_candidate_ok "$HEPHAESTUS_PYTHON"; then
    printf '%s\n' "$HEPHAESTUS_PYTHON"
    return 0
  fi
  local candidate
  for candidate in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
    if [[ -x "$candidate" ]] \
      && ! agentlas_is_runtime_python_shim "$candidate" \
      && agentlas_python_candidate_ok "$candidate"; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  for candidate in python3 python; do
    if command -v "$candidate" >/dev/null 2>&1 \
      && ! agentlas_is_runtime_python_shim "$candidate" \
      && agentlas_python_candidate_ok "$candidate"; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  if command -v py >/dev/null 2>&1 \
    && agentlas_python_candidate_ok py -3; then
    printf '%s\n' 'py -3'
    return 0
  fi
  return 1
}

# ------------------------------------------------------------------ platform
#
# Native Windows (Git Bash / MSYS2) is not "POSIX with different slashes" for
# any of the three things this runtime does at a boundary:
#
#   1. The CLI hosts spawn stdio MCP children WITHOUT a shell, so an
#      extensionless shebang script is not an executable image. Windows needs
#      `cmd /c <runner>.cmd`.
#   2. A native python.exe cannot resolve a POSIX path like /c/Users/x, so any
#      path handed to it (PYTHONPATH, sys.path) has to be converted.
#   3. PYTHONPATH is semicolon-separated on Windows. Joining with ':' silently
#      produces one nonexistent entry, and the symptom is
#      `ModuleNotFoundError: No module named 'agentlas_cloud'`.
#
# These helpers live here because every launcher and every hook already sources
# this file, so there is exactly one place that answers the platform question.

agentlas_is_windows() {
  case "$(uname -s 2>/dev/null || true)" in
    MINGW*|MSYS*|CYGWIN*) return 0 ;;
    *) return 1 ;;
  esac
}

agentlas_path_sep() {
  if agentlas_is_windows; then printf ';'; else printf ':'; fi
}

# POSIX path -> the form the native interpreter understands. cygpath ships with
# Git for Windows; the manual branch covers a stripped MSYS. Anything that is
# not a /<drive>/... path is returned unchanged rather than mangled.
agentlas_native_path() {
  local path="${1:-}"
  [[ -n "$path" ]] || return 1
  if ! agentlas_is_windows; then
    printf '%s\n' "$path"
    return 0
  fi
  local converted=""
  if command -v cygpath >/dev/null 2>&1; then
    converted="$(cygpath -w "$path" 2>/dev/null || true)"
    if [[ -n "$converted" ]]; then
      printf '%s\n' "$converted"
      return 0
    fi
  fi
  if [[ "$path" =~ ^/([A-Za-z])/(.*)$ ]]; then
    local drive="${BASH_REMATCH[1]}" rest="${BASH_REMATCH[2]}"
    drive="$(printf '%s' "$drive" | tr '[:lower:]' '[:upper:]')"
    printf '%s:\\%s\n' "$drive" "${rest//\//\\}"
    return 0
  fi
  printf '%s\n' "$path"
}

# Export PYTHONPATH for a runtime root in native form with the native
# separator. Exported instead of printed so no caller can reintroduce ':'.
agentlas_export_pythonpath() {
  local root="${1:-}" native sep
  [[ -n "$root" ]] || return 1
  native="$(agentlas_native_path "$root")" || return 1
  sep="$(agentlas_path_sep)"
  if [[ -n "${PYTHONPATH:-}" ]]; then
    export PYTHONPATH="$native$sep$PYTHONPATH"
  else
    export PYTHONPATH="$native"
  fi
}

# The exact launch vector a host must record for the local Core MCP server.
# Prints one field per line: command, then each argument. `$1` is the
# extensionless runner path (its `.cmd` sibling is used on Windows).
agentlas_mcp_launch() {
  local runner="${1:-}"
  [[ -n "$runner" ]] || return 1
  if agentlas_is_windows; then
    printf '%s\n' "cmd" "/c" "$(agentlas_native_path "${runner}.cmd")" "mcp" "serve"
  else
    printf '%s\n' "$runner" "mcp" "serve"
  fi
}
