#!/bin/bash
# Prepare this machine to serve as the Omarchy repository host.
#
# The host receives uploads, promotes packages into the published tree, and
# syncs that tree to the mirror. Everything Arch-specific — makepkg, repo-add,
# package signing — happens inside containers, so the host itself needs very
# little and does not need to be Arch. The production host is Ubuntu.
#
# Run this on the host itself:
#   ssh root@<host> 'cd /root/omarchy-pkgs && bin/setup'
#
# It is idempotent — run it again after adding a dependency.

set -e

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

CHECK_ONLY=false
SKIP_TIMERS=false

STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}"
CREDENTIALS="/root/.omarchy/build-credentials"

print_header "Omarchy Repository Host Setup"

while [[ $# -gt 0 ]]; do
  case $1 in
  --check)
    CHECK_ONLY=true
    shift
    ;;
  --skip-timers)
    SKIP_TIMERS=true
    shift
    ;;
  -h | --help)
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Install and enable everything the repository host needs."
    echo "Works on Debian/Ubuntu (apt) and Arch (pacman)."
    echo ""
    echo "Options:"
    echo "  --check         Report what is missing, change nothing"
    echo "  --skip-timers   Do not install or enable the release timers"
    echo "  -h, --help      Show this help message"
    exit 0
    ;;
  *)
    print_error "Unknown option: $1"
    exit 1
    ;;
  esac
done

# --- distribution ------------------------------------------------------------

# Package names differ where it matters: bsdtar is libarchive-tools on Debian
# and libarchive on Arch, and Docker is docker.io rather than docker.
if command -v apt-get >/dev/null 2>&1; then
  DISTRO="debian"
  PKG_BSDTAR="libarchive-tools"
  PKG_DOCKER="docker.io"
elif command -v pacman >/dev/null 2>&1; then
  DISTRO="arch"
  PKG_BSDTAR="libarchive"
  PKG_DOCKER="docker"
else
  print_error "Unsupported distribution — need apt-get or pacman"
  exit 1
fi

print_info "Distribution: $DISTRO"

if [[ "$CHECK_ONLY" != true && $EUID -ne 0 ]]; then
  print_error "Run as root (installing packages and systemd units)"
  exit 1
fi

# Docker and the release timers are both systemd units. Say so plainly rather
# than failing later on a missing command — a container is the usual way to end
# up here, and it cannot be a repository host.
if [[ "$CHECK_ONLY" != true ]] && ! command -v systemctl >/dev/null 2>&1; then
  print_error "systemctl not found — the repository host must run systemd"
  echo ""
  echo "Docker and the release timers are systemd units. This looks like a"
  echo "container; run setup on the host itself."
  exit 1
fi

install_packages() {
  case "$DISTRO" in
  debian)
    apt-get update -qq
    DEBIAN_FRONTEND=noninteractive apt-get install -y "$@"
    ;;
  arch)
    pacman -S --needed --noconfirm "$@"
    ;;
  esac
}

# --- dependencies ------------------------------------------------------------

# Only what the host runs directly. Signing and repo-add happen in containers,
# so gnupg and the Arch build tools are deliberately absent from this list.
REQUIREMENTS=(
  "bsdtar:$PKG_BSDTAR" # reads repo databases and .PKGINFO out of packages
  "git:git"            # pulls this repository
  "jq:jq"              # package metadata in .omarchy/package.json
  "rsync:rsync"        # receives uploads from bin/repo push
  "rclone:rclone"      # publishes to the mirror
)

print_info "Checking dependencies..."
MISSING_PACKAGES=()
for requirement in "${REQUIREMENTS[@]}"; do
  cmd="${requirement%%:*}"
  pkg="${requirement#*:}"
  if command -v "$cmd" >/dev/null 2>&1; then
    print_step "$cmd"
  else
    print_warning "$cmd missing (provided by $pkg)"
    MISSING_PACKAGES+=("$pkg")
  fi
done
echo ""

