#!/bin/bash
set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; }
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }

# Global variables
COMMIT_MESSAGE=""
RELEASE_MODE=false
VERSION_INCREMENT_TYPE="patch"  # "patch", "minor", or "major"

show_usage() {
    echo "C-Relay Increment and Push Script"
    echo ""
    echo "USAGE:"
    echo "  $0 [OPTIONS] \"commit message\""
    echo ""
    echo "COMMANDS:"
    echo "  $0 \"commit message\"           Default: increment patch, commit & push"
    echo "  $0 -p \"commit message\"        Increment patch version"
    echo "  $0 -m \"commit message\"        Increment minor version"
    echo "  $0 -M \"commit message\"        Increment major version"
    echo "  $0 -r \"commit message\"        Create release with assets (no version increment)"
    echo "  $0 -r -m \"commit message\"     Create release with minor version increment"
    echo "  $0 -h                          Show this help message"
    echo ""
    echo "OPTIONS:"
    echo "  -p, --patch    Increment patch version (default)"
    echo "  -m, --minor    Increment minor version"
    echo "  -M, --major    Increment major version"
    echo "  -r, --release  Create release with assets"
    echo "  -h, --help     Show this help message"
    echo ""
    echo "EXAMPLES:"
    echo "  $0 \"Fixed event validation bug\""
    echo "  $0 -m \"Added new features\""
    echo "  $0 -M \"Breaking API changes\""
    echo "  $0 -r \"Release current version\""
    echo "  $0 -r -m \"Release with minor increment\""
    echo ""
    echo "VERSION INCREMENT MODES:"
    echo "  -p, --patch (default): Increment patch version (v1.2.3 → v1.2.4)"
    echo "  -m, --minor:           Increment minor version, zero patch (v1.2.3 → v1.3.0)"
    echo "  -M, --major:           Increment major version, zero minor+patch (v1.2.3 → v2.0.0)"
    echo ""
    echo "RELEASE MODE (-r flag):"
    echo "  - Build static binary using build_static.sh"
    echo "  - Create source tarball"
    echo "  - Git add, commit, push, and create Gitea release with assets"
    echo "  - Can be combined with version increment flags"
    echo ""
    echo "REQUIREMENTS FOR RELEASE MODE:"
    echo "  - Gitea token in ~/.gitea_token for release uploads"
    echo "  - Docker installed for static binary builds"
}

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -r|--release)
            RELEASE_MODE=true
            shift
            ;;
        -p|--patch)
            VERSION_INCREMENT_TYPE="patch"
            shift
            ;;
        -m|--minor)
            VERSION_INCREMENT_TYPE="minor"
            shift
            ;;
        -M|--major)
            VERSION_INCREMENT_TYPE="major"
            shift
            ;;
        -h|--help)
            show_usage
            exit 0
            ;;
        *)
            # First non-flag argument is the commit message
            if [[ -z "$COMMIT_MESSAGE" ]]; then
                COMMIT_MESSAGE="$1"
            fi
            shift
            ;;
    esac
done

# Validate inputs
if [[ -z "$COMMIT_MESSAGE" ]]; then
    print_error "Commit message is required"
    echo ""
    show_usage
    exit 1
fi

# Check if we're in a git repository
check_git_repo() {
    if ! git rev-parse --git-dir > /dev/null 2>&1; then
        print_error "Not in a git repository"
        exit 1
    fi
}

# Function to get current version and increment appropriately
increment_version() {
    local increment_type="$1"  # "patch", "minor", or "major"

    print_status "Getting current version..."

    # Get the highest version tag (not chronologically latest)
    LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "")
    if [[ -z "$LATEST_TAG" ]]; then
        LATEST_TAG="v0.0.0"
        print_warning "No version tags found, starting from $LATEST_TAG"
    fi

    # Extract version components (remove 'v' prefix)
    VERSION=${LATEST_TAG#v}

    # Parse major.minor.patch using regex
    if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
        MAJOR=${BASH_REMATCH[1]}
        MINOR=${BASH_REMATCH[2]}
        PATCH=${BASH_REMATCH[3]}
    else
        print_error "Invalid version format in tag: $LATEST_TAG"
        print_error "Expected format: v0.1.0"
        exit 1
    fi

    # Increment version based on type
    if [[ "$increment_type" == "major" ]]; then
        # Major release: increment major, zero minor and patch
        NEW_MAJOR=$((MAJOR + 1))
        NEW_MINOR=0
        NEW_PATCH=0
        NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
        print_status "Major version increment: incrementing major version"
    elif [[ "$increment_type" == "minor" ]]; then
        # Minor release: increment minor, zero patch
        NEW_MAJOR=$MAJOR
        NEW_MINOR=$((MINOR + 1))
        NEW_PATCH=0
        NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
        print_status "Minor version increment: incrementing minor version"
    else
        # Default: increment patch
        NEW_MAJOR=$MAJOR
        NEW_MINOR=$MINOR
        NEW_PATCH=$((PATCH + 1))
        NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
        print_status "Patch version increment: incrementing patch version"
    fi

    print_status "Current version: $LATEST_TAG"
    print_status "New version: $NEW_VERSION"

    # Update version in src/main.h
    update_version_in_header "$NEW_VERSION" "$NEW_MAJOR" "$NEW_MINOR" "$NEW_PATCH"

    # Export for use in other functions
    export NEW_VERSION
}

