#!/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 "Didactyl 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 signing bug\""
    echo "  $0 -m \"Added tool-use system\""
    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 (v0.1.0 → v0.1.1)"
    echo "  -m, --minor:           Increment minor version, zero patch (v0.1.0 → v0.2.0)"
    echo "  -M, --major:           Increment major version, zero minor+patch (v0.1.0 → v1.0.0)"
    echo ""
    echo "RELEASE MODE (-r flag):"
    echo "  - Build static x86_64 and arm64 binaries using build_static.sh"
    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
}

# Ensure git identity exists so commits do not fail on clean machines
ensure_git_identity() {
    local current_name=""
    local current_email=""

    current_name=$(git config --get user.name 2>/dev/null || true)
    current_email=$(git config --get user.email 2>/dev/null || true)

    if [[ -n "$current_name" && -n "$current_email" ]]; then
        return 0
    fi

    print_warning "Git user.name / user.email not fully configured for this repository"

    local fallback_name
    local fallback_email

    fallback_name="${GIT_AUTHOR_NAME:-Didactyl User}"
    fallback_email="${GIT_AUTHOR_EMAIL:-didactyl@local}"

    if [[ -z "$current_name" ]]; then
        git config user.name "$fallback_name"
        print_status "Set local git user.name to '$fallback_name'"
    fi

    if [[ -z "$current_email" ]]; then
        git config user.email "$fallback_email"
        print_status "Set local git user.email to '$fallback_email'"
    fi
}

