#!/bin/bash

# FunASR Python Client Release Script
# Automated version release process

set -euo pipefail  # Exit on error, undefined variables, and pipe failures

# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Logging functions
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# Check if command exists
check_command() {
    if ! command -v "$1" &> /dev/null; then
        log_error "Command '$1' not found, please install it first"
        exit 1
    fi
}

# Check working directory
check_working_directory() {
    if [[ ! -f "pyproject.toml" ]]; then
        log_error "Please run this script from the project root directory"
        exit 1
    fi
}

# Check Git status (adapted for local-only repository)
check_git_status() {
    # Check if we're in a Git repository
    if ! git rev-parse --is-inside-work-tree &> /dev/null; then
        log_error "Not in a Git repository, please initialize Git first"
        exit 1
    fi

    # Check current branch (local repository, no strict main branch requirement)
    local current_branch
    current_branch=$(git rev-parse --abbrev-ref HEAD)
    log_info "Current branch: $current_branch"

    # Check for untracked important files
    local untracked_files
    untracked_files=$(git ls-files --others --exclude-standard | grep -E '\.py$|\.toml$|\.md$' || true)
    if [[ -n "$untracked_files" ]]; then
        log_warning "Found untracked important files:"
        echo "$untracked_files"
        read -p "Continue with release? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            log_info "Release cancelled"
            exit 0
        fi
    fi

    # Check for staged but uncommitted changes (excluding version files)
    local staged_files
    staged_files=$(git diff --cached --name-only | grep -v -E 'pyproject\.toml|CHANGELOG\.md' || true)
    if [[ -n "$staged_files" ]]; then
        log_error "Found staged but uncommitted changes (excluding version files):"
        echo "$staged_files"
        log_info "Please commit these changes or unstage them first"
        exit 1
    fi
}

# Get current version
get_current_version() {
    python -c "
import sys
import os
sys.path.insert(0, os.path.join(os.getcwd(), 'src'))
from funasr_client import __version__
print(__version__)
"
}

# Version comparison and increment
increment_version() {
    local current_version=$1
    local version_type=${2:-patch}  # major, minor, patch

    IFS='.' read -ra VERSION_PARTS <<< "$current_version"
    local major=${VERSION_PARTS[0]}
    local minor=${VERSION_PARTS[1]}
    local patch=${VERSION_PARTS[2]}

    case $version_type in
        major)
            major=$((major + 1))
            minor=0
            patch=0
            ;;
        minor)
            minor=$((minor + 1))
            patch=0
            ;;
        patch)
            patch=$((patch + 1))
            ;;
        *)
            log_error "Invalid version type: $version_type"
            exit 1
            ;;
    esac

    echo "$major.$minor.$patch"
}

# Update version number
update_version() {
    local new_version=$1

    # Update version in src/funasr_client/__init__.py
    if [[ "$OSTYPE" == "darwin"* ]]; then
        # macOS
        sed -i '' "s/^__version__ = \".*\"/__version__ = \"$new_version\"/" src/funasr_client/__init__.py
    else
        # Linux
        sed -i "s/^__version__ = \".*\"/__version__ = \"$new_version\"/" src/funasr_client/__init__.py
    fi

    log_success "Version updated to: $new_version"
}

# Update CHANGELOG
update_changelog() {
    local new_version=$1
    local date
    date=$(date +%Y-%m-%d)

    # Generate git changelog
    local last_tag
    last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
    local git_log_range

    if [[ -n "$last_tag" ]]; then
        git_log_range="$last_tag..HEAD"
    else
        git_log_range="HEAD"
    fi

    # Get commit records
    local commits
    commits=$(git log --oneline --pretty=format:"- %s" $git_log_range | grep -v "^- Merge" || true)

    if [[ -n "$commits" ]]; then
        # Backup original CHANGELOG
        cp CHANGELOG.md CHANGELOG.md.bak

        # Create temporary file
        local temp_file
        temp_file=$(mktemp)

        # Generate new CHANGELOG content
        {
            head -n 6 CHANGELOG.md  # Keep header
            echo
            echo "## [$new_version] - $date"
            echo
            echo "### Changes"
            echo "$commits"
            echo
            tail -n +8 CHANGELOG.md  # Keep remaining content
        } > "$temp_file"

        mv "$temp_file" CHANGELOG.md
        log_success "CHANGELOG.md updated"
    else
        log_warning "No new commits found"
    fi
}

