#!/usr/bin/env bash
# Single-file bootstrap + run for novelai_preset_optimize.py (Debian-like).
# Double-click friendly: reopens in a terminal if needed.
# All deps stay in this file (no requirements.txt).
set -euo pipefail

ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || realpath "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}")")" && pwd)"
cd "$ROOT"

# NLTK 3.10.1+ inisec blocks imports whose files live under CWD. With the venv at
# $ROOT/.venv that wrongly blocks site-packages (regex, etc.) when we cd to $ROOT.
# Disable only this hook; PYTHONSAFEPATH/-P does not help (path is still under CWD).
export NLTK_DISABLE_IMPORT_SECURITY=1

# Re-open in a terminal when launched from a GUI file manager
if [[ -z "${RUN_OPTIMIZE_IN_TERM:-}" ]] && { [[ ! -t 1 ]] || [[ -z "${TERM:-}" ]]; }; then
  export RUN_OPTIMIZE_IN_TERM=1
  if command -v gnome-terminal >/dev/null 2>&1; then
    exec gnome-terminal --working-directory="$ROOT" -- bash "$ROOT/run.sh" "$@"
  elif command -v xfce4-terminal >/dev/null 2>&1; then
    exec xfce4-terminal --working-directory="$ROOT" -e "bash \"$ROOT/run.sh\""
  elif command -v konsole >/dev/null 2>&1; then
    exec konsole --workdir "$ROOT" -e bash "$ROOT/run.sh" "$@"
  elif command -v x-terminal-emulator >/dev/null 2>&1; then
    exec x-terminal-emulator -e bash -lc "cd \"$ROOT\" && exec bash \"$ROOT/run.sh\" $*"
  elif command -v xterm >/dev/null 2>&1; then
    exec xterm -e bash -lc "cd \"$ROOT\" && exec bash \"$ROOT/run.sh\" $*"
  fi
fi

hold() {
  local code=${1:-0}
  echo
  if [[ $code -ne 0 ]]; then
    echo "FAILED (exit $code)."
  else
    echo "Done."
  fi
  if [[ -n "${RUN_OPTIMIZE_IN_TERM:-}" ]] || [[ ! -t 0 ]]; then
    read -r -p "Press Enter to close…" _ || true
  fi
  exit "$code"
}
trap 'hold $?' EXIT

echo "=== NovelAI preset optimize ==="
echo "Project: $ROOT"
echo

for f in exam.txt exam.toml optimize_fin.py stylo.py .env; do
  if [[ ! -f "$ROOT/$f" ]]; then
    echo "MISSING: $f (must sit next to this script)"
    exit 1
  fi
done

# System packages (git required by optunahub/GitPython to fetch AutoSampler)
need_apt=()
command -v python3 >/dev/null 2>&1 || need_apt+=(python3)
python3 -m venv -h >/dev/null 2>&1 || need_apt+=(python3-venv)
python3 -c "import ensurepip" 2>/dev/null || need_apt+=(python3-venv)
command -v pip3 >/dev/null 2>&1 || python3 -m pip -h >/dev/null 2>&1 || need_apt+=(python3-pip)
command -v git >/dev/null 2>&1 || need_apt+=(git)