# Function to update version macros in src/main.h
update_version_in_header() {
    local new_version="$1"
    local major="$2"
    local minor="$3"
    local patch="$4"

    print_status "Updating version in src/main.h..."

    # Check if src/main.h exists
    if [[ ! -f "src/main.h" ]]; then
        print_error "src/main.h not found"
        exit 1
    fi

    # Update VERSION macro
    sed -i "s/#define VERSION \".*\"/#define VERSION \"$new_version\"/" src/main.h

    # Update VERSION_MAJOR macro
    sed -i "s/#define VERSION_MAJOR [0-9]\+/#define VERSION_MAJOR $major/" src/main.h

    # Update VERSION_MINOR macro
    sed -i "s/#define VERSION_MINOR .*/#define VERSION_MINOR $minor/" src/main.h

    # Update VERSION_PATCH macro
    sed -i "s/#define VERSION_PATCH [0-9]\+/#define VERSION_PATCH $patch/" src/main.h

    print_success "Updated version in src/main.h to $new_version"
}

# Function to commit and push changes
git_commit_and_push() {
    print_status "Preparing git commit..."

    # Stage all changes
    if git add . > /dev/null 2>&1; then
        print_success "Staged all changes"
    else
        print_error "Failed to stage changes"
        exit 1
    fi

    # Check if there are changes to commit
    if git diff --staged --quiet; then
        print_warning "No changes to commit"
    else
        # Commit changes
        if git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" > /dev/null 2>&1; then
            print_success "Committed changes"
        else
            print_error "Failed to commit changes"
            exit 1
        fi
    fi

    # Create new git tag
    if git tag "$NEW_VERSION" > /dev/null 2>&1; then
        print_success "Created tag: $NEW_VERSION"
    else
        print_warning "Tag $NEW_VERSION already exists"
    fi

    # Push changes and tags
    print_status "Pushing to remote repository..."
    if git push > /dev/null 2>&1; then
        print_success "Pushed changes"
    else
        print_error "Failed to push changes"
        exit 1
    fi

    # Push only the new tag to avoid conflicts with existing tags
    if git push origin "$NEW_VERSION" > /dev/null 2>&1; then
        print_success "Pushed tag: $NEW_VERSION"
    else
        print_warning "Tag push failed, trying force push..."
        if git push --force origin "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Force-pushed updated tag: $NEW_VERSION"
        else
            print_error "Failed to push tag: $NEW_VERSION"
            exit 1
        fi
    fi
}

# Function to commit and push changes without creating a tag (tag already created)
git_commit_and_push_no_tag() {
    print_status "Preparing git commit..."

    # Stage all changes
    if git add . > /dev/null 2>&1; then
        print_success "Staged all changes"
    else
        print_error "Failed to stage changes"
        exit 1
    fi

    # Check if there are changes to commit
    if git diff --staged --quiet; then
        print_warning "No changes to commit"
    else
        # Commit changes
        if git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" > /dev/null 2>&1; then
            print_success "Committed changes"
        else
            print_error "Failed to commit changes"
            exit 1
        fi
    fi

    # Push changes and tags
    print_status "Pushing to remote repository..."
    if git push > /dev/null 2>&1; then
        print_success "Pushed changes"
    else
        print_error "Failed to push changes"
        exit 1
    fi

    # Push only the new tag to avoid conflicts with existing tags
    if git push origin "$NEW_VERSION" > /dev/null 2>&1; then
        print_success "Pushed tag: $NEW_VERSION"
    else
        print_warning "Tag push failed, trying force push..."
        if git push --force origin "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Force-pushed updated tag: $NEW_VERSION"
        else
            print_error "Failed to push tag: $NEW_VERSION"
            exit 1
        fi
    fi
}

