#!/bin/bash
set -euo pipefail

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"

TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT

SPECIFIC_PACKAGES=()

usage() {
  cat <<EOF
Usage: $0 [PACKAGE...]

Sync AUR-backed packages into pkgbuilds/<package>/.

Package selection is driven by pkgbuilds/<package>/.omarchy/package.json:
  { "source": "aur" }                  # synced from matching AUR package name
  { "source": "aur", "aur": "yaru" }  # synced from different AUR package name
  { "source": "aur", "sync": false }  # AUR-origin, but not auto-synced

Arguments:
  PACKAGE    One or more package names to sync (optional)

Examples:
  $0                    # Sync all AUR packages with sync enabled
  $0 yay cursor-bin     # Sync specific packages
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help)
      usage
      exit 0
      ;;
    --tier)
      print_error "--tier is no longer supported; package metadata controls sync behavior"
      exit 1
      ;;
    --*)
      print_error "Unknown option: $1"
      exit 1
      ;;
    *)
      SPECIFIC_PACKAGES+=("$1")
      shift
      ;;
  esac
done

print_header "AUR Package Sync"
mkdir -p "$PKGBUILDS_DIR"

SYNCED=0
SKIPPED=0
FAILED=0
SYNCED_PACKAGES=()
SPECIFIC_MODE=false

get_pkgbuild_field() {
  local package_dir="$1"
  local field="$2"
  local value=""

  if [[ -f "$package_dir/PKGBUILD" ]]; then
    value=$(grep -m1 "^${field}=" "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'") || true
    if [[ -n "$value" ]]; then
      echo "$value"
      return
    fi
  fi

  if [[ -f "$package_dir/.SRCINFO" ]]; then
    awk -F' = ' -v field="$field" '$1 ~ "^[[:space:]]*" field "$" { print $2; exit }' "$package_dir/.SRCINFO"
  fi
}

set_pkgrel() {
  local package_dir="$1"
  local pkgrel="$2"
  local pkgbuild="$package_dir/PKGBUILD"

  if [[ -f "$pkgbuild" ]]; then
    sed -i "s/^pkgrel=.*/pkgrel=$pkgrel/" "$pkgbuild"
  fi
}

display_package_name() {
  local package_dir="$1"
  local name
  name=$(basename "$package_dir")
  echo "${name%.work}"
}

remove_aur_only_files() {
  local package_dir="$1"

  rm -f "$package_dir/.SRCINFO" "$package_dir/.gitignore"
}

