#!/bin/bash

set -e

REPO="Start9Labs/start-os"
REGISTRY="https://alpha-registry-x.start9.com"
S3_BUCKET="s3://startos-images"
S3_CDN="https://startos-images.nyc3.cdn.digitaloceanspaces.com"
START9_GPG_KEY="2D63C217"

ARCHES="aarch64 aarch64-nonfree aarch64-nvidia riscv64 riscv64-nonfree x86_64 x86_64-nonfree x86_64-nvidia"
CLI_ARCHES="aarch64 riscv64 x86_64"

parse_run_id() {
    local val="$1"
    if [[ "$val" =~ /actions/runs/([0-9]+) ]]; then
        echo "${BASH_REMATCH[1]}"
    else
        echo "$val"
    fi
}

require_version() {
    if [ -z "${VERSION:-}" ]; then
        read -rp "VERSION: " VERSION
        if [ -z "$VERSION" ]; then
            >&2 echo '$VERSION required'
            exit 2
        fi
    fi
}

release_dir() {
    echo "$HOME/Downloads/v$VERSION"
}

ensure_release_dir() {
    local dir
    dir=$(release_dir)
    if [ "$CLEAN" = "1" ]; then
        rm -rf "$dir"
    fi
    mkdir -p "$dir"
    cd "$dir"
}

enter_release_dir() {
    local dir
    dir=$(release_dir)
    if [ ! -d "$dir" ]; then
        >&2 echo "Release directory $dir does not exist. Run 'download' or 'pull' first."
        exit 1
    fi
    cd "$dir"
}

cli_target_for() {
    local arch=$1 os=$2
    local pair="${arch}-${os}"
    if [ "$pair" = "riscv64-linux" ]; then
        echo "riscv64gc-unknown-linux-musl"
    elif [ "$pair" = "riscv64-macos" ]; then
        return 1
    elif [ "$os" = "linux" ]; then
        echo "${arch}-unknown-linux-musl"
    elif [ "$os" = "macos" ]; then
        echo "${arch}-apple-darwin"
    fi
}

release_files() {
    for file in *.iso *.squashfs *.deb; do
        [ -f "$file" ] && echo "$file"
    done
    for file in start-cli_*; do
        [[ "$file" == *.asc ]] && continue
        [ -f "$file" ] && echo "$file"
    done
}

resolve_gh_user() {
    GH_USER=${GH_USER:-$(gh api user -q .login 2>/dev/null || true)}
    GH_GPG_KEY=$(git config user.signingkey 2>/dev/null || true)
}

# --- Subcommands ---

cmd_download() {
    require_version

    if [ -z "${RUN_ID:-}" ]; then
        read -rp "RUN_ID (OS images, leave blank to skip): " RUN_ID
    fi
    RUN_ID=$(parse_run_id "${RUN_ID:-}")

    if [ -z "${ST_RUN_ID:-}" ]; then
        read -rp "ST_RUN_ID (start-tunnel, leave blank to skip): " ST_RUN_ID
    fi
    ST_RUN_ID=$(parse_run_id "${ST_RUN_ID:-}")

    if [ -z "${CLI_RUN_ID:-}" ]; then
        read -rp "CLI_RUN_ID (start-cli, leave blank to skip): " CLI_RUN_ID
    fi
    CLI_RUN_ID=$(parse_run_id "${CLI_RUN_ID:-}")

    ensure_release_dir

    if [ -n "$RUN_ID" ]; then
        for arch in $ARCHES; do
            while ! gh run download -R $REPO "$RUN_ID" -n "$arch.squashfs" -D "$(pwd)"; do sleep 1; done
        done
        for arch in $ARCHES; do
            while ! gh run download -R $REPO "$RUN_ID" -n "$arch.iso" -D "$(pwd)"; do sleep 1; done
        done
    fi

    if [ -n "$ST_RUN_ID" ]; then
        for arch in $CLI_ARCHES; do
            while ! gh run download -R $REPO "$ST_RUN_ID" -n "start-tunnel_$arch.deb" -D "$(pwd)"; do sleep 1; done
        done
    fi

    if [ -n "$CLI_RUN_ID" ]; then
        for arch in $CLI_ARCHES; do
            for os in linux macos; do
                local target
                target=$(cli_target_for "$arch" "$os") || continue
                while ! gh run download -R $REPO "$CLI_RUN_ID" -n "start-cli_$target" -D "$(pwd)"; do sleep 1; done
                mv start-cli "start-cli_${arch}-${os}"
            done
        done
    fi
}

