#!/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; }

COMMIT_MESSAGE=""
RELEASE_MODE=false
VERSION_INCREMENT_TYPE="patch"
VERSION_INCREMENT_EXPLICIT=false

show_usage() {
    echo "nsigner Increment and Push Script"
    echo ""
    echo "USAGE:"
    echo "  $0 [OPTIONS] \"commit 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"
}

while [[ $# -gt 0 ]]; do
    case $1 in
        -r|--release)
            RELEASE_MODE=true
            shift
            ;;
        -p|--patch)
            VERSION_INCREMENT_TYPE="patch"
            VERSION_INCREMENT_EXPLICIT=true
            shift
            ;;
        -m|--minor)
            VERSION_INCREMENT_TYPE="minor"
            VERSION_INCREMENT_EXPLICIT=true
            shift
            ;;
        -M|--major)
            VERSION_INCREMENT_TYPE="major"
            VERSION_INCREMENT_EXPLICIT=true
            shift
            ;;
        -h|--help)
            show_usage
            exit 0
            ;;
        *)
            if [[ -z "$COMMIT_MESSAGE" ]]; then
                COMMIT_MESSAGE="$1"
            fi
            shift
            ;;
    esac
done

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

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

update_version_in_header() {
    local new_version="$1"
    local major="$2"
    local minor="$3"
    local patch="$4"
    local version_file=""

    if [[ -f "src/main.h" ]]; then
        version_file="src/main.h"
    elif [[ -f "src/main.c" ]]; then
        version_file="src/main.c"
    else
        print_error "Neither src/main.h nor src/main.c found"
        exit 1
    fi

    sed -i "s/#define NSIGNER_VERSION \".*\"/#define NSIGNER_VERSION \"$new_version\"/" "$version_file"
    sed -i "s/#define NSIGNER_VERSION_MAJOR [0-9]\+/#define NSIGNER_VERSION_MAJOR $major/" "$version_file"
    sed -i "s/#define NSIGNER_VERSION_MINOR [0-9]\+/#define NSIGNER_VERSION_MINOR $minor/" "$version_file"
    sed -i "s/#define NSIGNER_VERSION_PATCH [0-9]\+/#define NSIGNER_VERSION_PATCH $patch/" "$version_file"

    print_success "Updated $version_file to $new_version"
}

