#!/usr/bin/env bash
# Dev installation: editable install of all local wexample packages from sources.
#
# Unlike bin/install (which wipes the venv and pulls every dep from PyPI), this
# script keeps the venv as-is and overlays local sources in editable mode using
# --no-deps so that already-installed third-party deps are not disturbed.
#
# Run this:
#   - to repair a broken venv where some local packages are missing
#   - after pulling new code, to make sure your editable installs are up to date

set -e

WEX_DIR_CLI="$(realpath "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/"
WEX_DIR_ROOT=$(realpath "${WEX_DIR_CLI}../")/

VENV_PIP="${WEX_DIR_ROOT}.venv/bin/pip"
VENV_PYTHON="${WEX_DIR_ROOT}.venv/bin/python"

# Source .wex/.env to get PROGRAM_PUBLICATION_SOURCE_LIBRARY_PATH
# shellcheck disable=SC1091
. "${WEX_DIR_ROOT}.wex/.env"

if [ -z "${PROGRAM_PUBLICATION_SOURCE_LIBRARY_PATH}" ]; then
    echo "ERROR: PROGRAM_PUBLICATION_SOURCE_LIBRARY_PATH not set in .wex/.env" >&2
    exit 1
fi

LIB_PATH="${PROGRAM_PUBLICATION_SOURCE_LIBRARY_PATH%/}"

if [ ! -d "${LIB_PATH}" ]; then
    echo "ERROR: source library path does not exist: ${LIB_PATH}" >&2
    exit 1
fi

if [ ! -x "${VENV_PYTHON}" ]; then
    echo "ERROR: venv missing at ${WEX_DIR_ROOT}.venv — run bin/install first" >&2
    exit 1
fi

# Make sure third-party deps from requirements.txt are present (no-op if already
# installed). This catches the case where the venv is missing transitive deps
# after a botched install.
echo "Ensuring third-party deps from requirements.txt..."
"${VENV_PIP}" install -q -r "${WEX_DIR_ROOT}requirements.txt"

echo "Discovering local packages in ${LIB_PATH}..."
# Packages live exactly 3 levels deep: <LIB_PATH>/{packages,wex}/<name>/pyproject.toml
mapfile -t PKG_DIRS < <(find "${LIB_PATH}" -mindepth 3 -maxdepth 3 -name pyproject.toml \
    -not -path "*/.venv/*" -not -path "*/.pdm-build/*" -not -path "*/.wex/*" \
    -exec dirname {} \; | sort)

if [ "${#PKG_DIRS[@]}" -eq 0 ]; then
    echo "ERROR: no packages found under ${LIB_PATH}" >&2
    exit 1
fi

echo "Installing ${#PKG_DIRS[@]} local packages in editable mode (--no-deps)..."
for pkg_dir in "${PKG_DIRS[@]}"; do
    echo "  -e ${pkg_dir}"
    "${VENV_PIP}" install --no-cache-dir -q -e "${pkg_dir}" --no-deps
done

# `--no-deps` above is what keeps the editable overlay from disturbing the pinned
# third-party versions, but it also means a dependency a local package gained since
# requirements.txt was last compiled is never installed — and requirements.txt only
# covers wex's own dependency closure, so a suite package nobody in that chain
# depends on (orm, queue) never contributes its requirements at all. `pip check`
# names exactly what is missing; reinstalling the offenders *with* their deps heals
# it, and leaves the 42 sound packages untouched.
echo "Checking for unmet requirements..."
CHECK_OUTPUT="$("${VENV_PIP}" check 2>&1 || true)"

if [ -n "${CHECK_OUTPUT}" ] && [ "${CHECK_OUTPUT}" != "No broken requirements found." ]; then
    echo "${CHECK_OUTPUT}"

    # "wexample-orm 1.3.3 requires sqlalchemy, which is not installed." -> wexample-orm
    mapfile -t BROKEN < <(echo "${CHECK_OUTPUT}" | awk '{print $1}' | sort -u)

    BROKEN_DIRS=()
    for dist in "${BROKEN[@]}"; do
        # Distribution name -> source directory, by reading each package's own metadata.
        for pkg_dir in "${PKG_DIRS[@]}"; do
            pkg_name="$("${VENV_PYTHON}" - "${pkg_dir}" <<'EOF'
import sys, tomllib
from pathlib import Path
data = tomllib.loads((Path(sys.argv[1]) / "pyproject.toml").read_text(encoding="utf-8"))
print(data.get("project", {}).get("name", "").lower().replace("_", "-"))
EOF
)"
            if [ "${pkg_name}" = "${dist,,}" ]; then
                BROKEN_DIRS+=("${pkg_dir}")
                break
            fi
        done
    done

    if [ "${#BROKEN_DIRS[@]}" -gt 0 ]; then
        echo "Reinstalling ${#BROKEN_DIRS[@]} package(s) with their dependencies..."
        for pkg_dir in "${BROKEN_DIRS[@]}"; do
            echo "  -e ${pkg_dir} (with deps)"
            "${VENV_PIP}" install --no-cache-dir -q -e "${pkg_dir}"
        done
    fi

    # Anything still unmet is a dependency no local package can supply: say so and
    # fail, rather than let a later command die on a ModuleNotFoundError.
    if ! "${VENV_PIP}" check; then
        echo "ERROR: requirements above are still unmet — install them by hand." >&2
        exit 1
    fi
fi

# Install shell prompt handler directly in the current user's bashrc (no sudo).
chmod +x "${WEX_DIR_CLI}terminal-handler"
SOURCE_LINE=". ${WEX_DIR_CLI}terminal-handler"
BASHRC="${HOME}/.bashrc"
if [ -f "${BASHRC}" ] && ! grep -qxF "${SOURCE_LINE}" "${BASHRC}"; then
    echo "${SOURCE_LINE}" >> "${BASHRC}"
    echo "Added shell prompt handler to ${BASHRC} (open a new terminal to see it)."
fi

echo "Done."