if [[ ${#MISSING_PACKAGES[@]} -gt 0 ]]; then
  if [[ "$CHECK_ONLY" == true ]]; then
    print_warning "Would install: ${MISSING_PACKAGES[*]}"
  else
    print_info "Installing: ${MISSING_PACKAGES[*]}"
    install_packages "${MISSING_PACKAGES[@]}"
    print_success "Dependencies installed"
  fi
else
  print_success "All dependencies present"
fi
echo ""

# --- docker ------------------------------------------------------------------

# Docker is left alone when it already works. A host may well be running a
# version from Docker's own repository rather than the distribution's, and
# replacing that underneath a working builder would be a poor trade for
# tidiness.
print_info "Checking Docker..."

if command -v docker >/dev/null 2>&1; then
  print_step "docker present: $(docker --version 2>/dev/null | head -1)"
  if docker info >/dev/null 2>&1; then
    print_success "Docker is installed and running — leaving it alone"
  elif [[ "$CHECK_ONLY" == true ]]; then
    print_warning "Docker is installed but not running; would start it"
  else
    print_info "Docker is installed but not running — starting it"
    systemctl enable --now docker.service
    if docker info >/dev/null 2>&1; then
      print_success "Docker started"
    else
      print_error "Docker is installed but still not responding"
      echo "    Check 'systemctl status docker' — builds cannot run without it."
      exit 1
    fi
  fi
elif [[ "$CHECK_ONLY" == true ]]; then
  print_warning "Would install $PKG_DOCKER and enable it"
else
  print_info "Installing $PKG_DOCKER..."
  install_packages "$PKG_DOCKER"
  systemctl enable --now docker.service
  if docker info >/dev/null 2>&1; then
    print_success "Docker installed and running"
  else
    print_error "Docker installed but not responding"
    echo "    Check 'systemctl status docker' — builds cannot run without it."
    exit 1
  fi
fi
echo ""

# --- state directory ---------------------------------------------------------

if [[ -d "$STATE_DIR" ]]; then
  print_success "State directory present: $STATE_DIR"
elif [[ "$CHECK_ONLY" == true ]]; then
  print_warning "Would create $STATE_DIR"
else
  mkdir -p "$STATE_DIR"
  print_success "Created $STATE_DIR"
fi
echo ""

# --- rc branch worktree -------------------------------------------------------

# The rc channel builds from the standing `rc` branch (where the release
# orchestrator commits RC PKGBUILD pins) in its own worktree, so rc builds
# never contend with the master checkout. OMARCHY_REPO_ROOT in the rc service
# points its published tree back at this checkout, so all three channels live
# together.
RC_WORKTREE="/root/omarchy-pkgs-rc"

# Read-only: --check must not fetch. Local refs first, then a network peek.
rc_branch_exists() {
  git -C "$BUILD_ROOT" show-ref --verify --quiet refs/heads/rc && return 0
  git -C "$BUILD_ROOT" show-ref --verify --quiet refs/remotes/origin/rc && return 0
  git -C "$BUILD_ROOT" ls-remote --exit-code --heads origin rc >/dev/null 2>&1
}

if [[ "$CHECK_ONLY" == true ]]; then
  if [[ -d "$RC_WORKTREE" ]]; then
    print_success "rc worktree present: $RC_WORKTREE"
  elif rc_branch_exists; then
    print_warning "rc worktree would be created at $RC_WORKTREE"
  else
    # Expected before the first RC: nothing to create, and nothing to do —
    # omarchy-release creates the branch and the rc build trigger creates the
    # worktree on demand, so this resolves itself.
    print_info "rc worktree not needed yet (no rc branch — created on the first RC cut)"
  fi
elif [[ -d "$RC_WORKTREE" ]]; then
  print_success "rc worktree present: $RC_WORKTREE"
else
  print_info "Creating rc branch worktree at $RC_WORKTREE..."
  git -C "$BUILD_ROOT" fetch origin rc 2>/dev/null || true
  if git -C "$BUILD_ROOT" show-ref --verify --quiet refs/heads/rc; then
    git -C "$BUILD_ROOT" worktree add "$RC_WORKTREE" rc
  elif git -C "$BUILD_ROOT" show-ref --verify --quiet refs/remotes/origin/rc; then
    git -C "$BUILD_ROOT" worktree add --track -b rc "$RC_WORKTREE" origin/rc
  else
    print_info "No rc branch yet — the first RC cut creates it, and the rc"
    echo "    release unit creates this worktree on demand. Nothing to do."
  fi
fi
echo ""

# --- release timers ----------------------------------------------------------

TIMERS=(omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-rc omarchy-auto-release-stable)

if [[ "$SKIP_TIMERS" == true ]]; then
  print_info "Skipping release timers (--skip-timers)"
elif [[ "$CHECK_ONLY" == true ]]; then
  for timer in "${TIMERS[@]}"; do
    if systemctl is-enabled "$timer.timer" >/dev/null 2>&1; then
      print_success "$timer.timer is enabled"
    else
      print_warning "$timer.timer would be enabled"
    fi
  done
else
  print_info "Installing release timers..."
  cp "$BUILD_ROOT"/systemd/*.service "$BUILD_ROOT"/systemd/*.timer /etc/systemd/system/
  systemctl daemon-reload
  for timer in "${TIMERS[@]}"; do
    systemctl enable --now "$timer.timer"
    print_step "$timer.timer"
  done
  print_success "Release timers enabled"
fi
echo ""

# --- credentials -------------------------------------------------------------

# These hold secrets, so setup reports on them rather than creating them.
print_info "Checking credentials..."

if [[ -f "$CREDENTIALS" ]]; then
  print_success "Signing credentials present: $CREDENTIALS"
else
  print_warning "Missing $CREDENTIALS"
  echo "    Must export GPG_PRIVATE_KEY and GPG_PASSPHRASE; the release"
  echo "    services source it before signing."
fi

# Release reporting is optional, but silence is indistinguishable from a
# broken hook, so say which chat (if any) will receive build reports.
if [[ -f "$CREDENTIALS" ]]; then
  if grep -q 'OMARCHY_RELEASE_CHATBOT_URL' "$CREDENTIALS"; then
    print_success "Release reports go to the dedicated release chat"
  elif grep -q 'BASECAMP_CHATBOT_URL' "$CREDENTIALS"; then
    # A working setup, not a problem: one chat receives everything. Splitting
    # them is a preference, so this states the fact rather than nagging.
    print_success "Release reports go to BASECAMP_CHATBOT_URL (shared with the sync workflows)"
    echo "    To give releases their own Basecamp chat, create one, add a chatbot"
    echo "    to it, and export its lines URL as OMARCHY_RELEASE_CHATBOT_URL here."
  else
    print_warning "No chatbot URL in $CREDENTIALS — build reports are disabled"
    echo "    Export OMARCHY_RELEASE_CHATBOT_URL to receive start/publish/failure"
    echo "    reports in Basecamp."
  fi
fi

if rclone listremotes 2>/dev/null | grep -q '^pkgs.omarchy.org:'; then
  print_success "rclone remote 'pkgs.omarchy.org' configured"
else
  print_warning "rclone remote 'pkgs.omarchy.org' not configured"
  echo "    bin/repo sync publishes there; configure it with 'rclone config'."
fi
echo ""

if [[ "$CHECK_ONLY" == true ]]; then
  print_info "Check complete — nothing was changed"
else
  print_success "Repository host ready"
fi
