#!/bin/bash
# PyPI Publishing with 1Password Secret Management (Local-Only)
#
# WORKSPACE-WIDE POLICY: This script must ONLY run on local machines.
# CI/CD publishing is forbidden.
#
# Prerequisites:
#   - 1Password CLI installed (brew install 1password-cli)
#   - uv package manager installed (curl -LsSf https://astral.sh/uv/install.sh | sh)
#   - PyPI token stored in 1Password (Claude Automation vault, item: zdc7ap2ixpqgtpq62xm2davi7e)
#   - pyproject.toml with name and version fields
#
# Usage:
#   Called by: mise run release:pypi
#     git pull origin main
#     ./scripts/publish-to-pypi.sh

set -euo pipefail

# ============================================================================
# CONFIGURATION
# ============================================================================
# 1Password item ID for PyPI token
# "PyPI Token - terrylica-pypi-entire-account-token"
# Vault: Claude Automation (ggk4orq7rmcm7jinsb4ahygv7e) @ eonlabs.1password.com
OP_PYPI_ITEM="${OP_PYPI_ITEM:-zdc7ap2ixpqgtpq62xm2davi7e}"
OP_PYPI_VAULT="${OP_PYPI_VAULT:-ggk4orq7rmcm7jinsb4ahygv7e}"
PYPI_VERIFY_DELAY="${PYPI_VERIFY_DELAY:-3}"

# ============================================================================
# ENVIRONMENT DISCOVERY
# ============================================================================
# Discover how uv is installed before making assumptions.
# Supports: direct install, Homebrew, cargo, mise, asdf, or already in PATH.
#
# Non-interactive shells (like Claude Code) don't source shell configs,
# so we need to find tools explicitly.

discover_uv() {
    # Priority 1: Already in PATH (native install, Homebrew, or shell already configured)
    if command -v uv &> /dev/null; then
        echo "uv"
        return 0
    fi

    # Priority 2: Check common direct installation locations
    local uv_locations=(
        "$HOME/.local/bin/uv"             # Official curl installer
        "$HOME/.cargo/bin/uv"             # Cargo install
        "/opt/homebrew/bin/uv"            # macOS Homebrew (ARM)
        "/usr/local/bin/uv"               # macOS Homebrew (Intel) / Linux package manager
    )

    for uv_path in "${uv_locations[@]}"; do
        if [[ -x "$uv_path" ]]; then
            echo "$uv_path"
            return 0
        fi
    done

    # Priority 3: Try version managers (mise, asdf) as fallback
    # Only if uv not found directly - don't force any tool manager

    # Try mise
    local mise_locations=(
        "$HOME/.local/bin/mise"
        "/opt/homebrew/bin/mise"
        "/usr/local/bin/mise"
    )
    for mise_path in "${mise_locations[@]}"; do
        if [[ -x "$mise_path" ]]; then
            # Check if mise has uv available
            if "$mise_path" which uv &>/dev/null 2>&1; then
                echo "$mise_path exec -- uv"
                return 0
            fi
        fi
    done

    # Try asdf
    if [[ -f "$HOME/.asdf/asdf.sh" ]]; then
        # shellcheck source=/dev/null
        source "$HOME/.asdf/asdf.sh" 2>/dev/null || true
        if command -v uv &> /dev/null; then
            echo "uv"
            return 0
        fi
    fi

    # Not found
    return 1
}

# Discover uv installation method
UV_CMD=""
if UV_CMD=$(discover_uv); then
    : # Found
else
    echo ""
    echo "==============================================================="
    echo " ERROR: uv package manager not found"
    echo "==============================================================="
    echo ""
    echo "   Install uv using one of these methods:"
    echo ""
    echo "   # Official installer (recommended)"
    echo "   curl -LsSf https://astral.sh/uv/install.sh | sh"
    echo ""
    echo "   # Homebrew"
    echo "   brew install uv"
    echo ""
    echo "   # Cargo"
    echo "   cargo install uv"
    echo ""
    echo "   # mise"
    echo "   mise use uv@latest"
    echo ""
    exit 1
