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

readonly PROJECT_DIRECTORY=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
readonly VERSION=${VERSION:-0.8.0-rc1}
readonly GO_BINARY=${GO_BINARY:-go}
readonly PACKAGE_NAME=${PACKAGE_NAME:-cfpp-unified-v$VERSION}
readonly OUTPUT=${OUTPUT:-$PROJECT_DIRECTORY/dist/$PACKAGE_NAME}
readonly ARCHIVE_OUTPUT=${ARCHIVE_OUTPUT:-$PROJECT_DIRECTORY/dist/$PACKAGE_NAME.tar.gz}
readonly ONLINE_BASE_URL=${ONLINE_BASE_URL:-https://example.invalid/cfpp}
readonly UPLOAD_OUTPUT=${UPLOAD_OUTPUT:-$PROJECT_DIRECTORY/dist/cfpp}
readonly UPLOAD_BUNDLE_OUTPUT=${UPLOAD_BUNDLE_OUTPUT:-$PROJECT_DIRECTORY/dist/cfpp-web-upload-v$VERSION.zip}
readonly BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
readonly WORK_DIRECTORY=$(mktemp -d /tmp/cfpp-directory-package.XXXXXX)
readonly STAGING_DIRECTORY="$WORK_DIRECTORY/$PACKAGE_NAME"

cleanup() {
  rm -rf -- "$WORK_DIRECTORY"
}
trap cleanup EXIT

case "$OUTPUT" in
  "$PROJECT_DIRECTORY"/dist/*) ;;
  *)
    printf 'refusing output outside %s/dist: %s\n' "$PROJECT_DIRECTORY" "$OUTPUT" >&2
    exit 1
    ;;
esac
case "$ARCHIVE_OUTPUT" in
  "$PROJECT_DIRECTORY"/dist/*.tar.gz) ;;
  *)
    printf 'refusing archive output outside %s/dist: %s\n' "$PROJECT_DIRECTORY" "$ARCHIVE_OUTPUT" >&2
    exit 1
    ;;
esac
case "$UPLOAD_OUTPUT" in
  "$PROJECT_DIRECTORY"/dist/cfpp) ;;
  *)
    printf 'refusing upload output outside the fixed dist/cfpp directory: %s\n' "$UPLOAD_OUTPUT" >&2
    exit 1
    ;;
esac
case "$UPLOAD_BUNDLE_OUTPUT" in
  "$PROJECT_DIRECTORY"/dist/cfpp-web-upload-v*.zip) ;;
  *)
    printf 'refusing upload bundle output outside the fixed dist naming pattern: %s\n' "$UPLOAD_BUNDLE_OUTPUT" >&2
    exit 1
    ;;
esac

mkdir -p "$STAGING_DIRECTORY" "$(dirname -- "$OUTPUT")"
cd "$PROJECT_DIRECTORY"

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 "$GO_BINARY" build -trimpath \
  -ldflags "-s -w -X main.version=$VERSION -X main.commit=delivery -X main.date=$BUILD_DATE" \
  -o "$STAGING_DIRECTORY/cf-pp-gateway" ./cmd/cf-pp-gateway

install -m 0644 "$PROJECT_DIRECTORY/configs/config.auto-local.500k.json" "$STAGING_DIRECTORY/config.json"
# The source template keeps its historical filename; fresh deliveries start at 32768.
sed -i.bak -E 's/"max_connections": [0-9]+/"max_connections": 32768/' "$STAGING_DIRECTORY/config.json"
rm -- "$STAGING_DIRECTORY/config.json.bak"
install -m 0644 "$PROJECT_DIRECTORY/configs/cloudflare.cidrs" "$STAGING_DIRECTORY/cloudflare.cidrs"
install -m 0644 "$PROJECT_DIRECTORY/configs/network.auto-local.all-tcp.env" "$STAGING_DIRECTORY/network.env"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-diagnose" "$STAGING_DIRECTORY/network-diagnose"
install -m 0755 "$PROJECT_DIRECTORY/packaging/control/cfppctl" "$STAGING_DIRECTORY/cfppctl"
install -m 0755 "$PROJECT_DIRECTORY/packaging/topology/plan.py" "$STAGING_DIRECTORY/topology-plan.py"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-reconcile" "$STAGING_DIRECTORY/network-reconcile"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/cf-pp-gateway-reconcile.service" "$STAGING_DIRECTORY/cf-pp-gateway-reconcile.service"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/cf-pp-gateway-reconcile.timer" "$STAGING_DIRECTORY/cf-pp-gateway-reconcile.timer"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-common" "$STAGING_DIRECTORY/network-common"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-apply" "$STAGING_DIRECTORY/network-apply"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-check" "$STAGING_DIRECTORY/network-check"
install -m 0755 "$PROJECT_DIRECTORY/packaging/network/network-remove" "$STAGING_DIRECTORY/network-remove"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/cf-pp-gateway.service" "$STAGING_DIRECTORY/cf-pp-gateway.service"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/cf-pp-gateway-legacy.service" "$STAGING_DIRECTORY/cf-pp-gateway-legacy.service"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/cf-pp-gateway-network.service" "$STAGING_DIRECTORY/cf-pp-gateway-network.service"
install -m 0644 "$PROJECT_DIRECTORY/packaging/systemd/capacity-500k.conf" "$STAGING_DIRECTORY/capacity-500k.conf"
install -m 0755 "$PROJECT_DIRECTORY/packaging/delivery/uninstall.sh" "$STAGING_DIRECTORY/uninstall.sh"

printf '%s\n' \
  "cf-pp-gateway directory delivery $VERSION" \
  "built_utc=$BUILD_DATE" \
  'target=systemd-based Ubuntu, Debian, CentOS/RHEL family; linux/amd64; candidate-support'  \
  'origin_network=auto origin=default-route-source primary_interface=default-route-interface' \
  'destination_networks=auto destinations=all-global-ipv4-on-selected-ingress-interfaces' \
  'ingress_interfaces=topology-planner-selected override=CFPP_INTERFACES' \
  'capture_destinations=managed-ipset-from-selected-interface-addresses' \
  'trusted_proxy_sources=cloudflare.cidrs' \
  'tcp_ports=1-65535 max_connections_default=32768 max_connections_override=CFPP_MAX_CONNECTIONS range=1..500000' \
  'capacity_guard=configuration-ceiling-not-performance-guarantee' \
  'protocol=ipv4-tcp-proxy-protocol-v1' \
  'docker=local-daemon-bridge-routes-private-nonoverlapping' \
  'reconcile=docker-owned-routes-only-on-validated-config' \
  'existing_install=network-helper-upgrade-preserve-config-and-main-daemon' \
  'release_validation=linux-kernel-docker-upstream-tests-required-before-production-certification' \
  'legacy_capacity_file=capacity-500k.conf-not-fresh-install-effective-limit' \
  'missing_proxy_policy=passthrough-original' \
  'keep_all_files_in_one_directory=yes' \
  >"$STAGING_DIRECTORY/BUILD-INFO.txt"

sed \
  -e "s/@@INSTALLER_NAME@@/cf-pp-gateway-directory/g" \
  -e "s/@@INSTALLER_VERSION@@/$VERSION/g" \
  -e "s/@@INSTALLER_MODE@@/directory/g" \
  -e "s/@@PAYLOAD_SHA256@@/external-files/g" \
  -e '/^__CFPP_ARCHIVE_BELOW__$/d' \
  "$PROJECT_DIRECTORY/packaging/delivery/installer-header.sh" >"$STAGING_DIRECTORY/install.sh"
chmod 0755 "$STAGING_DIRECTORY/install.sh"

cat >"$STAGING_DIRECTORY/README.txt" <<'EOF'
CF Proxy Protocol Gateway - Unified Linux Candidate Delivery (0.8.0-rc1)

Keep all files together. On the target server, enter this directory and verify:
  sha256sum -c SHA256SUMS

Fresh installation:
  sudo bash install.sh --check-only
  sudo bash install.sh

An existing CFPP installation follows the helper upgrade path automatically.
Its configuration, running gateway binary, and customer containers are retained.
Explicit package commands are also available:
  sudo bash cfppctl check
  sudo bash cfppctl upgrade
Save the printed backup and rollback command. Rule checks do not certify login.

Candidate scope: systemd-based Ubuntu, Debian, CentOS/RHEL family; Linux amd64.
Protocol: IPv4, TCP, Proxy Protocol v1 from trusted Cloudflare peers.
Backends: local listeners and compatible Docker bridge/host publishing. Bridge
subnets must be private and non-overlapping; Docker discovery uses a local socket.
IPv6, UDP/QUIC, PPv2, Kubernetes/CNI, alternate Docker nftables publishing, and
Windows are outside this candidate's supported network paths.

The topology planner selects host ingress interfaces, retains the default-route
source address as the shared listener, and allows global IPv4 addresses on all
selected interfaces. Use CFPP_INTERFACES=eth0,eth1 to select interfaces explicitly.
Unsupported or ambiguous topologies fail before gateway deployment. Trusted TCP
capture covers ports 1-65535 and the managed local-destination address set.

Fresh installs default to 32768 active connections. Set CFPP_MAX_CONNECTIONS to
an integer from 1 through 500000 before fresh installation, for example:
  sudo env CFPP_MAX_CONNECTIONS=65536 bash install.sh --check-only
  sudo env CFPP_MAX_CONNECTIONS=65536 bash install.sh
This is a resource guard, not a connection-performance guarantee. The bundled
capacity-500k.conf is a historical artifact; fresh installation generates its
service limit and capacity sysctls from the selected guard.

The background reconciler updates only CFPP-owned Docker routes under a shared
lock, after validating the installed configuration. It does not rediscover or
rewrite ingress addresses or replay the firewall automatically. After changing
ingress topology, use an explicitly reviewed migration. Use cfppctl status PORT
for diagnosis and cfppctl repair for explicit backed-up rule reapplication.

The installer does not replace customer-owned sets or chains such as long_cc,
short_cc, or short_cc2. Do not change trusted CIDRs merely to bypass a rejection.

Linux kernel integration, real Docker publishing, and upstream Cloudflare/client
login tests are required release evidence. A package checksum or script check
cannot establish those results. This candidate has no throughput guarantee.

CentOS 7 requires a working Vault/internal repository or runtime dependencies
installed in advance: iproute/iproute2, iptables, ipset, curl, kmod, Python 3,
and util-linux. Run check-only first; dependency installation may need a repository.
EOF

(
  cd "$STAGING_DIRECTORY"
  find . -maxdepth 1 -type f ! -name SHA256SUMS -print | LC_ALL=C sort | xargs sha256sum >SHA256SUMS
)

if [[ -e $OUTPUT || -L $OUTPUT ]]; then
  rm -rf -- "$OUTPUT"
fi
mv "$STAGING_DIRECTORY" "$OUTPUT"

if [[ -e $ARCHIVE_OUTPUT || -L $ARCHIVE_OUTPUT ]]; then
  rm -f -- "$ARCHIVE_OUTPUT"
fi
python3 - "$OUTPUT" "$ARCHIVE_OUTPUT" <<'PYARCHIVE'
import sys,tarfile
from pathlib import Path
root=Path(sys.argv[1])
def neutral_owner(info):
    info.uid=info.gid=0
    info.uname=info.gname=''
    return info
with tarfile.open(sys.argv[2],'w:gz',format=tarfile.PAX_FORMAT) as archive:
    archive.add(root,arcname=root.name,filter=neutral_owner)
PYARCHIVE
(
  cd "$(dirname -- "$ARCHIVE_OUTPUT")"
  sha256sum "$(basename -- "$ARCHIVE_OUTPUT")" >"$(basename -- "$ARCHIVE_OUTPUT").sha256"
)

if [[ -e $UPLOAD_OUTPUT || -L $UPLOAD_OUTPUT ]]; then
  rm -rf -- "$UPLOAD_OUTPUT"
fi
mkdir -p "$UPLOAD_OUTPUT"
for upload_file in \
  BUILD-INFO.txt \
  capacity-500k.conf \
  cf-pp-gateway \
  cf-pp-gateway-legacy.service \
  cf-pp-gateway-network.service \
  cf-pp-gateway.service \
  cf-pp-gateway-reconcile.service \
  cf-pp-gateway-reconcile.timer \
  cfppctl \
  topology-plan.py \
  network-reconcile \
  cloudflare.cidrs \
  config.json \
  network-apply \
  network-check \
  network-common \
  network-diagnose \
  network-remove \
  network.env \
  uninstall.sh; do
  install -m 0644 "$OUTPUT/$upload_file" "$UPLOAD_OUTPUT/$upload_file"
done
install -m 0644 "$OUTPUT/install.sh" "$UPLOAD_OUTPUT/host-install.sh"
sed \
  -e "s#@@PACKAGE_NAME@@#$PACKAGE_NAME#g" \
  -e "s#@@BASE_URL@@#$ONLINE_BASE_URL#g" \
  "$PROJECT_DIRECTORY/packaging/delivery/online-install.sh" >"$UPLOAD_OUTPUT/install.sh"
chmod 0755 "$UPLOAD_OUTPUT/install.sh"
chmod 0755 "$UPLOAD_OUTPUT/uninstall.sh"
(
  cd "$UPLOAD_OUTPUT"
  find . -maxdepth 1 -type f ! -name install.sh ! -name SHA256SUMS -print | LC_ALL=C sort | xargs sha256sum >SHA256SUMS
)

if [[ -e $UPLOAD_BUNDLE_OUTPUT || -L $UPLOAD_BUNDLE_OUTPUT ]]; then
  rm -f -- "$UPLOAD_BUNDLE_OUTPUT"
fi
(
  cd "$(dirname -- "$UPLOAD_OUTPUT")"
  zip -qr "$(basename -- "$UPLOAD_BUNDLE_OUTPUT")" "$(basename -- "$UPLOAD_OUTPUT")"
  sha256sum "$(basename -- "$UPLOAD_BUNDLE_OUTPUT")" >"$(basename -- "$UPLOAD_BUNDLE_OUTPUT").sha256"
)

printf 'created %s\n' "$OUTPUT"
du -sh "$OUTPUT"
printf 'files:\n'
find "$OUTPUT" -maxdepth 1 -type f -exec basename {} \; | LC_ALL=C sort
printf 'archive %s\n' "$ARCHIVE_OUTPUT"
sha256sum "$ARCHIVE_OUTPUT"
printf 'upload directory %s\n' "$UPLOAD_OUTPUT"
printf 'upload bundle %s\n' "$UPLOAD_BUNDLE_OUTPUT"
sha256sum "$UPLOAD_BUNDLE_OUTPUT"
