#!/usr/bin/env bash
set -euo pipefail

# debrepo-poll — Poll GitHub Releases for .deb assets and import them.
# Installed at /usr/sbin/debrepo-poll, triggered by debrepo-poll.timer.

CONF_DIR="/etc/debrepo"
DATA_DIR="/var/lib/debrepo"
STATE_FILE="$DATA_DIR/db/poll-state"
SOURCES_FILE="$CONF_DIR/github-sources"
TOKEN_FILE="$CONF_DIR/github-token"
LOCK_FILE="/run/lock/debrepo-poll.lock"
API_BASE="https://api.github.com"

# Filename convention: <pkg>_<ver>_<codename>_<arch>.deb
DEB_PATTERN='^[a-zA-Z0-9][a-zA-Z0-9.+~-]*_[^_]+_[^_]+_[^_]+\.deb$'

COUNTER_FILE=""
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') debrepo-poll: $*"; }

# --- Acquire exclusive lock ---
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
    log "Another instance is running, exiting."
    exit 0
fi

# --- Load GitHub token (optional — supports tokenless public-only mode) ---
auth_header=""
if [ -f "$TOKEN_FILE" ] && [ -s "$TOKEN_FILE" ]; then
    token=$(head -1 "$TOKEN_FILE" | tr -d '[:space:]')
    auth_header="Authorization: token $token"
fi

# --- Exit if no sources file ---
if [ ! -f "$SOURCES_FILE" ]; then
    log "No sources file at $SOURCES_FILE, nothing to poll."
    exit 0
fi

# --- Ensure state file exists ---
touch "$STATE_FILE"

# --- Temp dir with cleanup ---
tmp_dir=$(mktemp -d /tmp/debrepo-poll.XXXXXX)
COUNTER_FILE="$tmp_dir/counters"
echo "0 0" > "$COUNTER_FILE"
cleanup() { rm -rf "$tmp_dir"; }
trap cleanup EXIT

# --- Build curl auth args ---
curl_auth=()
if [ -n "$auth_header" ]; then
    curl_auth=(-H "$auth_header")
fi

# --- Process each repo ---
while IFS= read -r line || [ -n "$line" ]; do
    # Skip blanks and comments
    line=$(echo "$line" | sed 's/#.*//' | xargs)
    [ -z "$line" ] && continue

    repo="$line"
    log "Checking $repo..."

    # Fetch releases (last 100 — monorepos publish many packages per push)
    http_code=$(curl -s -o "$tmp_dir/releases.json" -w "%{http_code}" \
        "${curl_auth[@]+"${curl_auth[@]}"}" \
        -H "Accept: application/vnd.github+json" \
        "$API_BASE/repos/$repo/releases?per_page=100" 2>/dev/null || echo "000")

    if [ "$http_code" = "403" ] || [ "$http_code" = "429" ]; then
        log "Rate limited ($http_code) on $repo, stopping."
        break
    fi

    if [ "$http_code" != "200" ]; then
        log "HTTP $http_code fetching releases for $repo, skipping."
        read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
        continue
    fi

    # Extract .deb assets to a file: tag_name, asset name, API download URL
    jq -r '
        .[] | .tag_name as $tag |
        .assets[] |
        select(.name | test("\\.deb$")) |
        [$tag, .name, .url] | @tsv
    ' "$tmp_dir/releases.json" > "$tmp_dir/assets.tsv" 2>/dev/null || true

    while IFS=$'\t' read -r tag asset_name asset_url; do
        [ -z "$tag" ] && continue

        # Check if already imported
        if grep -qF "$repo $tag $asset_name" "$STATE_FILE"; then
            continue
        fi

        log "  New asset: $repo $tag $asset_name"

        # Validate filename pattern
        if ! echo "$asset_name" | grep -qE "$DEB_PATTERN"; then
            log "  Skipping $asset_name: does not match expected filename pattern."
            read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
            continue
        fi

        # Parse codename from filename: <pkg>_<ver>_<codename>_<arch>.deb
        codename=$(echo "$asset_name" | sed -E 's/^[^_]+_[^_]+_([^_]+)_[^_]+\.deb$/\1/')
        if [ "$codename" = "$asset_name" ]; then
            log "  Skipping $asset_name: could not parse codename from filename."
            read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
            continue
        fi

        # Download asset via API URL (works for both public and private repos)
        deb_path="$tmp_dir/$asset_name"
        dl_code=$(curl -sL -o "${deb_path}.part" -w "%{http_code}" \
            "${curl_auth[@]+"${curl_auth[@]}"}" \
            -H "Accept: application/octet-stream" \
            "$asset_url" 2>/dev/null || echo "000")

        if [ "$dl_code" != "200" ]; then
            log "  Download failed (HTTP $dl_code) for $asset_name, skipping."
            rm -f "${deb_path}.part"
            read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
            continue
        fi
        mv "${deb_path}.part" "$deb_path"

        # Verify valid deb structure
        if ! dpkg-deb -I "$deb_path" >/dev/null 2>&1; then
            log "  Invalid .deb structure: $asset_name, skipping."
            rm -f "$deb_path"
            read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
            continue
        fi

        # Import via debrepo-add-package (enforces allowlist)
        if debrepo-add-package "$codename" "$deb_path" 2>&1; then
            echo "$repo $tag $asset_name" >> "$STATE_FILE"
            read -r i e < "$COUNTER_FILE"; echo "$((i + 1)) $e" > "$COUNTER_FILE"
            log "  Imported $asset_name to $codename."
        else
            log "  Failed to import $asset_name."
            read -r i e < "$COUNTER_FILE"; echo "$i $((e + 1))" > "$COUNTER_FILE"
        fi

        rm -f "$deb_path"
    done < "$tmp_dir/assets.tsv"
done < "$SOURCES_FILE"

read -r imported errors < "$COUNTER_FILE"
log "Done. Imported: $imported, Errors: $errors"
