#!/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"; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# Global variables
COMMIT_MESSAGE=""
RELEASE_MODE=false

show_usage() {
    echo "C-Relay Build and Push Script"
    echo ""
    echo "Usage:"
    echo "  $0 \"commit message\"           - Default: compile, increment patch, commit & push"
    echo "  $0 -r \"commit message\"        - Release: compile x86+arm64, increment minor, create release"
    echo ""
    echo "Examples:"
    echo "  $0 \"Fixed event validation bug\""
    echo "  $0 --release \"Major release with new features\""
    echo ""
    echo "Default Mode (patch increment):"
    echo "  - Compile C-Relay"
    echo "  - Increment patch version (v1.2.3 → v1.2.4)"
    echo "  - Git add, commit with message, and push"
    echo ""
    echo "Release Mode (-r flag):"
    echo "  - Compile C-Relay for x86_64 and arm64 (dynamic and static versions)"
    echo "  - Increment minor version, zero patch (v1.2.3 → v1.3.0)"
    echo "  - Git add, commit, push, and create Gitea release"
    echo ""
    echo "Requirements for Release Mode:"
    echo "  - For ARM64 builds: make install-arm64-deps (optional - will build x86_64 only if missing)"
    echo "  - For static builds: sudo apt-get install musl-dev libcap-dev libuv1-dev libev-dev"
    echo "  - Gitea token in ~/.gitea_token for release uploads"
}

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -r|--release)
            RELEASE_MODE=true
            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" or "minor"
    
    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" == "minor" ]]; then
        # Minor release: increment minor, zero patch
        NEW_MINOR=$((MINOR + 1))
        NEW_PATCH=0
        NEW_VERSION="v${MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
        print_status "Release mode: incrementing minor version"
    else
        # Default: increment patch
        NEW_PATCH=$((PATCH + 1))
        NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
        print_status "Default mode: incrementing patch version"
    fi
    
    print_status "Current version: $LATEST_TAG"
    print_status "New version: $NEW_VERSION"
    
    # Export for use in other functions
    export NEW_VERSION
}

# Function to compile the C-Relay project
compile_project() {
    print_status "Compiling C-Relay..."
    
    # Clean previous build
    if make clean > /dev/null 2>&1; then
        print_success "Cleaned previous build"
    else
        print_warning "Clean failed or no Makefile found"
    fi
    
    # Force regenerate main.h to pick up new tags
    if make force-version > /dev/null 2>&1; then
        print_success "Regenerated main.h"
    else
        print_warning "Failed to regenerate main.h"
    fi
    
    # Compile the project
    if make > /dev/null 2>&1; then
        print_success "C-Relay compiled successfully"
    else
        print_error "Compilation failed"
        exit 1
    fi
}

# Function to build release binaries
build_release_binaries() {
    print_status "Building release binaries..."
    
    # Build x86_64 version
    print_status "Building x86_64 version..."
    make clean > /dev/null 2>&1
    if make x86 > /dev/null 2>&1; then
        if [[ -f "build/c_relay_x86" ]]; then
            cp build/c_relay_x86 c-relay-x86_64
            print_success "x86_64 binary created: c-relay-x86_64"
        else
            print_error "x86_64 binary not found after compilation"
            exit 1
        fi
    else
        print_error "x86_64 build failed"
        exit 1
    fi
    
    # Try to build ARM64 version
    print_status "Attempting ARM64 build..."
    make clean > /dev/null 2>&1
    if make arm64 > /dev/null 2>&1; then
        if [[ -f "build/c_relay_arm64" ]]; then
            cp build/c_relay_arm64 c-relay-arm64
            print_success "ARM64 binary created: c-relay-arm64"
        else
            print_warning "ARM64 binary not found after compilation"
        fi
    else
        print_warning "ARM64 build failed - ARM64 cross-compilation not properly set up"
        print_status "Only x86_64 binary will be included in release"
    fi
    
    # Build static x86_64 version
    print_status "Building static x86_64 version..."
    make clean > /dev/null 2>&1
    if make static-musl-x86_64 > /dev/null 2>&1; then
        if [[ -f "build/c_relay_static_musl_x86_64" ]]; then
            cp build/c_relay_static_musl_x86_64 c-relay-static-x86_64
            print_success "Static x86_64 binary created: c-relay-static-x86_64"
        else
            print_warning "Static x86_64 binary not found after compilation"
        fi
    else
        print_warning "Static x86_64 build failed - MUSL development packages may not be installed"
        print_status "Run 'sudo apt-get install musl-dev libcap-dev libuv1-dev libev-dev' to enable static builds"
    fi
    
    # Try to build static ARM64 version
    print_status "Attempting static ARM64 build..."
    make clean > /dev/null 2>&1
    if make static-musl-arm64 > /dev/null 2>&1; then
        if [[ -f "build/c_relay_static_musl_arm64" ]]; then
            cp build/c_relay_static_musl_arm64 c-relay-static-arm64
            print_success "Static ARM64 binary created: c-relay-static-arm64"
        else
            print_warning "Static ARM64 binary not found after compilation"
        fi
    else
        print_warning "Static ARM64 build failed - ARM64 cross-compilation or MUSL ARM64 packages not set up"
    fi
    
    # Restore normal build
    make clean > /dev/null 2>&1
    make > /dev/null 2>&1
}