# Run tests (adapted for CosyVoice Python SDK)
run_tests() {
    log_info "Running tests..."

    # Check if test files exist
    local has_tests=false
    if [[ -d "tests" ]] && [[ -n "$(find tests -name 'test_*.py' -o -name '*_test.py' 2>/dev/null)" ]]; then
        has_tests=true
    elif [[ -n "$(find . -maxdepth 2 -name 'test_*.py' -o -name '*_test.py' 2>/dev/null)" ]]; then
        has_tests=true
    fi

    if [[ "$has_tests" == "true" ]]; then
        if command -v uv &> /dev/null; then
            log_info "Running tests with uv..."
            uv run pytest -v || {
                log_warning "Some tests failed, but continuing with release (development version)"
                log_warning "Consider fixing test failures for production release"
            }
        else
            log_info "Running tests with python..."
            python -m pytest -v || {
                log_warning "Some tests failed, but continuing with release (development version)"
                log_warning "Consider fixing test failures for production release"
            }
        fi
        log_success "Tests completed"
    else
        log_warning "No test files found, skipping test step"
        log_info "Consider adding test files in the tests/ directory"
    fi

    # Run code quality checks
    log_info "Running code quality checks..."
    if command -v uv &> /dev/null; then
        # Check ruff
        log_info "Running ruff check..."
        uv run ruff check src tests examples || {
            log_error "Ruff check failed"
            exit 1
        }

        # Type checking
        log_info "Running type checking..."
        uv run mypy src/funasr_client || {
            log_warning "Type checking has warnings, but continuing with release"
        }
    else
        log_info "uv not installed, skipping code quality checks"
        log_info "Consider installing uv for full development tool support"
    fi

    log_success "Code quality checks completed"
}

