#!/usr/bin/env bash
# ============================================================================
# release.sh — Automated release & deploy for neng-temp-logger
#
# Usage:
#   ./release.sh patch          # 0.1.1 → 0.1.2  (bug-fix)
#   ./release.sh minor          # 0.1.1 → 0.2.0  (new feature)
#   ./release.sh major          # 0.1.1 → 1.0.0  (breaking change)
#   ./release.sh patch --pypi   # … and also upload to PyPI
#   ./release.sh patch --dry-run # simulate (no git push, no upload)
#
# What it does (in order):
#   1. Check the working tree is clean
#   2. Bump version in src/temp_logger/__init__.py  (single source of truth)
#   3. Update CHANGELOG.md [Unreleased] header → new version
#   4. git commit + git tag  vX.Y.Z
#   5. python -m build  (sdist + wheel)
#   6. Upload to GitLab Package Registry (always)
#   7. Upload to PyPI (only with --pypi flag)
#   8. git push + git push --tags
#
# Prerequisites:
#   pip install build twine
#
# Configuration (environment variables):
#   GITLAB_HOST       — default: gitlab.flavio.be
#   GITLAB_PROJECT_ID — numeric project ID (see Settings → General on GitLab)
#   GITLAB_TOKEN      — personal or deploy token with api + write_repository scope
#   TWINE_USERNAME    — PyPI username  (default: __token__)
#   TWINE_PASSWORD    — PyPI API token (only for --pypi)
#
# Tip: store tokens in ~/.config/temp-logger/release.env (sourced automatically)
# ============================================================================
set -euo pipefail

# ── Colour helpers ──────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'

info()  { printf "${CYAN}ℹ ${RESET}%s\n" "$*"; }
ok()    { printf "${GREEN}✔ ${RESET}%s\n" "$*"; }
warn()  { printf "${YELLOW}⚠ ${RESET}%s\n" "$*"; }
die()   { printf "${RED}✖ ${RESET}%s\n" "$*" >&2; exit 1; }

# ── Parse arguments ─────────────────────────────────────────────────────────
BUMP=""
PYPI=false
DRY_RUN=false

for arg in "$@"; do
    case "$arg" in
        patch|minor|major) BUMP="$arg" ;;
        --pypi)            PYPI=true   ;;
        --dry-run)         DRY_RUN=true ;;
        -h|--help)
            sed -n '2,/^# ====/{ /^# ====/d; s/^# \?//; p; }' "$0"
            exit 0 ;;
        *) die "Unknown argument: $arg (use patch|minor|major [--pypi] [--dry-run])" ;;
    esac
done

[[ -z "$BUMP" ]] && die "Usage: $0 <patch|minor|major> [--pypi] [--dry-run]"

# ── Locate project root (directory containing pyproject.toml) ───────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$SCRIPT_DIR"
[[ -f "$PROJECT_ROOT/pyproject.toml" ]] || die "pyproject.toml not found in $PROJECT_ROOT"
cd "$PROJECT_ROOT"

# ── Load optional env file ──────────────────────────────────────────────────
ENV_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/temp-logger/release.env"
if [[ -f "$ENV_FILE" ]]; then
    info "Loading tokens from $ENV_FILE"
    # shellcheck disable=SC1090
    source "$ENV_FILE"
fi

# ── Configuration defaults ──────────────────────────────────────────────────
GITLAB_HOST="${GITLAB_HOST:-gitlab.flavio.be}"
GITLAB_PROJECT_ID="${GITLAB_PROJECT_ID:-}"
GITLAB_TOKEN="${GITLAB_TOKEN:-}"
TWINE_USERNAME="${TWINE_USERNAME:-__token__}"
# TWINE_PASSWORD must be set externally for PyPI

# ── Preflight checks ───────────────────────────────────────────────────────
command -v python3 >/dev/null || die "python3 not found"
python3 -c "import build"   2>/dev/null || die "'build' not installed (pip install build)"
python3 -c "import twine"   2>/dev/null || die "'twine' not installed (pip install twine)"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "Not a git repository"

if ! $DRY_RUN; then
    if [[ -n "$(git status --porcelain)" ]]; then
        die "Working tree is not clean. Commit or stash changes first."
    fi
fi

# ── Read current version ───────────────────────────────────────────────────
VERSION_FILE="src/temp_logger/__init__.py"
CURRENT=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' "$VERSION_FILE")
[[ -n "$CURRENT" ]] || die "Cannot read __version__ from $VERSION_FILE"

IFS='.' read -r V_MAJOR V_MINOR V_PATCH <<< "$CURRENT"

case "$BUMP" in
    patch) V_PATCH=$((V_PATCH + 1)) ;;
    minor) V_MINOR=$((V_MINOR + 1)); V_PATCH=0 ;;
    major) V_MAJOR=$((V_MAJOR + 1)); V_MINOR=0; V_PATCH=0 ;;
esac

NEW_VERSION="${V_MAJOR}.${V_MINOR}.${V_PATCH}"
TAG="v${NEW_VERSION}"
TODAY=$(date +%Y-%m-%d)

echo ""
printf "${BOLD}Release plan${RESET}\n"
printf "  Version : ${YELLOW}%s${RESET} → ${GREEN}%s${RESET}\n" "$CURRENT" "$NEW_VERSION"
printf "  Tag     : ${GREEN}%s${RESET}\n" "$TAG"
printf "  GitLab  : ${CYAN}%s${RESET}\n" "https://${GITLAB_HOST}/flavio/temp-logger"
if $PYPI; then
    printf "  PyPI    : ${CYAN}yes${RESET}\n"
else
    printf "  PyPI    : ${YELLOW}no${RESET}  (add --pypi to upload)\n"
