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

repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "${repo_root}"

platform="${1:-all}"
build_number="${BUILD_NUMBER:-${RELEASE_BUILD_NUMBER:-${RELEASE_REF:-manual}}}"
out_root="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${build_number}}"

mkdir -p "${out_root}"

sha256_file() {
  local file="$1"
  if command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$file" > "${file}.sha256"
  else
    sha256sum "$file" > "${file}.sha256"
  fi
}

burrow_crate_version() {
  awk -F '"' '/^version = / { print $2; exit }' burrow/Cargo.toml
}

package_release_suffix() {
  printf '%s' "$build_number" | tr -c 'A-Za-z0-9.+~' '.'
}

arch_package_suffix() {
  printf '%s' "$build_number" | tr -c 'A-Za-z0-9.+_' '.'
}

deb_arch_for_target() {
  case "$1" in
    x86_64-unknown-linux-gnu) printf 'amd64' ;;
    aarch64-unknown-linux-gnu) printf 'arm64' ;;
    riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
    *) return 1 ;;
  esac
}

rpm_arch_for_target() {
  case "$1" in
    x86_64-unknown-linux-gnu) printf 'x86_64' ;;
    aarch64-unknown-linux-gnu) printf 'aarch64' ;;
    riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
    *) return 1 ;;
  esac
}

pacman_arch_for_target() {
  case "$1" in
    x86_64-unknown-linux-gnu) printf 'x86_64' ;;
    aarch64-unknown-linux-gnu) printf 'aarch64' ;;
    riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
    *) return 1 ;;
  esac
}

copy_license_readme() {
  local dst="$1"
  cp README.md "$dst/README.md"
  if [[ -f LICENSE ]]; then
    cp LICENSE "$dst/LICENSE"
  elif [[ -f LICENSE.md ]]; then
    cp LICENSE.md "$dst/LICENSE.md"
  fi
}

package_deb() {
  local target binary deb_arch pkg_version suffix staging deb_file installed_size
  target="$1"
  binary="$2"
  if ! deb_arch="$(deb_arch_for_target "$target")"; then
    echo "warning: no Debian architecture mapping for ${target}; skipping .deb package" >&2
    return 0
  fi
  if ! command -v dpkg-deb >/dev/null 2>&1; then
    echo "warning: dpkg-deb is not available; skipping .deb package" >&2
    return 0
  fi

  suffix="$(package_release_suffix)"
  pkg_version="${BURROW_PACKAGE_VERSION:-$(burrow_crate_version)+${suffix}}"
  staging="${out_root}/linux/pkg-deb/burrow_${pkg_version}_${deb_arch}"
  rm -rf "$staging"
  mkdir -p \
    "$staging/DEBIAN" \
    "$staging/usr/bin" \
    "$staging/usr/lib/systemd/system" \
    "$staging/usr/share/doc/burrow" \
    "$staging/usr/share/licenses/burrow"

  install -m 0755 "$binary" "$staging/usr/bin/burrow"
  install -m 0644 systemd/burrow.service "$staging/usr/lib/systemd/system/burrow.service"
  install -m 0644 systemd/burrow.socket "$staging/usr/lib/systemd/system/burrow.socket"
  install -m 0644 README.md "$staging/usr/share/doc/burrow/README.md"
  install -m 0644 LICENSE.md "$staging/usr/share/licenses/burrow/LICENSE.md"
  installed_size="$(du -sk "$staging/usr" | awk '{ print $1 }')"

  cat > "$staging/DEBIAN/control" <<EOF
Package: burrow
Version: ${pkg_version}
Section: net
Priority: optional
Architecture: ${deb_arch}
Maintainer: Burrow Release Automation <packages@burrow.net>
Installed-Size: ${installed_size}
Depends: systemd
Description: Burrow VPN daemon and CLI
 Burrow installs the daemon entrypoint and systemd socket used by the desktop UI.
EOF

  deb_file="${out_root}/linux/burrow_${pkg_version}_${deb_arch}.deb"
  dpkg-deb --build --root-owner-group "$staging" "$deb_file"
  sha256_file "$deb_file"
}

package_rpm() {
  local target binary rpm_arch
  target="$1"
  binary="$2"
  if ! rpm_arch="$(rpm_arch_for_target "$target")"; then
    echo "warning: no RPM architecture mapping for ${target}; skipping .rpm package" >&2
    return 0
  fi
  if ! command -v cargo-generate-rpm >/dev/null 2>&1; then
    echo "warning: cargo-generate-rpm is not available; skipping .rpm package" >&2
    return 0
  fi

  mkdir -p target/release
  cp "$binary" target/release/burrow
  if cargo generate-rpm -p burrow; then
    find target/generate-rpm -type f -name '*.rpm' -exec cp {} "${out_root}/linux/" \; 2>/dev/null || true
    find "${out_root}/linux" -maxdepth 1 -type f -name '*.rpm' -print | while read -r rpm; do
      sha256_file "$rpm"
    done
  else
    echo "warning: cargo-generate-rpm failed for ${rpm_arch}; skipping .rpm package" >&2
  fi
}