# Function to build release binary
build_release_binary() {
    print_status "Building release binary..."

    # Check if build_static.sh exists
    if [[ ! -f "build_static.sh" ]]; then
        print_error "build_static.sh not found"
        return 1
    fi

    # Run the static build script
    if ./build_static.sh > /dev/null 2>&1; then
        print_success "Built static binary successfully"
        return 0
    else
        print_error "Failed to build static binary"
        return 1
    fi
}

# Function to create source tarball
create_source_tarball() {
    print_status "Creating source tarball..."

    local tarball_name="c-relay-${NEW_VERSION#v}.tar.gz"

    # Create tarball excluding build artifacts and git files
    if tar -czf "$tarball_name" \
        --exclude='build/*' \
        --exclude='.git*' \
        --exclude='*.db' \
        --exclude='*.db-*' \
        --exclude='*.log' \
        --exclude='*.tar.gz' \
        . > /dev/null 2>&1; then
        print_success "Created source tarball: $tarball_name"
        echo "$tarball_name"
        return 0
    else
        print_error "Failed to create source tarball"
        return 1
    fi
}

# Function to upload release assets to Gitea
upload_release_assets() {
    local release_id="$1"
    local binary_path="$2"
    local tarball_path="$3"

    print_status "Uploading release assets..."

    # Check for Gitea token
    if [[ ! -f "$HOME/.gitea_token" ]]; then
        print_warning "No ~/.gitea_token found. Skipping asset uploads."
        return 0
    fi

    local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r')
    local api_url="https://git.laantungir.net/api/v1/repos/laantungir/c-relay"
    local assets_url="$api_url/releases/$release_id/assets"
    print_status "Assets URL: $assets_url"

    # Upload binary
    if [[ -f "$binary_path" ]]; then
        print_status "Uploading binary: $(basename "$binary_path")"

        # Retry loop for eventual consistency
        local max_attempts=3
        local attempt=1
        while [[ $attempt -le $max_attempts ]]; do
            print_status "Upload attempt $attempt/$max_attempts"
            local binary_response=$(curl -fS -X POST "$assets_url" \
                  -H "Authorization: token $token" \
                  -F "attachment=@$binary_path;filename=$(basename "$binary_path")" \
                  -F "name=$(basename "$binary_path")")

            if echo "$binary_response" | grep -q '"id"'; then
                print_success "Uploaded binary successfully"
                break
            else
                print_warning "Upload attempt $attempt failed"
                if [[ $attempt -lt $max_attempts ]]; then
                    print_status "Retrying in 2 seconds..."
                    sleep 2
                else
                    print_error "Failed to upload binary after $max_attempts attempts"
                    print_error "Response: $binary_response"
                fi
            fi
            ((attempt++))
        done
    fi

    # Upload source tarball
    if [[ -f "$tarball_path" ]]; then
        print_status "Uploading source tarball: $(basename "$tarball_path")"
        local tarball_response=$(curl -s -X POST "$api_url/releases/$release_id/assets" \
              -H "Authorization: token $token" \
              -F "attachment=@$tarball_path;filename=$(basename "$tarball_path")")

        if echo "$tarball_response" | grep -q '"id"'; then
            print_success "Uploaded source tarball successfully"
        else
            print_warning "Failed to upload source tarball: $tarball_response"
        fi
    fi
}