increment_version() {
    local increment_type="$1"

    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

    VERSION=${LATEST_TAG#v}
    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"
        exit 1
    fi

    if [[ "$increment_type" == "major" ]]; then
        NEW_VERSION="v$((MAJOR + 1)).0.0"
    elif [[ "$increment_type" == "minor" ]]; then
        NEW_VERSION="v${MAJOR}.$((MINOR + 1)).0"
    else
        NEW_VERSION="v${MAJOR}.${MINOR}.$((PATCH + 1))"
    fi

    local new_no_v=${NEW_VERSION#v}
    local new_major=${new_no_v%%.*}
    local rest=${new_no_v#*.}
    local new_minor=${rest%%.*}
    local new_patch=${rest##*.}

    update_version_in_header "$NEW_VERSION" "$new_major" "$new_minor" "$new_patch"
    export NEW_VERSION
}

git_commit_and_push_no_tag() {
    git add .

    if ! git diff --staged --quiet; then
        git commit -m "$NEW_VERSION - $COMMIT_MESSAGE"
    else
        print_warning "No changes to commit"
    fi

    git push
    git push origin "$NEW_VERSION" || git push --force origin "$NEW_VERSION"
}

verify_binary_version() {
    local bin_path="$1"
    local expected="$2"

    if [[ ! -x "$bin_path" ]]; then
        print_error "Binary not found or not executable: $bin_path"
        return 1
    fi

    local got
    got="$($bin_path --version 2>/dev/null | awk '{print $2}')"
    if [[ "$got" != "$expected" ]]; then
        print_error "Binary version mismatch for $(basename "$bin_path"): expected $expected, got ${got:-<unknown>}"
        return 1
    fi

    return 0
}

build_release_binary() {
    if [[ ! -f "build_static.sh" ]]; then
        print_error "build_static.sh not found"
        return 1
    fi

    # Prevent stale artifacts from previous builds being uploaded.
    rm -f build/nsigner_static_x86_64 build/nsigner_static_arm64

    ./build_static.sh > /dev/null 2>&1 || return 1
    verify_binary_version "build/nsigner_static_x86_64" "$NEW_VERSION" || return 1

    ./build_static.sh --arch arm64 > /dev/null 2>&1 || print_warning "ARM64 build failed (continuing)"
    if [[ -x "build/nsigner_static_arm64" ]]; then
        verify_binary_version "build/nsigner_static_arm64" "$NEW_VERSION" || return 1
    fi

    return 0
}

create_source_tarball() {
    local tarball_name="nsigner-${NEW_VERSION#v}.tar.gz"

    if tar -czf "$tarball_name" \
        --exclude='build/*' \
        --exclude='.git*' \
        --exclude='*.log' \
        --exclude='*.tar.gz' \
        . > /dev/null 2>&1; then
        echo "$tarball_name"
    else
        return 1
    fi
}

create_gitea_release() {
    if [[ ! -f "$HOME/.gitea_token" ]]; then
        print_warning "No ~/.gitea_token found. Skipping release creation."
        return 0
    fi

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

    local response
    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
        echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2
    else
        return 1
    fi
}

upload_release_assets() {
    local release_id="$1"
    local binary_path_x86="$2"
    local tarball_path="$3"
    local binary_path_arm64="$4"

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

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

    if [[ -f "$binary_path_x86" ]]; then
        curl -s -X POST "$assets_url" \
            -H "Authorization: token $token" \
            -F "attachment=@$binary_path_x86;filename=$(basename "$binary_path_x86")" \
            -F "name=$(basename "$binary_path_x86")" > /dev/null
    fi

    if [[ -f "$binary_path_arm64" ]]; then
        curl -s -X POST "$assets_url" \
            -H "Authorization: token $token" \
            -F "attachment=@$binary_path_arm64;filename=$(basename "$binary_path_arm64")" \
            -F "name=$(basename "$binary_path_arm64")" > /dev/null
    fi

    if [[ -f "$tarball_path" ]]; then
        curl -s -X POST "$assets_url" \
            -H "Authorization: token $token" \
            -F "attachment=@$tarball_path;filename=$(basename "$tarball_path")" \
            -F "name=$(basename "$tarball_path")" > /dev/null
    fi
}

main() {
    check_git_repo

    if [[ "$RELEASE_MODE" == true ]]; then
        increment_version "$VERSION_INCREMENT_TYPE"

        if git tag "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Created tag: $NEW_VERSION"
        else
            git tag -d "$NEW_VERSION" > /dev/null 2>&1 || true
            git tag "$NEW_VERSION" > /dev/null 2>&1
        fi

        if ! build_release_binary; then
            print_error "x86_64 release build failed; aborting release before push/upload"
            exit 1
        fi

        git_commit_and_push_no_tag

        local binary_path_x86="build/nsigner_static_x86_64"
        local binary_path_arm64="build/nsigner_static_arm64"
        local tarball_path=""
        tarball_path=$(create_source_tarball || true)

        local release_id=""
        release_id=$(create_gitea_release || true)

        if [[ -n "$release_id" ]]; then
            upload_release_assets "$release_id" "$binary_path_x86" "$tarball_path" "$binary_path_arm64"
        fi

        print_success "Release flow completed"
    else
        increment_version "$VERSION_INCREMENT_TYPE"

        if git tag "$NEW_VERSION" > /dev/null 2>&1; then
            print_success "Created tag: $NEW_VERSION"
        else
            git tag -d "$NEW_VERSION" > /dev/null 2>&1 || true
            git tag "$NEW_VERSION" > /dev/null 2>&1
        fi

        git_commit_and_push_no_tag
        print_success "Increment and push completed"
    fi
}

main
