#!/bin/bash
# Check PKGBUILD versions against published repo versions
# Creates state files for edge and/or stable if any packages need building

set -e

BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/package-metadata.sh"
source "$BUILD_ROOT/helpers/lock-helpers.sh"

STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}"
ARCH="${ARCH:-x86_64}"
PULL=false

[[ "${1:-}" == "--pull" ]] && PULL=true

mkdir -p "$STATE_DIR"

# Runs on a tight schedule, so it must never disturb a build in progress:
# comparing versions reads the PKGBUILDs a running build is reading, and
# --pull would swap them underneath it. Take the lock non-blocking and skip
# the tick when a release owns it — the queue is unchanged, so the next tick
# picks up exactly where this one left off.
if ! try_release_lock; then
  exit 0
fi

print_header "Package Version Check"

# The scheduled run pulls; a manual run leaves the operator's tree alone.
if [[ "$PULL" == true ]]; then
  print_info "Updating repository..."
  git -C "$BUILD_ROOT" pull --ff-only || print_warning "git pull failed — checking the current tree"
fi

get_repo_version() {
  local pkg="$1"
  local mirror="$2"
  local repo_db="$REPO_ROOT/$mirror/$ARCH/omarchy.db.tar.zst"

  [[ -f "$repo_db" ]] || return 0

  tar -xOf "$repo_db" --wildcards '*/desc' 2>/dev/null | awk -v pkg="$pkg" '
    function reset() { name=""; base=""; version="" }
    function emit() {
      if (!found && version != "" && (name == pkg || base == pkg)) {
        print version
        found=1
      }
      reset()
    }
    $0 == "%FILENAME%" { emit(); next }
    $0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
    $0 == "%BASE%" { getline; base=$0; next }
    $0 == "%VERSION%" { getline; version=$0; next }
    END { emit() }
  '
}

get_pkgbuild_version() {
  local pkgdir="$1"
  if [[ -f "$pkgdir/PKGBUILD" ]]; then
    (cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; if [[ -n "$epoch" ]]; then echo "${epoch}:${pkgver}-${pkgrel}"; else echo "${pkgver}-${pkgrel}"; fi')
  fi
}

check_vcs_unchanged() {
  local pkgdir="$1"
  local repo_version="$2"
  local pkgbuild="$pkgdir/PKGBUILD"

  [[ -f "$pkgbuild" ]] || return 1
  grep -qE '^pkgver[[:space:]]*\(\)' "$pkgbuild" || return 1
  [[ -n "$repo_version" ]] || return 1

  # If epoch or pkgrel changed in PKGBUILD, release even if upstream is unchanged.
  local pkgbuild_epoch pkgbuild_pkgrel
  pkgbuild_epoch=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${epoch:-}"')
  pkgbuild_pkgrel=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${pkgrel}"')

  local repo_pkgrel="${repo_version##*-}"
  local repo_no_pkgrel="${repo_version%-*}"
  local repo_epoch=""
  if [[ "$repo_no_pkgrel" == *:* ]]; then
    repo_epoch="${repo_no_pkgrel%%:*}"
  fi

  [[ "$pkgbuild_epoch" != "$repo_epoch" ]] && return 1
  [[ "$pkgbuild_pkgrel" != "$repo_pkgrel" ]] && return 1

  local repo_hash upstream_hash
  repo_hash=$(package_extract_vcs_hash_from_version "$repo_version")
  [[ -n "$repo_hash" ]] || return 1

  upstream_hash=$(package_git_upstream_hash "$pkgdir")
  [[ -n "$upstream_hash" ]] || return 1

  [[ "$repo_hash" == "$upstream_hash" ]]
}

check_package() {
  local pkg="$1"
  local pkgdir="$2"
  local mirror="$3"

  local pkgbuild_version repo_version
  pkgbuild_version=$(get_pkgbuild_version "$pkgdir")
  repo_version=$(get_repo_version "$pkg" "$mirror")

  if [[ -z "$pkgbuild_version" ]]; then
    return 1
  fi

  if grep -qE '^pkgver[[:space:]]*\(\)' "$pkgdir/PKGBUILD"; then
    if [[ -n "$repo_version" && -n "$(package_extract_vcs_hash_from_version "$repo_version")" ]]; then
      if check_vcs_unchanged "$pkgdir" "$repo_version"; then
        return 1
      else
        return 0
      fi
    elif [[ "$pkgbuild_version" == "$repo_version" ]]; then
      return 1
    else
      return 0
    fi
  fi

  if [[ "$pkgbuild_version" == "$repo_version" ]]; then
    return 1
  fi

  # Different is not the same as newer. A checkout behind the published state
  # — a stale branch, an un-pulled host — otherwise queues a REBUILD OF AN
  # OLDER VERSION every cycle: the builder produces it, and promotion refuses
  # it because that exact filename is already published with different bytes.
  # If the artifact for this version already exists in the channel, there is
  # nothing to build regardless of which direction the versions differ.
  if compgen -G "$REPO_ROOT/$mirror/$ARCH/${pkg}-${pkgbuild_version}-*.pkg.tar."[!s]* >/dev/null 2>&1; then
    print_warning "$pkg $pkgbuild_version is already published — this checkout is behind the channel; not queueing"
    return 1
  fi

  return 0
}

check_mirror() {
  local mirror="$1"
  local state_file="$STATE_DIR/.sync-needed-$mirror"
  local needs_build=false
  local packages=()

  print_info "Checking $mirror packages..."

  while IFS= read -r pkg; do
    local pkgdir="$PKGBUILDS_DIR/$pkg"
    if check_package "$pkg" "$pkgdir" "$mirror"; then
      needs_build=true
      packages+=("$pkg")
    fi
  done < <(packages_for_unscoped_build "$mirror")

  echo ""

  if [[ "$needs_build" == true ]]; then
    # Record WHAT is queued, not just THAT something is: the release run reads
    # this to name the packages in its start report, which is the difference
    # between "a build is running" and "your package is in this build".
    printf '%s\n' "${packages[@]}" >"$state_file"
    print_success "${mirror^} needs building (${#packages[@]} packages)"
    print_info "Packages: ${packages[*]}"
    print_info "State file created: $state_file"
  else
    print_info "${mirror^} is up to date"
  fi

  echo ""
}

check_mirror edge
check_mirror rc
check_mirror stable

print_success "Version check complete!"