# Build package
build_package() {
    log_info "Cleaning old build files..."
    rm -rf dist/ build/ *.egg-info/

    log_info "Building package..."
    if command -v uv &> /dev/null; then
        uv run python -m build
    else
        python -m build
    fi

    # Check build results
    if [[ ! -d "dist" ]] || [[ -z "$(ls -A dist/)" ]]; then
        log_error "Package build failed: dist directory is empty"
        exit 1
    fi

    log_info "Validating package..."
    if command -v uv &> /dev/null; then
        uv run twine check dist/*
    else
        twine check dist/*
    fi

    log_success "Package build completed"
}


# Commit changes only (adapted for local-only repository)
commit_changes_only() {
    local new_version=$1

    # Check if version-related files need to be committed
    local files_to_commit=()

    # Check pyproject.toml
    if git diff --name-only | grep -q "pyproject.toml"; then
        files_to_commit+=("pyproject.toml")
    fi

    # Check CHANGELOG.md
    if git diff --name-only | grep -q "CHANGELOG.md"; then
        files_to_commit+=("CHANGELOG.md")
    fi

    # If there are files to commit
    if [[ ${#files_to_commit[@]} -gt 0 ]]; then
        log_info "Committing version-related files: ${files_to_commit[*]}"
        git add "${files_to_commit[@]}"
        git commit -m "chore: release v$new_version"
        log_success "Version changes committed"
    else
        log_info "No version file changes to commit"
    fi

    # Check if tag already exists
    if git tag -l | grep -q "^v$new_version$"; then
        log_warning "Tag v$new_version already exists"
        read -p "Delete existing tag and recreate? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            git tag -d "v$new_version"
            log_info "Existing tag deleted"
        else
            log_error "Tag exists, release cancelled"
            exit 1
        fi
    fi

    log_info "Creating tag..."
    git tag -a "v$new_version" -m "Release version $new_version"

    log_success "Tag created: v$new_version"
    log_info "Local repository release completed (not pushed to remote)"
}

# Publish to PyPI (supports local builds)
publish_to_pypi() {
    # Ask if PyPI publishing is needed
    read -p "Publish to PyPI? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        log_info "Skipping PyPI publishing"
        return 0
    fi

    log_info "Publishing to PyPI..."

    # Check if PyPI authentication is configured
    if [[ -f ~/.pypirc ]] || [[ -n "${TWINE_USERNAME:-}" ]] || [[ -n "${TWINE_PASSWORD:-}" ]]; then
        log_info "PyPI authentication configuration detected"

        # Optionally upload to Test PyPI first
        read -p "Upload to Test PyPI first for testing? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            log_info "Uploading to Test PyPI..."
            if command -v uv &> /dev/null; then
                uv run twine upload --repository testpypi dist/* || {
                    log_warning "Test PyPI upload failed, continuing to production PyPI"
                }
            else
                twine upload --repository testpypi dist/* || {
                    log_warning "Test PyPI upload failed, continuing to production PyPI"
                }
            fi
        fi

        # Upload to production PyPI
        read -p "Confirm upload to production PyPI? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            log_info "Uploading to PyPI..."
            if command -v uv &> /dev/null; then
                uv run twine upload dist/*
            else
                twine upload dist/*
            fi
            log_success "Published to PyPI"
        else
            log_info "Skipping production PyPI upload"
        fi
    else
        log_error "PyPI authentication not configured"
        log_info "Please configure ~/.pypirc file or set TWINE_USERNAME and TWINE_PASSWORD environment variables"
        log_info "Or manually run: twine upload dist/*"

        read -p "Skip PyPI publishing and only build locally? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            exit 1
        fi
        log_warning "Skipping PyPI publishing"
    fi
}

# Cleanup function
cleanup() {
    if [[ -f "CHANGELOG.md.bak" ]]; then
        rm -f CHANGELOG.md.bak
    fi
}

# Main function
main() {
    log_info "Starting FunASR Python Client release process..."

    # Set cleanup trap
    trap cleanup EXIT

    # Check environment
    check_working_directory
    check_command "git"
    check_command "python"

    if command -v uv &> /dev/null; then
        log_info "Using uv as package manager"
        # Ensure necessary packages are installed
        uv add --dev build twine 2>/dev/null || true
    else
        check_command "twine"
        if ! python -c "import build" 2>/dev/null; then
            log_error "Build tools not found, please install: pip install build twine"
            exit 1
        fi
        log_info "Using pip/twine as package manager"
    fi

    # Check Git status
    check_git_status

    # Get current version
    local current_version
    current_version=$(get_current_version)
    log_info "Current version: $current_version"

    # Check if version has already been modified
    local new_version
    local version_changed=false

    if git diff --name-only | grep -q "pyproject.toml"; then
        log_info "pyproject.toml has been modified, using current version for release"
        new_version="$current_version"
        version_changed=true
    else
        # Choose version increment type
        echo "Choose version increment type:"
        echo "1) patch (patch version, e.g., 1.0.0 -> 1.0.1)"
        echo "2) minor (minor version, e.g., 1.0.0 -> 1.1.0)"
        echo "3) major (major version, e.g., 1.0.0 -> 2.0.0)"
        echo "4) custom version number"

        read -p "Choose (1-4): " -n 1 -r choice
        echo

        case $choice in
            1)
                new_version=$(increment_version "$current_version" "patch")
                ;;
            2)
                new_version=$(increment_version "$current_version" "minor")
                ;;
            3)
                new_version=$(increment_version "$current_version" "major")
                ;;
            4)
                read -p "Enter new version number: " new_version
                ;;
            *)
                log_error "Invalid choice"
                exit 1
                ;;
        esac
    fi

    log_info "Target version: $new_version"

    # Confirm release
    read -p "Confirm release of version $new_version? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        log_info "Release cancelled"
        exit 0
    fi

    # Execute release process
    log_info "Starting release process..."

    # If version hasn't been pre-modified, update according to user choice
    if [[ "$version_changed" == "false" ]]; then
        # 1. Update version number
        update_version "$new_version"
    fi

    # 2. Update CHANGELOG (if needed)
    if ! git diff --name-only | grep -q "CHANGELOG.md"; then
        update_changelog "$new_version"
    else
        log_info "CHANGELOG.md has been modified, skipping automatic update"
    fi

    # 3. Run tests
    run_tests

    # 4. Build package
    build_package

    # 5. Commit and create tag (local only)
    commit_changes_only "$new_version"

    # 6. Publish to PyPI (optional)
    publish_to_pypi

    log_success "🎉 Version $new_version released successfully!"

    # Show local information instead of remote links
    log_info "Local release completed:"
    log_info "- Git tag: v$new_version"
    log_info "- Build files: dist/"
    log_info "- Package name: funasr-python"

    if [[ -d "dist" ]]; then
        log_info "Build artifacts:"
        ls -la dist/
    fi

    log_info "Next steps:"
    log_info "1. To push to remote: git push origin && git push origin v$new_version"
    log_info "2. To manually publish to PyPI: twine upload dist/*"
    log_info "3. To install locally for testing: pip install dist/*.whl"
}

# Run main function
main "$@"