fi
if $DRY_RUN; then
    printf "  Mode    : ${YELLOW}DRY RUN${RESET}\n"
fi
echo ""

# Confirm
if ! $DRY_RUN; then
    read -rp "Proceed? [y/N] " confirm
    [[ "$confirm" =~ ^[Yy]$ ]] || { info "Aborted."; exit 0; }
fi

# ── Step 1: Bump version in __init__.py ─────────────────────────────────────
info "Bumping version → ${NEW_VERSION}"
if ! $DRY_RUN; then
    sed -i.bak "s/__version__ = \".*\"/__version__ = \"${NEW_VERSION}\"/" "$VERSION_FILE"
    rm -f "${VERSION_FILE}.bak"
    ok "Updated $VERSION_FILE"
else
    info "[DRY RUN] Would update $VERSION_FILE"
fi

# ── Step 2: Update CHANGELOG.md ────────────────────────────────────────────
CHANGELOG="CHANGELOG.md"
if [[ -f "$CHANGELOG" ]]; then
    if $DRY_RUN; then
        info "[DRY RUN] Would update $CHANGELOG"
    elif grep -q '## \[Unreleased\]' "$CHANGELOG"; then
        sed -i.bak "s/## \[Unreleased\]/## [Unreleased]\n\n## [${NEW_VERSION}] — ${TODAY}/" "$CHANGELOG"
        rm -f "${CHANGELOG}.bak"

        # Update the bottom links
        # Add new Unreleased comparison link
        if grep -q "\[Unreleased\]:" "$CHANGELOG"; then
            sed -i.bak "s|\[Unreleased\]:.*|[Unreleased]: https://${GITLAB_HOST}/flavio/temp-logger/-/compare/${TAG}...HEAD|" "$CHANGELOG"
            rm -f "${CHANGELOG}.bak"
        fi
        # Insert new version link before the first [x.y.z]: link
        PREV_TAG="v${CURRENT}"
        NEW_LINK="[${NEW_VERSION}]: https://${GITLAB_HOST}/flavio/temp-logger/-/compare/${PREV_TAG}...${TAG}"
        if ! grep -qF "[${NEW_VERSION}]:" "$CHANGELOG"; then
            # Insert after the [Unreleased] link
            sed -i.bak "/^\[Unreleased\]:/a\\
${NEW_LINK}" "$CHANGELOG"
            rm -f "${CHANGELOG}.bak"
        fi
        ok "Updated $CHANGELOG"
    else
        warn "No [Unreleased] section found in $CHANGELOG — skipping."
    fi
else
    warn "No CHANGELOG.md found — skipping."
fi

# ── Step 3: Git commit & tag ───────────────────────────────────────────────
if $DRY_RUN; then
    info "[DRY RUN] Would commit and tag as ${TAG}"
else
    git add "$VERSION_FILE" "$CHANGELOG" 2>/dev/null || true
    git commit -m "release: ${TAG}

Bump version ${CURRENT} → ${NEW_VERSION}."
    git tag -a "$TAG" -m "Release ${NEW_VERSION}"
    ok "Committed and tagged ${TAG}"
fi

# ── Step 4: Build sdist + wheel ─────────────────────────────────────────────
info "Building package…"
rm -rf dist/ build/
python3 -m build --sdist --wheel .
ok "Built dist/"
ls -lh dist/

# ── Step 5: Upload to GitLab Package Registry ──────────────────────────────
if [[ -z "$GITLAB_PROJECT_ID" ]]; then
    warn "GITLAB_PROJECT_ID not set — skipping GitLab upload."
    warn "Set it in $ENV_FILE or export GITLAB_PROJECT_ID=<id>"
elif [[ -z "$GITLAB_TOKEN" ]]; then
    warn "GITLAB_TOKEN not set — skipping GitLab upload."
    warn "Set it in $ENV_FILE or export GITLAB_TOKEN=<token>"
elif $DRY_RUN; then
    info "[DRY RUN] Would upload to GitLab (project ${GITLAB_PROJECT_ID})"
else
    info "Uploading to GitLab Package Registry…"
    TWINE_USERNAME="__token__" \
    TWINE_PASSWORD="$GITLAB_TOKEN" \
    python3 -m twine upload \
        --repository-url "https://${GITLAB_HOST}/api/v4/projects/${GITLAB_PROJECT_ID}/packages/pypi" \
        dist/*
    ok "Uploaded to GitLab ✓"
fi

# ── Step 6 (optional): Upload to PyPI ──────────────────────────────────────
if $PYPI; then
    if [[ -z "${TWINE_PASSWORD:-}" ]]; then
        die "TWINE_PASSWORD (PyPI API token) is not set."
    fi
    if $DRY_RUN; then
        info "[DRY RUN] Would upload to PyPI"
    else
        info "Uploading to PyPI…"
        python3 -m twine upload dist/*
        ok "Uploaded to PyPI ✓"
    fi
fi

# ── Step 7: Push to remote ─────────────────────────────────────────────────
if $DRY_RUN; then
    info "[DRY RUN] Would push to origin (main + tags)"
else
    info "Pushing to origin…"
    git push origin main
    git push origin "$TAG"
    ok "Pushed ${TAG} to origin"
fi

# ── Done ────────────────────────────────────────────────────────────────────
echo ""
printf "${GREEN}${BOLD}🎉 Release ${TAG} complete!${RESET}\n"
echo ""
printf "  GitLab : https://${GITLAB_HOST}/flavio/temp-logger/-/releases/${TAG}\n"
if $PYPI; then
    printf "  PyPI   : https://pypi.org/project/neng-temp-logger/${NEW_VERSION}/\n"
fi
printf "  Install: pip install neng-temp-logger==${NEW_VERSION}\n"
echo ""