if [[ ${#need_apt[@]} -gt 0 ]]; then
  echo "Installing system packages: ${need_apt[*]}"
  if ! command -v apt-get >/dev/null 2>&1; then
    echo "apt-get not found; install Python 3 + venv + git manually."
    exit 1
  fi
  if [[ "$(id -u)" -eq 0 ]]; then
    apt-get update -y
    DEBIAN_FRONTEND=noninteractive apt-get install -y "${need_apt[@]}"
  elif command -v sudo >/dev/null 2>&1; then
    sudo apt-get update -y
    sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${need_apt[@]}"
  else
    echo "Need root for: ${need_apt[*]}"
    exit 1
  fi
fi

if ! command -v git >/dev/null 2>&1; then
  echo "git is required (optunahub loads AutoSampler from GitHub)."
  echo "Install with: sudo apt-get install -y git"
  exit 1
fi

VENV="$ROOT/.venv"
MARKER="$VENV/.deps_ok"
# Bump when install recipe changes (forces reinstall path).
DEPS_STAMP="5"

if [[ ! -d "$VENV" ]]; then
  echo "Creating virtualenv at $VENV"
  python3 -m venv "$VENV"
fi
# shellcheck disable=SC1091
source "$VENV/bin/activate"

# Cheap: find_spec only (does not load spaCy model)
light_ok() {
  python -c "
import importlib.util as u
mods = 'optuna optunahub cmaes scipy torch spacy textdescriptives openai tokenizers numpy dotenv textstat lexicalrichness nrclex requests lexical_diversity'.split()
missing = [m for m in mods if u.find_spec(m) is None]
raise SystemExit(1 if missing else 0)
" >/dev/null 2>&1
}

spacy_ok() {
  python -c "import spacy; spacy.load('en_core_web_sm')" >/dev/null 2>&1
}

# Happy path: marker + light check → start app (one process, no heavy imports here)
if [[ -f "$MARKER" ]] && [[ "$(tr -d '[:space:]' <"$MARKER" 2>/dev/null || true)" == "$DEPS_STAMP" ]] && light_ok; then
  echo "Dependencies ready — starting."
else
  echo "Checking dependencies..."
  python -c "
import importlib.util as u
mods = 'optuna optunahub cmaes scipy torch spacy textdescriptives openai tokenizers numpy dotenv textstat lexicalrichness nrclex requests lexical_diversity'.split()
miss = [m for m in mods if u.find_spec(m) is None]
print('missing packages:', ', '.join(miss) if miss else '(none)')
raise SystemExit(1 if miss else 0)
" && NEED_PIP=0 || NEED_PIP=1

  if spacy_ok; then
    NEED_SPACY=0
  else
    NEED_SPACY=1
    echo "missing spaCy model: en_core_web_sm"
  fi

  if [[ "$NEED_PIP" -eq 0 && "$NEED_SPACY" -eq 0 ]]; then
    echo "All deps present — writing marker."
    printf '%s\n' "$DEPS_STAMP" >"$MARKER"
  else
    if [[ "$NEED_PIP" -ne 0 ]]; then
      echo "Upgrading pip…"
      python -m pip install --upgrade pip "setuptools>=69,<81" wheel

      # Never build numpy from source: on Debian/py3.13 an old sdist can be
      # selected and GCC may ICE (Bus error in nditer_constr.c). Prefer a
      # modern wheel-only numpy first, then the rest with binary preferred.
      echo "Installing numpy (binary wheel only)…"
      python -m pip install --only-binary=:all: "numpy>=2.1,<3"

      echo "Installing Python deps (first run / repair)…"
      # textstat → nltk → regex; pin nltk so a future inisec change cannot
      # re-break validation when the venv lives under the project root.
      python -m pip install --prefer-binary \
        "optuna>=4.0" \
        optunahub \
        cmaes \
        scipy \
        python-dotenv \
        textstat \
        "nltk>=3.9,<3.10.1" \
        regex \
        lexicalrichness \
        "lexical-diversity" \
        nrclex \
        spacy \
        textdescriptives \
        openai \
        "tokenizers>=0.20" \
        requests \
        "numpy>=2.1,<3"

      # AutoSampler (optunahub) needs torch; CPU wheel is enough.
      echo "Installing torch (CPU) for AutoSampler…"
      python -m pip install --prefer-binary torch \
        --index-url https://download.pytorch.org/whl/cpu

      # setuptools 81+ drops pkg_resources (needed by lexical-diversity)
      python -m pip install "setuptools>=69,<81" >/dev/null
    fi

    if [[ "$NEED_SPACY" -ne 0 ]] || ! spacy_ok; then
      echo "Ensuring spaCy en_core_web_sm model…"
      if ! spacy_ok; then
        python -m spacy download en_core_web_sm
      else
        echo "spaCy model already installed."
      fi
    fi

    echo "Validating install…"
    python -c "import optuna, optunahub, cmaes, scipy, torch, spacy, textdescriptives, openai, tokenizers, numpy, dotenv, textstat, lexicalrichness, nrclex, requests; from lexical_diversity import lex_div; spacy.load('en_core_web_sm'); optunahub.load_module('samplers/auto_sampler').AutoSampler"
    printf '%s\n' "$DEPS_STAMP" >"$MARKER"
    echo "Install OK."
  fi
fi

echo
echo "Starting optimize_fin.py (Ctrl+C to stop)…"
echo
python "$ROOT/optimize_fin.py"