copy_aur_contents() {
  local aur_dir="$1"
  local target_dir="$2"

  mkdir -p "$target_dir"

  shopt -s dotglob nullglob
  local item base
  for item in "$aur_dir"/*; do
    base=$(basename "$item")
    [[ "$base" == ".git" ]] && continue
    cp -a "$item" "$target_dir/"
  done
  shopt -u dotglob nullglob
}

commit_synced_worktree() {
  local work_dir="$1"
  local target_dir="$2"
  local parent base staged_dir backup_root backup_dir

  parent=$(dirname "$target_dir")
  base=$(basename "$target_dir")
  staged_dir=$(mktemp -d "$parent/.${base}.staged.XXXXXX")
  backup_root=$(mktemp -d "$parent/.${base}.backup.XXXXXX")
  backup_dir="$backup_root/$base"

  # Copy to the package filesystem before swapping. This keeps the original
  # package directory intact if copying from /tmp fails or is interrupted.
  if ! cp -a "$work_dir/." "$staged_dir/"; then
    print_error "Failed to stage synced package for $base"
    rm -rf "$staged_dir" "$backup_root"
    return 1
  fi

  if [[ -e "$target_dir" ]]; then
    if ! mv "$target_dir" "$backup_dir"; then
      print_error "Failed to back up existing package directory: $target_dir"
      rm -rf "$staged_dir" "$backup_root"
      return 1
    fi
  fi

  if ! mv "$staged_dir" "$target_dir"; then
    print_error "Failed to install synced package directory: $target_dir"
    if [[ -e "$backup_dir" ]]; then
      mv "$backup_dir" "$target_dir" || true
    fi
    rm -rf "$staged_dir" "$backup_root"
    return 1
  fi

  rm -rf "$backup_root" "$work_dir"
}

set_upstream_commit() {
  local package_dir="$1"
  local commit="$2"
  local metadata="$package_dir/.omarchy/package.json"
  local tmpfile

  tmpfile=$(mktemp)
  jq --arg commit "$commit" '.upstream_commit = $commit' "$metadata" > "$tmpfile"
  mv "$tmpfile" "$metadata"
}

apply_omarchy_patches() {
  local package_dir="$1"
  local patches_dir="$package_dir/.omarchy/patches"
  local applied=false

  [[ -d "$patches_dir" ]] || return 1

  shopt -s nullglob
  local patch_files=("$patches_dir"/*.patch)
  local patch_dir_files=("$patches_dir"/*)
  shopt -u nullglob

  if [[ ${#patch_files[@]} -eq 0 ]]; then
    if [[ ${#patch_dir_files[@]} -gt 0 ]]; then
      print_warning "No .patch files found in $patches_dir"
    fi
    return 1
  fi

  print_info "Applying Omarchy patches for $(display_package_name "$package_dir")..."
  local patch_file
  for patch_file in "${patch_files[@]}"; do
    print_info "  $(basename "$patch_file")"
    if ! (cd "$package_dir" && patch -p1 --forward --batch --no-backup-if-mismatch < "$patch_file"); then
      print_error "Failed to apply patch: $patch_file"
      return 2
    fi
    applied=true
  done

  [[ "$applied" == true ]]
}

run_omarchy_post_sync_hook() {
  local package_dir="$1"
  local package="$2"
  local aur_package="$3"
  local aur_pkgrel="$4"
  local hook="$package_dir/.omarchy/post-sync.sh"

  [[ -f "$hook" ]] || return 1

  print_info "Running Omarchy post-sync hook for $(display_package_name "$package_dir")..."
  if ! (
    cd "$package_dir"
    PACKAGE_NAME="$package" \
      AUR_PACKAGE_NAME="$aur_package" \
      AUR_PKGREL="$aur_pkgrel" \
      bash ".omarchy/post-sync.sh"
  ); then
    print_error "Failed to run post-sync hook: $hook"
    return 2
  fi

  return 0
}

apply_pkgrel_suffix_if_customized() {
  local package_dir="$1"
  local aur_pkgrel="$2"

  [[ -n "$aur_pkgrel" ]] || return 0

  print_info "Applying Omarchy pkgrel suffix for $(display_package_name "$package_dir"): pkgrel=$aur_pkgrel.1"
  set_pkgrel "$package_dir" "$aur_pkgrel.1"
}

apply_pkgrel_override() {
  local package_dir="$1"
  local aur_pkgrel="$2"
  local previous_pkgver="$3"
  local metadata="$package_dir/.omarchy/package.json"
  local pkgbuild="$package_dir/PKGBUILD"

  [[ -f "$metadata" ]] || return 1
  jq -e 'has("pkgrel")' "$metadata" >/dev/null || return 1

  local current_pkgver suffix offset base rel tmpfile
  # Read through the same accessor that produced previous_pkgver. Parsing it a
  # second time here let a quoted pkgver= compare unequal to itself, which threw
  # away the pkgrel metadata of an unchanged package on every sync.
  current_pkgver=$(get_pkgbuild_field "$package_dir" pkgver)

  if [[ -n "$previous_pkgver" && "$current_pkgver" != "$previous_pkgver" ]]; then
    print_info "Removing stale pkgrel metadata for $(display_package_name "$package_dir") (pkgver changed: $previous_pkgver -> $current_pkgver)"
    tmpfile=$(mktemp)
    jq 'del(.pkgrel)' "$metadata" > "$tmpfile"
    mv "$tmpfile" "$metadata"
    return 1
  fi

  suffix=$(jq -r '.pkgrel.suffix // 1' "$metadata")
  offset=$(jq -r '.pkgrel.offset // 0' "$metadata")

  if [[ ! "$offset" =~ ^[0-9]+$ || ! "$aur_pkgrel" =~ ^[0-9]+$ ]]; then
    print_error "pkgrel offset requires numeric AUR pkgrel for $(display_package_name "$package_dir")"
    return 2
  fi

  base=$((aur_pkgrel + offset))
  rel="$base.$suffix"

  print_info "Applying pkgrel suffix for $(display_package_name "$package_dir"): AUR pkgrel=$aur_pkgrel, offset=$offset, suffix=$suffix -> pkgrel=$rel"
  set_pkgrel "$package_dir" "$rel"
  return 0
}

clone_aur_package() {
  local aur_package="$1"
  local dest="$2"
  local clone_log="$TEMP_DIR/clone.log"
  local attempt

  for attempt in 1 2 3; do
    rm -rf "$dest"
    if git clone "https://aur.archlinux.org/${aur_package}.git" "$dest" >"$clone_log" 2>&1; then
      return 0
    fi
    if [[ $attempt -lt 3 ]]; then
      print_warning "Clone of $aur_package failed (attempt $attempt/3), retrying in 10s..."
      sleep 10
    fi
  done

  print_warning "Failed to clone $aur_package after 3 attempts: $(tail -n 1 "$clone_log")"
  return 1
}

sync_package() {
  local package="$1"
  local package_dir="$PKGBUILDS_DIR/$package"
  local metadata="$package_dir/.omarchy/package.json"

  if [[ ! -f "$metadata" ]]; then
    if [[ "$SPECIFIC_MODE" == true ]]; then
      print_error "Package $package is missing .omarchy/package.json"
      ((++FAILED))
    else
      print_warning "Skipping $package: missing .omarchy/package.json"
      ((++SKIPPED))
    fi
    return 0
  fi

  if [[ "$(jq -r '.source // ""' "$metadata")" != "aur" ]]; then
    print_info "Skipping $package: source is not AUR"
    ((++SKIPPED))
    return 0
  fi

  if [[ "$(jq -r 'if has("sync") then .sync else true end' "$metadata")" == "false" ]]; then
    print_info "Skipping $package: AUR sync disabled"
    ((++SKIPPED))
    return 0
  fi

  local aur_package
  aur_package=$(jq -r --arg package "$package" '.aur // $package' "$metadata")

  if [[ "$aur_package" == "$package" ]]; then
    print_info "Syncing $package from AUR..."
  else
    print_info "Syncing $package from AUR package $aur_package..."
  fi

  cd "$TEMP_DIR"

  if ! clone_aur_package "$aur_package" "$TEMP_DIR/$aur_package"; then
    ((++FAILED))
    return 0
  fi

  if [[ ! -f "$TEMP_DIR/$aur_package/PKGBUILD" ]]; then
    print_warning "AUR repository for $aur_package is empty (package does not exist in AUR)"
    ((++FAILED))
    return 0
  fi

  local aur_dir="$TEMP_DIR/$aur_package"
  local work_dir="$TEMP_DIR/${package}.work"
  local aur_pkgrel previous_pkgver upstream_commit
  aur_pkgrel=$(get_pkgbuild_field "$aur_dir" pkgrel)
  previous_pkgver=$(get_pkgbuild_field "$package_dir" pkgver || true)
  upstream_commit=$(git -C "$aur_dir" rev-parse HEAD)

  rm -rf "$work_dir"
  copy_aur_contents "$aur_dir" "$work_dir"
  rm -rf "$work_dir/.omarchy"
  cp -a "$package_dir/.omarchy" "$work_dir/.omarchy"

  local customized=false

  if apply_omarchy_patches "$work_dir"; then
    customized=true
  else
    local patch_status=$?
    if [[ $patch_status -eq 2 ]]; then
      ((++FAILED))
      return 0
    fi
  fi

  if run_omarchy_post_sync_hook "$work_dir" "$package" "$aur_package" "$aur_pkgrel"; then
    customized=true
  else
    local hook_status=$?
    if [[ $hook_status -eq 2 ]]; then
      ((++FAILED))
      return 0
    fi
  fi

  local pkgrel_overridden=false
  set +e
  apply_pkgrel_override "$work_dir" "$aur_pkgrel" "$previous_pkgver"
  local pkgrel_status=$?
  set -e
  case "$pkgrel_status" in
    0) pkgrel_overridden=true ;;
    1) ;;
    *) ((++FAILED)); return 0 ;;
  esac

  if [[ "$customized" == true && "$pkgrel_overridden" == false ]]; then
    apply_pkgrel_suffix_if_customized "$work_dir" "$aur_pkgrel"
  fi

  remove_aur_only_files "$work_dir"

  set_upstream_commit "$work_dir" "$upstream_commit"
  if ! commit_synced_worktree "$work_dir" "$package_dir"; then
    ((++FAILED))
    return 0
  fi

  SYNCED_PACKAGES+=("$package")
  ((++SYNCED))
}

if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
  SPECIFIC_MODE=true
  for package in "${SPECIFIC_PACKAGES[@]}"; do
    sync_package "$package"
  done
else
  while IFS= read -r package; do
    sync_package "$package"
  done < <(packages_for_aur_sync)
fi

echo ""
if [[ $FAILED -gt 0 ]]; then
  print_error "Sync completed with failures"
else
  print_success "Sync complete!"
fi
echo "  Target: $PKGBUILDS_DIR"
echo "  Synced: $SYNCED"
echo "  Skipped: $SKIPPED"
echo "  Failed: $FAILED"

if [[ $FAILED -gt 0 ]]; then
  exit 1
fi
