#!/usr/bin/env bash
#
# Build script for WolfPanel Agent release packaging.
# Produces a DETERMINISTIC tarball, a bare-filename SHA-256 sidecar, and the
# latest.json manifest under dist/. One version maps to exactly one artifact
# hash; an already-built version is not rebuilt unless --force is given.
#
set -euo pipefail

# Locate directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

cd "${PROJECT_ROOT}"

# --- Arguments --------------------------------------------------------------
FORCE="${FORCE:-0}"
CHANNEL="stable"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --force|-f) FORCE=1; shift ;;
    --channel|-c) CHANNEL="$2"; shift 2 ;;
    --channel=*) CHANNEL="${1#*=}"; shift ;;
    -c=*) CHANNEL="${1#*=}"; shift ;;
    *) echo "Unknown argument: $1" >&2; exit 1 ;;
  esac
done

if [[ "$CHANNEL" != "stable" && "$CHANNEL" != "beta" && "$CHANNEL" != "dev" ]]; then
  echo "Error: Invalid channel '$CHANNEL'. Must be stable, beta, or dev." >&2
  exit 1
fi


# Check for VERSION file
if [ ! -f VERSION ]; then
  echo "Error: VERSION file not found in project root!" >&2
  exit 1
fi

VERSION=$(tr -d '\r\n[:space:]' < VERSION)
if [ -z "${VERSION}" ]; then
  echo "Error: VERSION file is empty!" >&2
  exit 1
fi

echo "Building WolfPanel Agent release v${VERSION}..."

# Ensure dist directory exists
mkdir -p dist

ARCHIVE_NAME="wolfpanel-agent-${VERSION}.tar.gz"
ARCHIVE_PATH="dist/${ARCHIVE_NAME}"

# Do not rebuild an already-published version unless explicitly forced. This
# guarantees one version => one artifact hash.
if [ -f "${ARCHIVE_PATH}" ] && [ "${FORCE}" != "1" ]; then
  echo "Error: ${ARCHIVE_PATH} already exists. Refusing to rebuild." >&2
  echo "Bump VERSION, or pass --force to overwrite." >&2
  exit 1
fi

# Prepare clean temporary packaging directory
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "${TEMP_DIR}"' EXIT
echo "Staging files in temporary directory: ${TEMP_DIR}"

# Files/directories to package (must exist in the repository).
ITEMS=(
  "src"
  "config"
  "systemd"
  "deploy"
  "README.md"
  "VERSION"
  "install.sh"
  "uninstall.sh"
)

# Copy files/dirs to staging area
for ITEM in "${ITEMS[@]}"; do
  if [ -e "${ITEM}" ]; then
    cp -R "${ITEM}" "${TEMP_DIR}/"
  fi
done

# Clean staging area of development remnants.
find "${TEMP_DIR}" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find "${TEMP_DIR}" -type f -name "*.pyc" -delete 2>/dev/null || true
find "${TEMP_DIR}" -type f -name ".DS_Store" -delete 2>/dev/null || true

echo "Creating tarball: ${ARCHIVE_PATH}"
# Deterministic packaging: stable file order, fixed mtime, owner/group 0,
# numeric owner, and gzip without an embedded timestamp (gzip -n). This makes
# the artifact reproducible so one version always hashes to one value.
if tar --version 2>/dev/null | grep -qi 'gnu'; then
  tar --sort=name \
      --mtime="@0" \
      --owner=0 --group=0 --numeric-owner \
      --format=gnu \
      -cf - -C "${TEMP_DIR}" . | gzip -n > "${ARCHIVE_PATH}"
else
  echo "WARNING: GNU tar not found; archive may not be fully deterministic." >&2
  ( cd "${TEMP_DIR}" && tar -cf - . ) | gzip -n > "${ARCHIVE_PATH}"
fi

# Generate SHA-256 checksum with a BARE filename so `sha256sum -c` works from
# inside the published artifact directory.
echo "Generating SHA-256 checksum..."
(
  cd dist
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "${ARCHIVE_NAME}" > "${ARCHIVE_NAME}.sha256"
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "${ARCHIVE_NAME}" > "${ARCHIVE_NAME}.sha256"
  elif command -v openssl >/dev/null 2>&1; then
    HASH=$(openssl dgst -sha256 "${ARCHIVE_NAME}" | awk '{print $NF}')
    echo "${HASH}  ${ARCHIVE_NAME}" > "${ARCHIVE_NAME}.sha256"
  else
    echo "Error: No sha256 checksum utility found!" >&2
    exit 1
  fi
)

SHA256_HASH=$(awk '{print $1}' "${ARCHIVE_PATH}.sha256")
echo "SHA-256 Checksum: ${SHA256_HASH}"

# Sign release archive. Exactly two ways to supply the production key are
# supported (see docs/RELEASE_GUIDE.md); there is NO mock/fallback signing
# path -- a release build with no real key configured MUST fail loudly.
if [ -z "${RELEASE_PRIVATE_KEY:-}" ] && [ -z "${RELEASE_PRIVATE_KEY_FILE:-}" ]; then
  echo "Error: Release aborted: no production signing key configured." >&2
  echo "Set RELEASE_PRIVATE_KEY_FILE=/secure/path/release_private_key.pem" >&2
  echo "or RELEASE_PRIVATE_KEY=\"<PEM_CONTENT>\"." >&2
  exit 1
fi

# Determine python executable
PYTHON_CMD=""
if command -v python3 >/dev/null 2>&1 && python3 --version >/dev/null 2>&1; then
  PYTHON_CMD="python3"
elif command -v python >/dev/null 2>&1 && python --version >/dev/null 2>&1; then
  PYTHON_CMD="python"
else
  echo "Error: Python is not installed or not working!" >&2
  exit 1
fi

echo "Signing release archive..."
SIGNATURE_HEX=$("${PYTHON_CMD}" scripts/sign_release.py "${ARCHIVE_PATH}")

if [ -z "${SIGNATURE_HEX}" ]; then
  echo "Error: Failed to generate release signature!" >&2
  exit 1
fi

echo "Signature generated successfully."

# Actual tarball byte size, used by installers/agents to catch truncated
# downloads before trusting sha256/signature.
if command -v stat >/dev/null 2>&1 && stat -c%s "${ARCHIVE_PATH}" >/dev/null 2>&1; then
  SIZE_BYTES=$(stat -c%s "${ARCHIVE_PATH}")
elif command -v stat >/dev/null 2>&1 && stat -f%z "${ARCHIVE_PATH}" >/dev/null 2>&1; then
  SIZE_BYTES=$(stat -f%z "${ARCHIVE_PATH}")
else
  SIZE_BYTES=$(wc -c < "${ARCHIVE_PATH}" | tr -d '[:space:]')
fi

PUBLISHED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Generate latest.json manifest. No "url" field: installers/agents build the
# download URL themselves as ${RELEASE_BASE}/${CHANNEL}/${file}.
echo "Generating dist/latest.json..."
cat > dist/latest.json <<EOF
{
  "manifest_version": 1,
  "channel": "${CHANNEL}",
  "version": "${VERSION}",
  "file": "${ARCHIVE_NAME}",
  "sha256": "${SHA256_HASH}",
  "signature": "${SIGNATURE_HEX}",
  "size_bytes": ${SIZE_BYTES},
  "published_at": "${PUBLISHED_AT}"
}
EOF

echo "Build release completed successfully."
ls -la dist/
