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

usage() {
  cat <<'USAGE'
Usage: ./pip-publish [options]

Build, check, and upload the current Python package with build + twine.
Run this from the project directory containing pyproject.toml.

Options:
  --testpypi            Upload to the testpypi repository configured in ~/.pypirc.
  --repository NAME     Upload to a named repository configured in ~/.pypirc.
  --repository-url URL  Upload to an explicit repository URL.
  --dry-run             Build and twine-check only; do not upload.
  --skip-clean          Do not remove dist/, build/, or *.egg-info before building.
  --no-install-deps     Do not install/upgrade build and twine first.
  -h, --help            Show this help.

Credentials:
  Twine will use TWINE_USERNAME/TWINE_PASSWORD, ~/.pypirc, keyring, or prompts.
  For PyPI API tokens, the username must be __token__ and the password is the
  token value, including the pypi- prefix.

Convenience:
  If PYPI_TOKEN is set and TWINE_PASSWORD is not, this script exports:
      TWINE_USERNAME=__token__
      TWINE_PASSWORD=$PYPI_TOKEN

  If --testpypi is used, TEST_PYPI_TOKEN is preferred when set.
USAGE
}

REPOSITORY="pypi"
REPOSITORY_URL=""
DRY_RUN=0
CLEAN=1
INSTALL_DEPS=1

while [[ $# -gt 0 ]]; do
  case "$1" in
    --testpypi)
      REPOSITORY="testpypi"
      shift
      ;;
    --repository)
      [[ $# -ge 2 ]] || { echo "Error: --repository requires a value" >&2; exit 2; }
      REPOSITORY="$2"
      shift 2
      ;;
    --repository-url)
      [[ $# -ge 2 ]] || { echo "Error: --repository-url requires a value" >&2; exit 2; }
      REPOSITORY_URL="$2"
      shift 2
      ;;
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    --skip-clean)
      CLEAN=0
      shift
      ;;
    --no-install-deps)
      INSTALL_DEPS=0
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Error: unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

if [[ ! -f pyproject.toml ]]; then
  echo "Error: pyproject.toml not found. Run this script from your package root." >&2
  exit 1
fi

PYTHON_BIN="${PYTHON:-python3}"

if [[ "$INSTALL_DEPS" -eq 1 ]]; then
  "$PYTHON_BIN" -m pip install --upgrade pip build twine
else
  missing_modules=()
  for module in build twine; do
    if ! "$PYTHON_BIN" - <<'PYMODULE' "$module" >/dev/null 2>&1
import importlib.util
import sys
module_name = sys.argv[1]
raise SystemExit(0 if importlib.util.find_spec(module_name) else 1)
PYMODULE
    then
      missing_modules+=("$module")
    fi
  done

  if [[ "${#missing_modules[@]}" -gt 0 ]]; then
    echo "Error: missing Python module(s): ${missing_modules[*]}" >&2
    echo "Fix: rerun without --no-install-deps, or install them first:" >&2
    echo "     $PYTHON_BIN -m pip install --upgrade build twine" >&2
    exit 1
  fi
fi

if [[ "$CLEAN" -eq 1 ]]; then
  rm -rf dist build
  find . -path '*/src/*.egg-info' -type d -prune -exec rm -rf {} +
  find . -maxdepth 1 -name '*.egg-info' -type d -prune -exec rm -rf {} +
fi

# Convenience token mapping. Explicit TWINE_* variables always win.
if [[ -z "${TWINE_PASSWORD:-}" ]]; then
  if [[ "$REPOSITORY" == "testpypi" && -n "${TEST_PYPI_TOKEN:-}" ]]; then
    export TWINE_USERNAME="${TWINE_USERNAME:-__token__}"
    export TWINE_PASSWORD="$TEST_PYPI_TOKEN"
  elif [[ -n "${PYPI_TOKEN:-}" ]]; then
    export TWINE_USERNAME="${TWINE_USERNAME:-__token__}"
    export TWINE_PASSWORD="$PYPI_TOKEN"
  fi
fi

"$PYTHON_BIN" -m build
"$PYTHON_BIN" -m twine check dist/*

if [[ "$DRY_RUN" -eq 1 ]]; then
  echo "Dry run complete: built distributions passed twine check. No upload performed."
  exit 0
fi

UPLOAD_ARGS=(upload)
if [[ -n "$REPOSITORY_URL" ]]; then
  UPLOAD_ARGS+=(--repository-url "$REPOSITORY_URL")
elif [[ -n "$REPOSITORY" ]]; then
  UPLOAD_ARGS+=(--repository "$REPOSITORY")
fi
UPLOAD_ARGS+=(dist/*)

"$PYTHON_BIN" -m twine "${UPLOAD_ARGS[@]}"