# 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 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\"}")
    
    local upload_result=false
    
    if echo "$response" | grep -q '"id"'; then
        print_success "Created release $NEW_VERSION"
        if upload_release_binaries "$api_url" "$token"; then
            upload_result=true
        fi
    elif echo "$response" | grep -q "already exists"; then
        print_warning "Release $NEW_VERSION already exists"
        if upload_release_binaries "$api_url" "$token"; then
            upload_result=true
        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"
            if upload_release_binaries "$api_url" "$token"; then
                upload_result=true
            fi
        else
            print_error "Release does not exist and creation failed"
            return 1
        fi
    fi
    
    # Return based on upload success
    if [[ "$upload_result" == true ]]; then
        return 0
    else
        print_error "Binary upload failed"
        return 1
    fi
}

# Function to upload release binaries
upload_release_binaries() {
    local api_url="$1"
    local token="$2"
    local upload_success=true
    
    # Get release ID with more robust parsing
    print_status "Getting release ID for $NEW_VERSION..."
    local response=$(curl -s -H "Authorization: token $token" "$api_url/releases/tags/$NEW_VERSION")
    local release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -n1 | cut -d: -f2)
    
    if [[ -z "$release_id" ]]; then
        print_error "Could not get release ID for $NEW_VERSION"
        print_error "API Response: $response"
        
        # Try to list all releases to debug
        print_status "Available releases:"
        curl -s -H "Authorization: token $token" "$api_url/releases" | grep -o '"tag_name":"[^"]*"' | head -5
        return 1
    fi
    
    print_success "Found release ID: $release_id"
    
    # Upload x86_64 binary
    if [[ -f "c-relay-x86_64" ]]; then
        print_status "Uploading x86_64 binary..."
        local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \
                -H "Authorization: token $token" \
                -F "attachment=@c-relay-x86_64;filename=c-relay-${NEW_VERSION}-linux-x86_64")
        
        local http_code=$(echo "$upload_response" | tail -n1)
        local response_body=$(echo "$upload_response" | head -n -1)
        
        if [[ "$http_code" == "201" ]]; then
            print_success "Uploaded x86_64 binary successfully"
        else
            print_error "Failed to upload x86_64 binary (HTTP $http_code)"
            print_error "Response: $response_body"
            upload_success=false
        fi
    else
        print_warning "x86_64 binary not found: c-relay-x86_64"
    fi
    
    # Upload ARM64 binary
    if [[ -f "c-relay-arm64" ]]; then
        print_status "Uploading ARM64 binary..."
        local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \
                -H "Authorization: token $token" \
                -F "attachment=@c-relay-arm64;filename=c-relay-${NEW_VERSION}-linux-arm64")

        local http_code=$(echo "$upload_response" | tail -n1)
        local response_body=$(echo "$upload_response" | head -n -1)

        if [[ "$http_code" == "201" ]]; then
            print_success "Uploaded ARM64 binary successfully"
        else
            print_error "Failed to upload ARM64 binary (HTTP $http_code)"
            print_error "Response: $response_body"
            upload_success=false
        fi
    else
        print_warning "ARM64 binary not found: c-relay-arm64"
    fi

    # Upload static x86_64 binary
    if [[ -f "c-relay-static-x86_64" ]]; then
        print_status "Uploading static x86_64 binary..."
        local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \
                -H "Authorization: token $token" \
                -F "attachment=@c-relay-static-x86_64;filename=c-relay-${NEW_VERSION}-linux-x86_64-static")

        local http_code=$(echo "$upload_response" | tail -n1)
        local response_body=$(echo "$upload_response" | head -n -1)

        if [[ "$http_code" == "201" ]]; then
            print_success "Uploaded static x86_64 binary successfully"
        else
            print_error "Failed to upload static x86_64 binary (HTTP $http_code)"
            print_error "Response: $response_body"
            upload_success=false
        fi
    else
        print_warning "Static x86_64 binary not found: c-relay-static-x86_64"
    fi

    # Upload static ARM64 binary
    if [[ -f "c-relay-static-arm64" ]]; then
        print_status "Uploading static ARM64 binary..."
        local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \
                -H "Authorization: token $token" \
                -F "attachment=@c-relay-static-arm64;filename=c-relay-${NEW_VERSION}-linux-arm64-static")

        local http_code=$(echo "$upload_response" | tail -n1)
        local response_body=$(echo "$upload_response" | head -n -1)

        if [[ "$http_code" == "201" ]]; then
            print_success "Uploaded static ARM64 binary successfully"
        else
            print_error "Failed to upload static ARM64 binary (HTTP $http_code)"
            print_error "Response: $response_body"
            upload_success=false
        fi
    else
        print_warning "Static ARM64 binary not found: c-relay-static-arm64"
    fi
    
    # Return success/failure status
    if [[ "$upload_success" == true ]]; then
        return 0
    else
        return 1
    fi
}