cmd_pull() {
    require_version
    ensure_release_dir

    echo "Downloading release assets from tag v$VERSION..."

    # Download debs and CLI binaries from the GH release
    for file in $(gh release view -R $REPO "v$VERSION" --json assets -q '.assets[].name' | grep -E '\.(deb)$|^start-cli_'); do
        gh release download -R $REPO "v$VERSION" -p "$file" -D "$(pwd)" --clobber
    done

    # Download ISOs and squashfs from S3 CDN
    for arch in $ARCHES; do
        for ext in squashfs iso; do
            # Get the actual filename from the GH release asset list or body
            local filename
            filename=$(gh release view -R $REPO "v$VERSION" --json assets -q ".assets[].name" | grep "_${arch}\\.${ext}$" || true)
            if [ -z "$filename" ]; then
                filename=$(gh release view -R $REPO "v$VERSION" --json body -q .body | grep -oP "[^ ]*_${arch}\\.${ext}" | head -1 || true)
            fi
            if [ -n "$filename" ]; then
                echo "Downloading $filename from S3..."
                curl -fSL -o "$filename" "$S3_CDN/v$VERSION/$filename"
            fi
        done
    done
}

cmd_register() {
    require_version
    enter_release_dir
    start-cli --registry=$REGISTRY registry os version add "$VERSION" "v$VERSION" '' ">=0.3.5 <=$VERSION"
}

cmd_upload() {
    require_version
    enter_release_dir

    for file in $(release_files); do
        case "$file" in
            *.iso|*.squashfs)
                s3cmd put -P "$file" "$S3_BUCKET/v$VERSION/$file"
                ;;
            *)
                gh release upload -R $REPO "v$VERSION" "$file"
                ;;
        esac
    done
}

cmd_index() {
    require_version
    enter_release_dir

    for arch in $ARCHES; do
        for file in *_"$arch".squashfs *_"$arch".iso; do
            start-cli --registry=$REGISTRY registry os asset add --platform="$arch" --version="$VERSION" "$file" "$S3_CDN/v$VERSION/$file"
        done
    done
}

cmd_sign() {
    require_version
    enter_release_dir
    resolve_gh_user

    mkdir -p signatures

    for file in $(release_files); do
        gpg -u $START9_GPG_KEY --detach-sign --armor -o "signatures/${file}.start9.asc" "$file"
        if [ -n "$GH_USER" ] && [ -n "$GH_GPG_KEY" ]; then
            gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "signatures/${file}.${GH_USER}.asc" "$file"
        fi
    done

    gpg --export -a $START9_GPG_KEY > signatures/start9.key.asc
    if [ -n "$GH_USER" ] && [ -n "$GH_GPG_KEY" ]; then
        gpg --export -a "$GH_GPG_KEY" > "signatures/${GH_USER}.key.asc"
    else
        >&2 echo 'Warning: could not determine GitHub user or GPG signing key, skipping personal signature'
    fi
    tar -czvf signatures.tar.gz -C signatures .

    gh release upload -R $REPO "v$VERSION" signatures.tar.gz --clobber
}

cmd_cosign() {
    require_version
    enter_release_dir
    resolve_gh_user

    if [ -z "$GH_USER" ] || [ -z "$GH_GPG_KEY" ]; then
        >&2 echo 'Error: could not determine GitHub user or GPG signing key'
        >&2 echo "Set GH_USER and/or configure git user.signingkey"
        exit 1
    fi

    echo "Downloading existing signatures..."
    gh release download -R $REPO "v$VERSION" -p "signatures.tar.gz" -D "$(pwd)" --clobber
    mkdir -p signatures
    tar -xzf signatures.tar.gz -C signatures

    echo "Adding personal signatures as $GH_USER..."
    for file in $(release_files); do
        gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "signatures/${file}.${GH_USER}.asc" "$file"
    done

    gpg --export -a "$GH_GPG_KEY" > "signatures/${GH_USER}.key.asc"

    echo "Re-packing signatures..."
    tar -czvf signatures.tar.gz -C signatures .

    gh release upload -R $REPO "v$VERSION" signatures.tar.gz --clobber
    echo "Done. Personal signatures for $GH_USER added to v$VERSION."
}