# Ensure origin remote uses SSH for git.laantungir.net to avoid HTTPS username prompts
ensure_origin_ssh_remote() {
    local origin_url
    origin_url=$(git remote get-url origin 2>/dev/null || true)

    if [[ -z "$origin_url" ]]; then
        print_warning "No 'origin' remote found; skipping remote URL normalization"
        return 0
    fi

    # Convert only this host from HTTPS to SSH (uses ~/.ssh/config Host git.laantungir.net)
    if [[ "$origin_url" =~ ^https://git\.laantungir\.net/(.+)\.git$ ]]; then
        local repo_path
        local ssh_url
        repo_path="${BASH_REMATCH[1]}"
        ssh_url="git@git.laantungir.net:${repo_path}.git"
        git remote set-url origin "$ssh_url"
        print_status "Updated origin remote to SSH: $ssh_url"
    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
        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
        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
        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 DIDACTYL_VERSION macro
    sed -i "s/#define DIDACTYL_VERSION \".*\"/#define DIDACTYL_VERSION \"$new_version\"/" src/main.h

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

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

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

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

# Function to update README Current Status version and release comment
update_readme_current_status() {
    local new_version="$1"
    local release_comment="$2"

    if [[ ! -f "README.md" ]]; then
        print_warning "README.md not found, skipping Current Status update"
        return 0
    fi

    print_status "Updating README Current Status section..."

    if python3 - "$new_version" "$release_comment" <<'PY'
import sys
from pathlib import Path

version = sys.argv[1]
comment = sys.argv[2]
path = Path("README.md")
text = path.read_text(encoding="utf-8")
lines = text.splitlines()

heading_idx = None
for i, line in enumerate(lines):
    if line.startswith("## Current Status"):
        heading_idx = i
        break

if heading_idx is None:
    raise SystemExit("README Current Status heading not found")

lines[heading_idx] = f"## Current Status — {version}"

section_end = len(lines)
for i in range(heading_idx + 1, len(lines)):
    if lines[i].startswith("## "):
        section_end = i
        break

comment_line = f"> Last release update: {version} — {comment}"

# Replace existing autogenerated release comment inside Current Status section
existing_idx = None
for i in range(heading_idx + 1, section_end):
    if lines[i].startswith("> Last release update:"):
        existing_idx = i
        break

if existing_idx is not None:
    lines[existing_idx] = comment_line
else:
    insert_at = None
    for i in range(heading_idx + 1, section_end):
        if lines[i].startswith("**Active build"):
            insert_at = i + 1
            break

    if insert_at is None:
        insert_at = heading_idx + 1

    payload = ["", comment_line]
    lines[insert_at:insert_at] = payload

path.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
    then
        print_success "Updated README Current Status section"
    else
        print_error "Failed to update README Current Status section"
        exit 1
    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"
            print_error "git commit output:"
            git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" 2>&1 || true
            exit 1
        fi
    fi

    # Push changes
    print_status "Pushing to remote repository..."
    local current_branch
    current_branch=$(git branch --show-current 2>/dev/null || echo "")

    if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" > /dev/null 2>&1; then
        local push_output
        if push_output=$(git push 2>&1); then
            print_success "Pushed changes"
        else
            print_error "Failed to push changes"
            print_error "git push output:"
            echo "$push_output" >&2
            print_warning "If this is an SSH auth error, verify ~/.ssh/config and that the correct private key is available to SSH"
            exit 1
        fi
    else
        if [[ -z "$current_branch" ]]; then
            print_error "Unable to determine current branch for push"
            exit 1
        fi

        print_warning "No upstream configured for branch '$current_branch'; setting upstream to origin/$current_branch"
        local push_output
        if push_output=$(git push -u origin "$current_branch" 2>&1); then
            print_success "Pushed changes and configured upstream"
        else
            print_error "Failed to push changes while configuring upstream"
            print_error "git push output:"
            echo "$push_output" >&2
            print_warning "If this is an SSH auth error, verify ~/.ssh/config and that the correct private key is available to SSH"
            exit 1
        fi
    fi

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

# Function to build release binaries
build_release_binary() {
    print_status "Building release binaries (x86_64 + arm64)..."

    # 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 for both platforms
    if ./build_static.sh --all-platforms > /dev/null 2>&1; then
        print_success "Built static binaries successfully"
        return 0
    else
        print_error "Failed to build one or more static binaries"
        return 1
    fi
}

# Function to upload release assets to Gitea
upload_release_assets() {
    local release_id="$1"
    local binary_x86_path="$2"
    local binary_arm64_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/didactyl"
    local assets_url="$api_url/releases/$release_id/assets"
    print_status "Assets URL: $assets_url"

    # Upload x86_64 binary
    if [[ -f "$binary_x86_path" ]]; then
        print_status "Uploading binary: $(basename "$binary_x86_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_x86_path;filename=$(basename "$binary_x86_path")" \
                  -F "name=$(basename "$binary_x86_path")")

            if echo "$binary_response" | grep -q '"id"'; then
                print_success "Uploaded x86_64 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 x86_64 binary after $max_attempts attempts"
                    print_error "Response: $binary_response"
                fi
            fi
            ((attempt++))
        done
    fi

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

        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_arm64_path;filename=$(basename "$binary_arm64_path")" \
                  -F "name=$(basename "$binary_arm64_path")")

            if echo "$binary_response" | grep -q '"id"'; then
                print_success "Uploaded arm64 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 arm64 binary after $max_attempts attempts"
                    print_error "Response: $binary_response"
                fi
            fi
            ((attempt++))
        done
    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/didactyl"

    # 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"
        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"
        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"

        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 "Didactyl Increment and Push Script"

    # Check prerequisites
    check_git_repo
    ensure_git_identity
    ensure_origin_ssh_remote

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

        # Only increment version if explicitly requested
        if [[ "$VERSION_INCREMENT_TYPE" != "patch" ]]; then
            increment_version "$VERSION_INCREMENT_TYPE"
        else
            LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.0.0")
            NEW_VERSION="$LATEST_TAG"
            export NEW_VERSION
        fi

        # Keep README Current Status in sync with the version and commit message
        update_readme_current_status "$NEW_VERSION" "$COMMIT_MESSAGE"

        # Create new git tag BEFORE compilation so version 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
        git_commit_and_push_no_tag

        # Build release binaries
        local binary_x86_path=""
        local binary_arm64_path=""
        if build_release_binary; then
            [[ -f "build/didactyl_static_x86_64" ]] && binary_x86_path="build/didactyl_static_x86_64"
            [[ -f "build/didactyl_static_arm64" ]] && binary_arm64_path="build/didactyl_static_arm64"
        else
            print_warning "Binary build failed, continuing with release creation"
            if [[ -f "build/didactyl_static_x86_64" ]]; then
                print_status "Using existing x86_64 binary from previous build"
                binary_x86_path="build/didactyl_static_x86_64"
            fi
            if [[ -f "build/didactyl_static_arm64" ]]; then
                print_status "Using existing arm64 binary from previous build"
                binary_arm64_path="build/didactyl_static_arm64"
            fi
        fi

        # Create Gitea release
        local release_id=""
        if release_id=$(create_gitea_release); then
            if [[ "$release_id" =~ ^[0-9]+$ ]]; then
                if [[ -n "$release_id" && (-n "$binary_x86_path" || -n "$binary_arm64_path") ]]; then
                    upload_release_assets "$release_id" "$binary_x86_path" "$binary_arm64_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"

        # Keep README Current Status in sync with the version and commit message
        update_readme_current_status "$NEW_VERSION" "$COMMIT_MESSAGE"

        # Create new git tag BEFORE compilation so version 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
        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