fi

# ============================================================================
# CI DETECTION GUARDS
# ============================================================================
# This script must ONLY run on local machines, NEVER in CI/CD pipelines.
# Rationale (ADR-0027):
#   - Security: No long-lived PyPI tokens in GitHub secrets
#   - Speed: 30 seconds locally vs 3-5 minutes in CI
#   - Control: Manual approval step before production release

detect_ci_environment() {
    local ci_detected=false
    local detected_vars=""

    if [[ -n "${CI:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - CI: ${CI}"
    fi
    if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - GITHUB_ACTIONS: ${GITHUB_ACTIONS}"
    fi
    if [[ -n "${GITLAB_CI:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - GITLAB_CI: ${GITLAB_CI}"
    fi
    if [[ -n "${JENKINS_URL:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - JENKINS_URL: ${JENKINS_URL}"
    fi
    if [[ -n "${CIRCLECI:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - CIRCLECI: ${CIRCLECI}"
    fi
    if [[ -n "${TRAVIS:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - TRAVIS: ${TRAVIS}"
    fi
    if [[ -n "${BUILDKITE:-}" ]]; then
        ci_detected=true
        detected_vars="${detected_vars}\n   - BUILDKITE: ${BUILDKITE}"
    fi

    if [[ "${ci_detected}" == "true" ]]; then
        echo ""
        echo "==============================================================="
        echo " ERROR: This script must ONLY be run on your LOCAL machine"
        echo "==============================================================="
        echo ""
        echo "   Detected CI environment variables:"
        echo -e "${detected_vars}"
        echo ""
        echo "   This project enforces LOCAL-ONLY PyPI publishing for:"
        echo "   - Security: No long-lived PyPI tokens in GitHub secrets"
        echo "   - Speed: 30 seconds locally vs 3-5 minutes in CI"
        echo "   - Control: Manual approval step before production release"
        echo ""
        echo "   See: docs/development/PUBLISHING.md (ADR-0027)"
        echo ""
        exit 1
    fi
}

# Run CI detection immediately
detect_ci_environment

# ============================================================================
# BRANCH VALIDATION GUARDS
# ============================================================================
# Publishing must only happen from main/master to ensure released code matches
# the version tagged by semantic-release.

CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "${CURRENT_BRANCH}" != "main" && "${CURRENT_BRANCH}" != "master" ]]; then
    echo ""
    echo "==============================================================="
    echo " ERROR: This script must ONLY be run on main/master branch"
    echo "==============================================================="
    echo ""
    echo "   Current branch: ${CURRENT_BRANCH}"
    echo "   Required branch: main or master"
    echo ""
    echo "   To publish, switch to main and pull latest:"
    echo "     git checkout main"
    echo "     git pull origin main"
    echo "     ./scripts/publish-to-pypi.sh"
    echo ""
    exit 1
fi

# ============================================================================
# MAIN PUBLISHING WORKFLOW
# ============================================================================

echo ""
echo "Publishing to PyPI (Local Workflow)"
echo "======================================================"

# Step 0: Verify 1Password token is available
echo -e "\n Step 0: Verifying 1Password credentials..."

if ! command -v op &> /dev/null; then
    echo "   ERROR: 1Password CLI not installed"
    echo "   Install: brew install 1password-cli"
    exit 1
fi

# Load service account token if available (headless/automation — no biometric prompt)
_OP_SA_TOKEN_FILE="$HOME/.claude/.secrets/op-service-account-token"
if [[ -f "$_OP_SA_TOKEN_FILE" ]]; then
    export OP_SERVICE_ACCOUNT_TOKEN
    OP_SERVICE_ACCOUNT_TOKEN="$(cat "$_OP_SA_TOKEN_FILE")"
fi

# Try to get PyPI token from 1Password (Claude Automation vault)
if ! PYPI_TOKEN=$(op item get "$OP_PYPI_ITEM" --vault "$OP_PYPI_VAULT" --fields credential --reveal 2>/dev/null); then
    echo "   ERROR: PyPI token not found in 1Password"
    echo "   Item: $OP_PYPI_ITEM"
    echo "   Vault: $OP_PYPI_VAULT (Claude Automation)"
    echo ""
    exit 1
fi
echo "   1Password token verified (${#PYPI_TOKEN} chars)"

# Step 1: Verify pyproject.toml exists
echo -e "\n Step 1: Reading package info from pyproject.toml..."

if [[ ! -f "pyproject.toml" ]]; then
    echo "   ERROR: pyproject.toml not found"
    echo "   This script must be run from the project root directory."
    exit 1
fi

# Extract package name from pyproject.toml
PACKAGE_NAME=$(grep '^name = ' pyproject.toml | sed 's/name = "\(.*\)"/\1/' | head -1)

if [[ -z "${PACKAGE_NAME}" ]]; then
    echo "   ERROR: Could not extract package name from pyproject.toml"
    exit 1
fi

# Extract version - check for dynamic version first (maturin pulls from Cargo.toml)
if grep -q 'dynamic = \["version"\]' pyproject.toml; then
    # SSoT: Cargo.toml [workspace.package] version is the source of truth
    if [[ -f "Cargo.toml" ]]; then
        CURRENT_VERSION=$(grep -A5 '\[workspace.package\]' Cargo.toml | grep '^version' | head -1 | sed 's/.*= "\(.*\)"/\1/')
        if [[ -z "${CURRENT_VERSION}" ]]; then
            # Fallback to [package] version
            CURRENT_VERSION=$(grep -A10 '\[package\]' Cargo.toml | grep -E '^version' | head -1 | sed 's/.*= "\(.*\)"/\1/')
        fi
    fi
else
    # Static version in pyproject.toml
    CURRENT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/' | head -1)
fi

if [[ -z "${CURRENT_VERSION}" ]]; then
    echo "   ERROR: Could not extract version from pyproject.toml or Cargo.toml"
    exit 1
fi

echo "   Package: ${PACKAGE_NAME}"
echo "   Version: v${CURRENT_VERSION}"

# Step 2: Check for existing wheels or build
echo -e "\n Step 2: Checking for pre-built wheels..."

# Check if wheels for current version exist in dist/
WHEEL_COUNT=$(find dist/ -name "${PACKAGE_NAME}-${CURRENT_VERSION}-*.whl" 2>/dev/null | wc -l | tr -d ' ')

if [[ "${WHEEL_COUNT}" -gt 0 ]]; then
    echo "   Found ${WHEEL_COUNT} pre-built wheel(s) for v${CURRENT_VERSION}"
    find dist/ -name "${PACKAGE_NAME}-${CURRENT_VERSION}-*.whl" -print0 2>/dev/null | while IFS= read -r -d '' f; do echo "     - $(basename "$f")"; done
    echo "   Skipping build (use pre-built wheels from release workflow)"
else
    # No wheels found - build with uv (for pure Python projects)
    # For maturin projects, run 'mise run release:build-all' first
    echo "   No pre-built wheels found"

    # Check if this is a maturin project
    if grep -q '\[tool.maturin\]' pyproject.toml; then
        echo ""
        echo "   ERROR: This is a maturin project - wheels must be pre-built"
        echo "   Run 'mise run release:build-all' first to build platform wheels"
        exit 1
    fi

    echo -e "\n Step 3: Building package..."
    echo "   Using: $UV_CMD"
    rm -rf dist/ build/ 2>/dev/null || true
    find . -maxdepth 1 -name "*.egg-info" -type d -exec rm -rf {} + 2>/dev/null || true
    $UV_CMD build 2>&1 | grep -E "(Building|Successfully built)" || $UV_CMD build
    echo "   Built: dist/${PACKAGE_NAME}-${CURRENT_VERSION}*"
fi

# Step 3: Pre-flight — verify PyPI is reachable before uploading (Issue #121)
echo -e "\n Step 3: Pre-flight PyPI connectivity check..."
if timeout 5 curl -sf "https://pypi.org/pypi/${PACKAGE_NAME}/json" >/dev/null 2>&1; then
    echo "   OK: PyPI API reachable"
else
    echo "   WARN: PyPI API unreachable (network issue?)"
    echo "   Continuing anyway — upload may fail"
fi

# Check if version already exists on PyPI (idempotent — skip if already published)
PYPI_EXISTS=false
if timeout 5 curl -sf "https://pypi.org/pypi/${PACKAGE_NAME}/${CURRENT_VERSION}/json" | grep -q "\"version\":" 2>/dev/null; then
    PYPI_EXISTS=true
    echo "   INFO: ${PACKAGE_NAME}==${CURRENT_VERSION} already on PyPI — skipping upload"
fi

# Step 4: Publish to PyPI using twine (skip if already published)
if [ "$PYPI_EXISTS" = "false" ]; then
    echo -e "\n Step 4: Publishing to PyPI..."
    echo "   Using PyPI token from 1Password"

    # Use twine for maturin projects (uv publish has issues with pre-built wheels)
    if grep -q '\[tool.maturin\]' pyproject.toml; then
        # Publish all wheels and sdist for current version
        # --skip-existing handles race conditions where another process uploaded first
        TWINE_USERNAME=__token__ TWINE_PASSWORD="${PYPI_TOKEN}" twine upload \
            --skip-existing \
            "dist/${PACKAGE_NAME}-${CURRENT_VERSION}"-*.whl \
            "target/wheels/${PACKAGE_NAME}-${CURRENT_VERSION}.tar.gz" 2>&1 || \
        TWINE_USERNAME=__token__ TWINE_PASSWORD="${PYPI_TOKEN}" twine upload \
            --skip-existing \
            "dist/${PACKAGE_NAME}-${CURRENT_VERSION}"-*.whl
    else
        # Use UV_PUBLISH_TOKEN environment variable for security (no token in process list)
        UV_PUBLISH_TOKEN="${PYPI_TOKEN}" $UV_CMD publish 2>&1 | grep -E "(Uploading|succeeded|Failed)" || \
            UV_PUBLISH_TOKEN="${PYPI_TOKEN}" $UV_CMD publish
    fi

    echo "   Published to PyPI"
else
    echo -e "\n Step 4: Skipped (already published)"
fi

# Step 5: Verify publication on PyPI (poll with retries)
echo -e "\n Step 5: Verifying on PyPI..."
PYPI_VERIFIED=false
for attempt in 1 2 3; do
    sleep "$PYPI_VERIFY_DELAY"
    if curl -sf "https://pypi.org/pypi/${PACKAGE_NAME}/${CURRENT_VERSION}/json" | grep -q "\"version\":"; then
        PYPI_VERIFIED=true
        echo "   Verified: https://pypi.org/project/${PACKAGE_NAME}/${CURRENT_VERSION}/"
        break
    fi
    echo "   Still propagating (attempt $attempt/3)..."
done
if [ "$PYPI_VERIFIED" = "false" ]; then
    echo "   CDN propagation slow — check in 30s: https://pypi.org/project/${PACKAGE_NAME}/${CURRENT_VERSION}/"
fi

echo -e "\n Complete! Published ${PACKAGE_NAME} v${CURRENT_VERSION} to PyPI"
echo ""
echo "Next steps:"
echo "  - Verify package is installable: pip install ${PACKAGE_NAME}==${CURRENT_VERSION}"
echo "  - Check PyPI page: https://pypi.org/project/${PACKAGE_NAME}/"
echo "  - Monitor downloads: https://pypistats.org/packages/${PACKAGE_NAME}"