package_pacman() {
  local target binary pacman_arch pkg_version pkgrel work source_sum service_sum socket_sum readme_sum license_sum
  target="$1"
  binary="$2"
  if ! pacman_arch="$(pacman_arch_for_target "$target")"; then
    echo "warning: no pacman architecture mapping for ${target}; skipping Arch package" >&2
    return 0
  fi
  if ! command -v makepkg >/dev/null 2>&1; then
    echo "warning: makepkg is not available; skipping Arch package" >&2
    return 0
  fi

  pkg_version="${BURROW_ARCH_PKGVER:-$(burrow_crate_version)_$(arch_package_suffix)}"
  pkgrel="${BURROW_ARCH_PKGREL:-1}"
  work="${out_root}/linux/pkg-arch/${pacman_arch}"
  rm -rf "$work"
  mkdir -p "$work"
  install -m 0755 "$binary" "$work/burrow"
  install -m 0644 systemd/burrow.service "$work/burrow.service"
  install -m 0644 systemd/burrow.socket "$work/burrow.socket"
  install -m 0644 README.md "$work/README.md"
  install -m 0644 LICENSE.md "$work/LICENSE.md"

  source_sum="$(sha256sum "$work/burrow" | awk '{ print $1 }')"
  service_sum="$(sha256sum "$work/burrow.service" | awk '{ print $1 }')"
  socket_sum="$(sha256sum "$work/burrow.socket" | awk '{ print $1 }')"
  readme_sum="$(sha256sum "$work/README.md" | awk '{ print $1 }')"
  license_sum="$(sha256sum "$work/LICENSE.md" | awk '{ print $1 }')"

  cat > "$work/PKGBUILD" <<EOF
# Maintainer: Burrow Release Automation <packages@burrow.net>
pkgname=burrow
pkgver=${pkg_version}
pkgrel=${pkgrel}
pkgdesc="Burrow VPN daemon and CLI"
arch=("${pacman_arch}")
url="https://git.burrow.net/hackclub/burrow"
license=("GPL-3.0-or-later")
depends=("systemd")
source=("burrow" "burrow.service" "burrow.socket" "README.md" "LICENSE.md")
sha256sums=("${source_sum}" "${service_sum}" "${socket_sum}" "${readme_sum}" "${license_sum}")

package() {
  install -Dm755 "\${srcdir}/burrow" "\${pkgdir}/usr/bin/burrow"
  install -Dm644 "\${srcdir}/burrow.service" "\${pkgdir}/usr/lib/systemd/system/burrow.service"
  install -Dm644 "\${srcdir}/burrow.socket" "\${pkgdir}/usr/lib/systemd/system/burrow.socket"
  install -Dm644 "\${srcdir}/README.md" "\${pkgdir}/usr/share/doc/burrow/README.md"
  install -Dm644 "\${srcdir}/LICENSE.md" "\${pkgdir}/usr/share/licenses/burrow/LICENSE.md"
}
EOF

  (
    cd "$work"
    makepkg --force --nodeps --noconfirm
  )
  find "$work" -maxdepth 1 -type f -name '*.pkg.tar.*' -exec cp {} "${out_root}/linux/" \;
  find "${out_root}/linux" -maxdepth 1 -type f -name '*.pkg.tar.*' -print | while read -r package; do
    sha256_file "$package"
  done
}

package_linux() {
  local target host archive staging
  host="$(rustc -vV | awk '/^host:/ { print $2 }')"
  target="${BURROW_LINUX_TARGET:-$host}"
  if [[ "$target" != *-linux-* && -z "${BURROW_LINUX_TARGET:-}" ]]; then
    mkdir -p "${out_root}/linux"
    echo "warning: host target ${target} is not Linux; skipping Linux artifact on this host" >&2
    printf 'Linux build was skipped on host target %s. Run this lane on a Linux Namespace runner or set BURROW_LINUX_TARGET explicitly.\n' "$target" > "${out_root}/linux/README.txt"
    return 0
  fi
  staging="${out_root}/linux/burrow-${build_number}-${target}"
  mkdir -p "$staging"

  cargo build --locked --release -p burrow --bin burrow --target "$target"
  install -m 0755 "target/${target}/release/burrow" "$staging/burrow"
  copy_license_readme "$staging"

  archive="${out_root}/linux/burrow-${build_number}-${target}.tar.gz"
  tar -C "${out_root}/linux" -czf "$archive" "$(basename "$staging")"
  sha256_file "$archive"

  package_deb "$target" "target/${target}/release/burrow"
  package_rpm "$target" "target/${target}/release/burrow"
  package_pacman "$target" "target/${target}/release/burrow"
}

