#!/usr/bin/env bash
#
# GDELT CLI Release Script
#
# Builds cross-platform binaries and publishes to GitHub Releases.
# Run from Linux (x86_64) to build for all targets.
#
# Usage:
#   ./release.sh [VERSION]       # Build and release (e.g., ./release.sh v0.2.0)
#   ./release.sh --build-only    # Build all targets without releasing
#   ./release.sh --help          # Show help
#
# Prerequisites:
#   - Rust toolchain (rustup)
#   - cross (cargo install cross)
#   - gh CLI (GitHub CLI, authenticated)
#   - zip (for Windows archives)
#
# Targets:
#   - Linux x86_64 (gnu)
#   - Linux aarch64 (gnu)
#   - macOS x86_64
#   - macOS aarch64 (Apple Silicon)
#   - Windows x86_64 (gnu)
#

set -euo pipefail

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

# Project configuration
PROJECT_NAME="gdelt"
REPO="neul-labs/gdelt-cli"

# Build targets
# Note: macOS targets require osxcross or native macOS build
# Use --include-macos to attempt macOS builds (requires osxcross)
declare -A TARGETS_LINUX=(
    ["x86_64-unknown-linux-gnu"]="linux-x86_64"
    ["aarch64-unknown-linux-gnu"]="linux-aarch64"
    ["x86_64-pc-windows-gnu"]="windows-x86_64"
)

declare -A TARGETS_MACOS=(
    ["x86_64-apple-darwin"]="darwin-x86_64"
    ["aarch64-apple-darwin"]="darwin-aarch64"
)

# Combined targets (set based on flags)
declare -A TARGETS

# Directory setup
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="${SCRIPT_DIR}/target/release-builds"
DIST_DIR="${SCRIPT_DIR}/dist"

# Logging functions
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

# Show usage
show_help() {
    cat << EOF
GDELT CLI Release Script

Usage:
    ./release.sh [OPTIONS] [VERSION]

Arguments:
    VERSION         Version tag (e.g., v0.2.0). If not provided, uses Cargo.toml version.

Options:
    --build-only    Build all targets without creating a GitHub release
    --dry-run       Show what would be done without executing
    --skip-build    Skip building, only create release from existing artifacts
    --include-macos Attempt to build macOS targets (requires osxcross)
    --linux-only    Only build Linux targets
    --help          Show this help message

Examples:
    ./release.sh v0.2.0                # Build and release version 0.2.0
    ./release.sh                       # Build and release using Cargo.toml version
    ./release.sh --build-only          # Build only, don't release
    ./release.sh --skip-build v0.2.0   # Release existing builds
    ./release.sh --include-macos       # Include macOS builds (requires osxcross)
    ./release.sh --linux-only          # Only Linux builds

Prerequisites:
    1. Install cross: cargo install cross
    2. Install GitHub CLI: https://cli.github.com/
    3. Authenticate: gh auth login
    4. Install Docker (required by cross for some targets)

Default targets (from Linux):
    - Linux x86_64 (glibc)
    - Linux aarch64 (glibc)
    - Windows x86_64 (mingw)

Additional targets (with --include-macos):
    - macOS x86_64 (requires osxcross)
    - macOS aarch64 (requires osxcross)

Note: For full cross-platform releases including macOS, consider using
the GitHub Actions workflow (.github/workflows/release.yml) instead.

EOF
}

# Get version from Cargo.toml
get_cargo_version() {
    grep '^version' "${SCRIPT_DIR}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/'
}