# Function to create Gitea release
create_gitea_release() {
    print_status "Creating Gitea release..."

    # Check for Gitea token
    if [[ ! -f "$HOME/.gitea_token" ]]; then
        print_warning "No ~/.gitea_token found. Skipping release creation."
        print_warning "Create ~/.gitea_token with your Gitea access token to enable releases."
        return 0
    fi

    local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r')
    local api_url="https://git.laantungir.net/api/v1/repos/laantungir/c-relay"

    # Create release
    print_status "Creating release $NEW_VERSION..."
    local response=$(curl -s -X POST "$api_url/releases" \
          -H "Authorization: token $token" \
          -H "Content-Type: application/json" \
          -d "{\"tag_name\": \"$NEW_VERSION\", \"name\": \"$NEW_VERSION\", \"body\": \"$COMMIT_MESSAGE\"}")

    if echo "$response" | grep -q '"id"'; then
        print_success "Created release $NEW_VERSION"
        # Extract release ID for asset uploads
        local release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
        echo $release_id
    elif echo "$response" | grep -q "already exists"; then
        print_warning "Release $NEW_VERSION already exists"
        # Try to get existing release ID
        local check_response=$(curl -s -H "Authorization: token $token" "$api_url/releases/tags/$NEW_VERSION")
        if echo "$check_response" | grep -q '"id"'; then
            local release_id=$(echo "$check_response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
            print_status "Using existing release ID: $release_id"
            echo $release_id
        else
            print_error "Could not find existing release ID"
            return 1
        fi
    else
        print_error "Failed to create release $NEW_VERSION"
        print_error "Response: $response"

        # Try to check if the release exists anyway
        print_status "Checking if release exists..."
        local check_response=$(curl -s -H "Authorization: token $token" "$api_url/releases/tags/$NEW_VERSION")
        if echo "$check_response" | grep -q '"id"'; then
            print_warning "Release exists but creation response was unexpected"
            local release_id=$(echo "$check_response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
            echo $release_id
        else
            print_error "Release does not exist and creation failed"
            return 1
        fi
    fi
}

# Main execution
main() {
    print_status "C-Relay Increment and Push Script"

    # Check prerequisites
    check_git_repo

    if [[ "$RELEASE_MODE" == true ]]; then
        print_status "=== RELEASE MODE ==="

        # Only increment version if explicitly requested (not just because of -r flag)
        if [[ "$VERSION_INCREMENT_TYPE" != "patch" ]]; then
            increment_version "$VERSION_INCREMENT_TYPE"
        else
            # In release mode without version increment, get current version
            LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.0.0")
            NEW_VERSION="$LATEST_TAG"
            export NEW_VERSION
        fi

        # Create new git tag BEFORE compilation so version.h picks it up
        if git tag "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Created tag: $NEW_VERSION"
        else
            print_warning "Tag $NEW_VERSION already exists, removing and recreating..."
            git tag -d "$NEW_VERSION" > /dev/null 2>&1
            git tag "$NEW_VERSION" > /dev/null 2>&1
        fi

        # Commit and push (but skip tag creation since we already did it)
        git_commit_and_push_no_tag

        # Build release binary
        if build_release_binary; then
            local binary_path="build/c_relay_static_x86_64"
        else
            print_warning "Binary build failed, continuing with release creation"
            # Check if binary exists from previous build
            if [[ -f "build/c_relay_static_x86_64" ]]; then
                print_status "Using existing binary from previous build"
                binary_path="build/c_relay_static_x86_64"
            else
                binary_path=""
            fi
        fi

        # Create source tarball
        local tarball_path=""
        if tarball_path=$(create_source_tarball); then
            : # tarball_path is set by the function
        else
            print_warning "Source tarball creation failed, continuing with release creation"
        fi

        # Create Gitea release
        local release_id=""
        if release_id=$(create_gitea_release); then
            # Validate release_id is numeric
            if [[ "$release_id" =~ ^[0-9]+$ ]]; then
                # Upload assets if we have a release ID and assets
                if [[ -n "$release_id" && (-n "$binary_path" || -n "$tarball_path") ]]; then
                    upload_release_assets "$release_id" "$binary_path" "$tarball_path"
                fi
                print_success "Release $NEW_VERSION completed successfully!"
            else
                print_error "Invalid release_id: $release_id"
                exit 1
            fi
        else
            print_error "Release creation failed"
        fi

    else
        print_status "=== DEFAULT MODE ==="

        # Increment version based on type (default to patch)
        increment_version "$VERSION_INCREMENT_TYPE"

        # Create new git tag BEFORE compilation so version.h picks it up
        if git tag "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Created tag: $NEW_VERSION"
        else
            print_warning "Tag $NEW_VERSION already exists, removing and recreating..."
            git tag -d "$NEW_VERSION" > /dev/null 2>&1
            git tag "$NEW_VERSION" > /dev/null 2>&1
        fi

        # Commit and push (but skip tag creation since we already did it)
        git_commit_and_push_no_tag

        print_success "Increment and push completed successfully!"
        print_status "Version $NEW_VERSION pushed to repository"
    fi
}

# Execute main function
main