package_windows_stub_unix() {
  local target exe archive staging
  target="${BURROW_WINDOWS_TARGET:-x86_64-pc-windows-gnu}"
  staging="${out_root}/windows/burrow-${build_number}-${target}"
  mkdir -p "$staging"

  target_libdir="$(rustc --print target-libdir --target "$target" 2>/dev/null || true)"
  if [[ -z "$target_libdir" || ! -d "$target_libdir" ]]; then
    echo "warning: Rust target ${target} is not installed on this host" >&2
    printf 'Windows stub build for %s was skipped on this host. Use the Namespace Windows lane for the native artifact.\n' "$target" > "${out_root}/windows/README.txt"
    return 0
  fi

  if cargo build --locked --release -p burrow-windows-stub --target "$target"; then
    exe="target/${target}/release/burrow.exe"
  else
    echo "warning: unable to build the Windows release stub on this host" >&2
    printf 'Windows stub build for %s was skipped on this host. Use the Namespace Windows lane for the native artifact.\n' "$target" > "${out_root}/windows/README.txt"
    return 0
  fi

  install -m 0755 "$exe" "$staging/burrow.exe"
  copy_license_readme "$staging"
  printf 'Burrow Windows stub build %s\n' "$build_number" > "$staging/README-Windows.txt"

  archive="${out_root}/windows/burrow-${build_number}-${target}.zip"
  (cd "${out_root}/windows" && zip -qr "$archive" "$(basename "$staging")")
  sha256_file "$archive"
}

package_sparkle_unsigned() {
  local macos_dir appcast channel dmg
  channel="${SPARKLE_CHANNEL:-build-${build_number}}"
  macos_dir="${out_root}/macos"
  appcast="${out_root}/sparkle/${channel}/appcast.xml"
  mkdir -p "$(dirname "$appcast")"

  dmg="$(find "$macos_dir" -maxdepth 1 -type f -name '*.dmg' -print 2>/dev/null | sort | tail -n 1 || true)"
  if [[ -z "$dmg" ]]; then
    echo "No macOS DMG exists yet; Sparkle appcast generation is waiting on the macOS signed artifact." > "${out_root}/sparkle/${channel}/README.txt"
    return 0
  fi

  local name length pubdate url
  name="$(basename "$dmg")"
  length="$(wc -c < "$dmg" | tr -d ' ')"
  pubdate="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
  url="${SPARKLE_DOWNLOAD_PREFIX:-https://releases.burrow.net/sparkle}/${channel}/${name}"
  cat > "$appcast" <<EOF
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
  <channel>
    <title>Burrow ${channel}</title>
    <item>
      <title>Burrow ${build_number}</title>
      <pubDate>${pubdate}</pubDate>
      <sparkle:version>${build_number}</sparkle:version>
      <sparkle:shortVersionString>0.1</sparkle:shortVersionString>
      <enclosure url="${url}" sparkle:version="${build_number}" length="${length}" type="application/octet-stream" />
    </item>
  </channel>
</rss>
EOF
  cp "$dmg" "${out_root}/sparkle/${channel}/"
}

case "$platform" in
  all)
    package_linux
    package_windows_stub_unix
    if [[ "$(uname -s)" == "Darwin" ]] && command -v xcodebuild >/dev/null 2>&1; then
      Scripts/ci/package-apple-artifacts.sh all
    else
      mkdir -p "${out_root}/apple"
      printf 'Apple artifacts require a macOS runner with Xcode. The release workflow builds them in the dedicated Apple lane.\n' > "${out_root}/apple/README.txt"
    fi
    package_sparkle_unsigned
    ;;
  linux)
    package_linux
    ;;
  windows-stub|windows)
    package_windows_stub_unix
    ;;
  sparkle)
    package_sparkle_unsigned
    ;;
  apple)
    Scripts/ci/package-apple-artifacts.sh all
    ;;
  ios|macos)
    Scripts/ci/package-apple-artifacts.sh "$platform"
    ;;
  *)
    echo "unknown release platform: $platform" >&2
    exit 64
    ;;
esac

find "$out_root" -type f -print | sort