# Check prerequisites
check_prerequisites() {
    local missing=()

    if ! command -v rustup &> /dev/null; then
        missing+=("rustup")
    fi

    if ! command -v cross &> /dev/null; then
        missing+=("cross (install with: cargo install cross)")
    fi

    if ! command -v gh &> /dev/null; then
        missing+=("gh (GitHub CLI)")
    fi

    if ! command -v zip &> /dev/null; then
        missing+=("zip")
    fi

    if ! command -v docker &> /dev/null; then
        log_warn "Docker not found. Some cross-compilation targets may fail."
    fi

    if [ ${#missing[@]} -gt 0 ]; then
        log_error "Missing prerequisites:"
        for item in "${missing[@]}"; do
            echo "  - $item"
        done
        exit 1
    fi

    # Check GitHub CLI authentication
    if ! gh auth status &> /dev/null; then
        log_error "GitHub CLI not authenticated. Run: gh auth login"
        exit 1
    fi

    log_success "All prerequisites met"
}

# Install Rust targets
install_targets() {
    log_info "Installing Rust targets..."

    for target in "${!TARGETS[@]}"; do
        log_info "  Adding target: $target"
        rustup target add "$target" 2>/dev/null || true
    done

    log_success "Targets installed"
}

# Build for a single target
build_target() {
    local target="$1"
    local platform="${TARGETS[$target]}"
    local features="${2:-full}"

    log_info "Building for $target ($platform)..."

    # Use cross for cross-compilation
    if cross build --release --target "$target" --features "$features" 2>&1; then
        log_success "Built $platform"
        return 0
    else
        log_error "Failed to build $platform"
        return 1
    fi
}

# Build all targets
build_all() {
    local features="${1:-full}"
    local failed=()

    log_info "Building all targets with features: $features"

    mkdir -p "$BUILD_DIR"

    for target in "${!TARGETS[@]}"; do
        if ! build_target "$target" "$features"; then
            failed+=("$target")
        fi
    done

    if [ ${#failed[@]} -gt 0 ]; then
        log_warn "Some targets failed to build:"
        for target in "${failed[@]}"; do
            echo "  - $target"
        done
        return 1
    fi

    log_success "All targets built successfully"
}

# Create distribution archives
create_archives() {
    local version="$1"

    log_info "Creating distribution archives for $version..."

    rm -rf "$DIST_DIR"
    mkdir -p "$DIST_DIR"

    for target in "${!TARGETS[@]}"; do
        local platform="${TARGETS[$target]}"
        local src_dir="${SCRIPT_DIR}/target/${target}/release"
        local binary="${PROJECT_NAME}"
        local ext=""
        local archive_type="tar.gz"

        # Windows uses .exe and .zip
        if [[ "$target" == *"windows"* ]]; then
            ext=".exe"
            archive_type="zip"
        fi

        local src_binary="${src_dir}/${binary}${ext}"

        if [ ! -f "$src_binary" ]; then
            log_warn "Binary not found: $src_binary (skipping)"
            continue
        fi

        local archive_name="${PROJECT_NAME}-${version}-${platform}"
        local staging_dir="${BUILD_DIR}/${archive_name}"

        # Create staging directory with binary and docs
        rm -rf "$staging_dir"
        mkdir -p "$staging_dir"

        cp "$src_binary" "$staging_dir/"

        # Include documentation if available
        [ -f "${SCRIPT_DIR}/README.md" ] && cp "${SCRIPT_DIR}/README.md" "$staging_dir/"
        [ -f "${SCRIPT_DIR}/LICENSE" ] && cp "${SCRIPT_DIR}/LICENSE" "$staging_dir/"
        [ -f "${SCRIPT_DIR}/CHANGELOG.md" ] && cp "${SCRIPT_DIR}/CHANGELOG.md" "$staging_dir/"

        # Create archive
        pushd "$BUILD_DIR" > /dev/null

        if [ "$archive_type" = "zip" ]; then
            zip -r "${DIST_DIR}/${archive_name}.zip" "$archive_name"
            log_success "Created ${archive_name}.zip"
        else
            tar -czf "${DIST_DIR}/${archive_name}.tar.gz" "$archive_name"
            log_success "Created ${archive_name}.tar.gz"
        fi

        popd > /dev/null

        # Cleanup staging
        rm -rf "$staging_dir"
    done

    # Create checksums
    log_info "Creating checksums..."
    pushd "$DIST_DIR" > /dev/null
    sha256sum * > "checksums-${version}.sha256"
    popd > /dev/null

    log_success "Archives created in $DIST_DIR"
    ls -la "$DIST_DIR"
}

# Create GitHub release
create_release() {
    local version="$1"
    local dry_run="${2:-false}"

    log_info "Creating GitHub release $version..."

    # Check if release already exists
    if gh release view "$version" --repo "$REPO" &> /dev/null; then
        log_warn "Release $version already exists"
        read -p "Delete and recreate? [y/N] " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            if [ "$dry_run" = "true" ]; then
                log_info "[DRY-RUN] Would delete release $version"
            else
                gh release delete "$version" --repo "$REPO" --yes
                log_info "Deleted existing release"
            fi
        else
            log_error "Aborted"
            exit 1
        fi
    fi

    # Generate release notes
    local release_notes=$(cat << EOF
## GDELT CLI ${version}

### Installation

**Quick install (Linux/macOS):**
\`\`\`bash
curl -fsSL https://raw.githubusercontent.com/${REPO}/main/install.sh | bash
\`\`\`

**Manual download:**
Download the appropriate archive for your platform below.

### Platforms

| Platform | Architecture | Archive |
|----------|--------------|---------|
| Linux | x86_64 | \`${PROJECT_NAME}-${version}-linux-x86_64.tar.gz\` |
| Linux | aarch64 (ARM64) | \`${PROJECT_NAME}-${version}-linux-aarch64.tar.gz\` |
| macOS | x86_64 (Intel) | \`${PROJECT_NAME}-${version}-darwin-x86_64.tar.gz\` |
| macOS | aarch64 (Apple Silicon) | \`${PROJECT_NAME}-${version}-darwin-aarch64.tar.gz\` |
| Windows | x86_64 | \`${PROJECT_NAME}-${version}-windows-x86_64.zip\` |

### Verification

Verify downloads with checksums:
\`\`\`bash
sha256sum -c checksums-${version}.sha256
\`\`\`

### What's New

See [CHANGELOG](https://github.com/${REPO}/blob/main/CHANGELOG.md) for details.
EOF
)

    if [ "$dry_run" = "true" ]; then
        log_info "[DRY-RUN] Would create release $version with notes:"
        echo "$release_notes"
        log_info "[DRY-RUN] Would upload files from $DIST_DIR"
        return
    fi

    # Create release
    gh release create "$version" \
        --repo "$REPO" \
        --title "GDELT CLI ${version}" \
        --notes "$release_notes" \
        --draft \
        "${DIST_DIR}"/*

    log_success "Release $version created (draft)"
    log_info "Review and publish at: https://github.com/${REPO}/releases"
}

# Update version in Cargo.toml
update_version() {
    local new_version="$1"
    local dry_run="${2:-false}"

    # Remove 'v' prefix if present
    new_version="${new_version#v}"

    local current_version=$(get_cargo_version)

    if [ "$current_version" = "$new_version" ]; then
        log_info "Version already set to $new_version"
        return
    fi

    log_info "Updating version: $current_version -> $new_version"

    if [ "$dry_run" = "true" ]; then
        log_info "[DRY-RUN] Would update Cargo.toml version to $new_version"
        return
    fi

    sed -i "s/^version = \".*\"/version = \"$new_version\"/" "${SCRIPT_DIR}/Cargo.toml"

    # Update Cargo.lock
    cargo update -p "$PROJECT_NAME" --precise "$new_version" 2>/dev/null || cargo check 2>/dev/null || true

    log_success "Version updated to $new_version"
}

# Initialize targets based on flags
init_targets() {
    local include_macos="$1"
    local linux_only="$2"

    # Always include Linux and Windows (unless linux-only)
    for target in "${!TARGETS_LINUX[@]}"; do
        if [ "$linux_only" = "true" ] && [[ "$target" == *"windows"* ]]; then
            continue
        fi
        TARGETS["$target"]="${TARGETS_LINUX[$target]}"
    done

    # Include macOS if requested
    if [ "$include_macos" = "true" ]; then
        for target in "${!TARGETS_MACOS[@]}"; do
            TARGETS["$target"]="${TARGETS_MACOS[$target]}"
        done
    fi

    log_info "Build targets:"
    for target in "${!TARGETS[@]}"; do
        echo "  - $target (${TARGETS[$target]})"
    done
}

# Main entry point
main() {
    local version=""
    local build_only=false
    local dry_run=false
    local skip_build=false
    local include_macos=false
    local linux_only=false

    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --help|-h)
                show_help
                exit 0
                ;;
            --build-only)
                build_only=true
                shift
                ;;
            --dry-run)
                dry_run=true
                shift
                ;;
            --skip-build)
                skip_build=true
                shift
                ;;
            --include-macos)
                include_macos=true
                shift
                ;;
            --linux-only)
                linux_only=true
                shift
                ;;
            -*)
                log_error "Unknown option: $1"
                show_help
                exit 1
                ;;
            *)
                version="$1"
                shift
                ;;
        esac
    done

    # Initialize targets
    init_targets "$include_macos" "$linux_only"

    # Get version from Cargo.toml if not provided
    if [ -z "$version" ]; then
        version="v$(get_cargo_version)"
        log_info "Using version from Cargo.toml: $version"
    fi

    # Ensure version starts with 'v'
    if [[ ! "$version" =~ ^v ]]; then
        version="v$version"
    fi

    log_info "========================================"
    log_info "GDELT CLI Release: $version"
    log_info "========================================"

    if [ "$dry_run" = "true" ]; then
        log_warn "DRY RUN MODE - No changes will be made"
    fi

    # Check prerequisites (skip for dry run if gh not installed)
    if [ "$dry_run" = "false" ] || [ "$build_only" = "true" ]; then
        check_prerequisites
    fi

    # Install targets
    if [ "$skip_build" = "false" ]; then
        install_targets

        # Build all targets
        build_all "full"
    fi

    # Create archives
    create_archives "$version"

    # Create release unless build-only
    if [ "$build_only" = "false" ]; then
        create_release "$version" "$dry_run"
    fi

    log_success "========================================"
    log_success "Release $version complete!"
    log_success "========================================"

    if [ "$build_only" = "false" ] && [ "$dry_run" = "false" ]; then
        echo ""
        log_info "Next steps:"
        echo "  1. Review the draft release on GitHub"
        echo "  2. Edit release notes if needed"
        echo "  3. Publish the release"
        echo ""
        echo "  https://github.com/${REPO}/releases"
    fi
}

# Run main
main "$@"