cmd_notes() {
    require_version
    enter_release_dir

    cat << EOF
# ISO Downloads

- [x86_64/AMD64]($S3_CDN/v$VERSION/$(ls *_x86_64-nonfree.iso))
- [x86_64/AMD64 + NVIDIA]($S3_CDN/v$VERSION/$(ls *_x86_64-nvidia.iso))
- [x86_64/AMD64-slim (FOSS-only)]($S3_CDN/v$VERSION/$(ls *_x86_64.iso) "Without proprietary software or drivers")
- [aarch64/ARM64]($S3_CDN/v$VERSION/$(ls *_aarch64-nonfree.iso))
- [aarch64/ARM64 + NVIDIA]($S3_CDN/v$VERSION/$(ls *_aarch64-nvidia.iso))
- [aarch64/ARM64-slim (FOSS-Only)]($S3_CDN/v$VERSION/$(ls *_aarch64.iso) "Without proprietary software or drivers")
- [RISCV64 (RVA23)]($S3_CDN/v$VERSION/$(ls *_riscv64-nonfree.iso))
- [RISCV64 (RVA23)-slim (FOSS-only)]($S3_CDN/v$VERSION/$(ls *_riscv64.iso) "Without proprietary software or drivers")

EOF
    cat << 'EOF'
# StartOS Checksums

## SHA-256
```
EOF
    sha256sum *.iso *.squashfs
    cat << 'EOF'
```

## BLAKE-3
```
EOF
    b3sum *.iso *.squashfs
    cat << 'EOF'
```

# Start-Tunnel Checksums

## SHA-256
```
EOF
    sha256sum start-tunnel*.deb
    cat << 'EOF'
```

## BLAKE-3
```
EOF
    b3sum start-tunnel*.deb
    cat << 'EOF'
```

# start-cli Checksums

## SHA-256
```
EOF
    release_files | grep '^start-cli_' | xargs sha256sum
    cat << 'EOF'
```

## BLAKE-3
```
EOF
    release_files | grep '^start-cli_' | xargs b3sum
    cat << 'EOF'
```
EOF
}

cmd_full_release() {
    cmd_download
    cmd_register
    cmd_upload
    cmd_index
    cmd_sign
    cmd_notes
}

usage() {
    cat << 'EOF'
Usage: manage-release.sh <subcommand>

Subcommands:
  download      Download artifacts from GitHub Actions runs
                Requires: RUN_ID, ST_RUN_ID, CLI_RUN_ID (any combination)
  pull          Download an existing release from the GH tag and S3
  register      Register the version in the Start9 registry
  upload        Upload artifacts to GitHub Releases and S3
  index         Add assets to the registry index
  sign          Sign all artifacts with Start9 org key (+ personal key if available)
                and upload signatures.tar.gz
  cosign        Add personal GPG signature to an existing release's signatures
                (requires 'pull' first so you can verify assets before signing)
  notes         Print release notes with download links and checksums
  full-release  Run: download → register → upload → index → sign → notes

Environment variables:
  VERSION     (required) Release version
  RUN_ID      GitHub Actions run ID for OS images (download subcommand)
  ST_RUN_ID   GitHub Actions run ID for start-tunnel (download subcommand)
  CLI_RUN_ID  GitHub Actions run ID for start-cli (download subcommand)
  GH_USER     Override GitHub username (default: autodetected via gh cli)
  CLEAN       Set to 1 to wipe and recreate the release directory
EOF
}

case "${1:-}" in
    download) cmd_download ;;
    pull)     cmd_pull ;;
    register) cmd_register ;;
    upload)   cmd_upload ;;
    index)    cmd_index ;;
    sign)     cmd_sign ;;
    cosign)   cmd_cosign ;;
    notes)    cmd_notes ;;
    full-release) cmd_full_release ;;
    *)            usage; exit 1 ;;
esac