# Function to clean up release binaries
cleanup_release_binaries() {
    local force_cleanup="$1"  # Optional parameter to force cleanup even on failure
    
    if [[ "$force_cleanup" == "force" ]] || [[ "$upload_success" == true ]]; then
        if [[ -f "c-relay-x86_64" ]]; then
            rm -f c-relay-x86_64
            print_status "Cleaned up x86_64 binary"
        fi
        if [[ -f "c-relay-arm64" ]]; then
            rm -f c-relay-arm64
            print_status "Cleaned up ARM64 binary"
        fi
        if [[ -f "c-relay-static-x86_64" ]]; then
            rm -f c-relay-static-x86_64
            print_status "Cleaned up static x86_64 binary"
        fi
        if [[ -f "c-relay-static-arm64" ]]; then
            rm -f c-relay-static-arm64
            print_status "Cleaned up static ARM64 binary"
        fi
    else
        print_warning "Keeping binary files due to upload failures"
        print_status "Files available for manual upload:"
        if [[ -f "c-relay-x86_64" ]]; then
            print_status "  - c-relay-x86_64"
        fi
        if [[ -f "c-relay-arm64" ]]; then
            print_status "  - c-relay-arm64"
        fi
        if [[ -f "c-relay-static-x86_64" ]]; then
            print_status "  - c-relay-static-x86_64"
        fi
        if [[ -f "c-relay-static-arm64" ]]; then
            print_status "  - c-relay-static-arm64"
        fi
    fi
}

# Main execution
main() {
    print_status "C-Relay Build and Push Script"
    
    # Check prerequisites
    check_git_repo
    
    if [[ "$RELEASE_MODE" == true ]]; then
        print_status "=== RELEASE MODE ==="
        
        # Increment minor version for releases
        increment_version "minor"
        
        # 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
        
        # Compile project first (will now pick up the new tag)
        compile_project
        
        # Build release binaries
        build_release_binaries
        
        # Commit and push (but skip tag creation since we already did it)
        git_commit_and_push_no_tag
        
        # Create Gitea release with binaries
        if create_gitea_release; then
            print_success "Release $NEW_VERSION completed successfully!"
            print_status "Binaries uploaded to Gitea release"
            upload_success=true
        else
            print_error "Release creation or binary upload failed"
            upload_success=false
        fi
        
        # Cleanup (only if upload was successful)
        cleanup_release_binaries
        
    else
        print_status "=== DEFAULT MODE ==="
        
        # Increment patch version for regular commits
        increment_version "patch"
        
        # 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
        
        # Compile project (will now pick up the new tag)
        compile_project
        
        # Commit and push (but skip tag creation since we already did it)
        git_commit_and_push_no_tag
        
        print_success "Build and push completed successfully!"
        print_status "Version $NEW_VERSION pushed to repository"
    fi
}

# Execute main function
main
