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

readonly PROJECT_DIRECTORY=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
readonly VERSION=${VERSION:-0.8.0-rc1}
readonly PACKAGE_NAME=${PACKAGE_NAME:-cfpp-unified-v$VERSION}
readonly PACKAGE_DIRECTORY=${1:-$PROJECT_DIRECTORY/dist/$PACKAGE_NAME}
readonly ARCHIVE=${ARCHIVE:-$PROJECT_DIRECTORY/dist/$PACKAGE_NAME.tar.gz}
readonly UPLOAD_DIRECTORY=${UPLOAD_DIRECTORY:-$PROJECT_DIRECTORY/dist/cfpp}
readonly UPLOAD_BUNDLE=${UPLOAD_BUNDLE:-$PROJECT_DIRECTORY/dist/cfpp-web-upload-v$VERSION.zip}
readonly EXPECTED_ONLINE_BASE_URL=${ONLINE_BASE_URL:-https://example.invalid/cfpp}
readonly WORK_DIRECTORY=$(mktemp -d /tmp/cfpp-directory-verification.XXXXXX)
cleanup() { find "$WORK_DIRECTORY" -depth -delete 2>/dev/null || true; }
trap cleanup EXIT
fail() { printf '%s\n' "$*" >&2; exit 1; }

[[ -d $PACKAGE_DIRECTORY && -r $PACKAGE_DIRECTORY/SHA256SUMS ]]
for name in install.sh cf-pp-gateway cfppctl topology-plan.py network-common network-apply network-check network-remove network-diagnose network-reconcile uninstall.sh; do
  [[ -x $PACKAGE_DIRECTORY/$name ]] || fail "missing executable: $name"
done
for name in cf-pp-gateway.service cf-pp-gateway-legacy.service cf-pp-gateway-network.service cf-pp-gateway-reconcile.service cf-pp-gateway-reconcile.timer config.json cloudflare.cidrs network.env BUILD-INFO.txt README.txt capacity-500k.conf; do
  [[ -r $PACKAGE_DIRECTORY/$name ]] || fail "missing payload file: $name"
done
(cd "$PACKAGE_DIRECTORY" && sha256sum --check SHA256SUMS)
# Every delivered regular file must be included exactly once, including newly
# added controls, topology planner, reconciler, and timer units.
python3 - "$PACKAGE_DIRECTORY" <<'PYMANIFEST'
from pathlib import Path
import sys
root=Path(sys.argv[1]); records=[]
for row in (root/'SHA256SUMS').read_text().splitlines():
    digest,name=row.split(maxsplit=1)
    if name.startswith('*'):name=name[1:]
    if name.startswith('./'):name=name[2:]
    assert len(digest)==64 and '/' not in name and name not in ('','SHA256SUMS'),row
    records.append(name)
assert len(records)==len(set(records)), 'duplicate checksum manifest entries'
actual={p.name for p in root.iterdir() if p.is_file() and p.name!='SHA256SUMS'}
assert set(records)==actual,(set(records)^actual)
PYMANIFEST

for name in install.sh cfppctl network-common network-apply network-check network-remove network-diagnose network-reconcile uninstall.sh; do
  bash -n "$PACKAGE_DIRECTORY/$name"
done
python3 - "$PACKAGE_DIRECTORY/topology-plan.py" <<'PYSYNTAX'
from pathlib import Path
import sys
p=Path(sys.argv[1]);compile(p.read_text(),str(p),'exec')
PYSYNTAX
CFPP_MAX_CONNECTIONS=32768 bash "$PACKAGE_DIRECTORY/install.sh" --help >/dev/null
CFPP_MAX_CONNECTIONS=32768 sh "$PACKAGE_DIRECTORY/install.sh" --help >/dev/null
bash "$PACKAGE_DIRECTORY/cfppctl" --help >"$WORK_DIRECTORY/control-help"
python3 "$PACKAGE_DIRECTORY/topology-plan.py" --help >/dev/null
for ceiling in 1 32768 500000; do CFPP_MAX_CONNECTIONS=$ceiling bash "$PACKAGE_DIRECTORY/install.sh" --help >/dev/null; done
for ceiling in 0 500001 -1 00 abc 18446744073709551617; do
  if CFPP_MAX_CONNECTIONS=$ceiling bash "$PACKAGE_DIRECTORY/install.sh" --help >"$WORK_DIRECTORY/invalid-ceiling" 2>&1; then fail "invalid connection ceiling accepted: $ceiling"; fi
done
for operation in plan check upgrade repair rollback status sync; do grep -q "cfppctl $operation" "$WORK_DIRECTORY/control-help"; done

grep -Fq 'readonly INSTALLER_MODE="directory"' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'readonly INSTALLER_VERSION="'"$VERSION"'"' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'TARGET_MAX_CONNECTIONS=${CFPP_MAX_CONNECTIONS:-32768}' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq '"$PAYLOAD_DIRECTORY/topology-plan.py"' "$PACKAGE_DIRECTORY/install.sh"
grep -q 'TARGET_INGRESS_INTERFACES=' "$PACKAGE_DIRECTORY/install.sh"
grep -q 'destination_ips' "$PACKAGE_DIRECTORY/topology-plan.py"
grep -Fq 'CFPP_INGRESS_INTERFACES="%s"' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'bash "$PAYLOAD_DIRECTORY/cfppctl" check' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'bash "$PAYLOAD_DIRECTORY/cfppctl" upgrade' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'TARGET_MAX_CONNECTIONS * 4 + 4096' "$PACKAGE_DIRECTORY/install.sh"
grep -q '^rollback() {' "$PACKAGE_DIRECTORY/install.sh"
grep -q 'backup_items=()' "$PACKAGE_DIRECTORY/install.sh"
grep -q 'installation failed; restoring the previous gateway state' "$PACKAGE_DIRECTORY/install.sh"
grep -Fq 'systemctl enable --now cf-pp-gateway-reconcile.timer' "$PACKAGE_DIRECTORY/install.sh"
# The preservation branch must precede fresh-install rendering and mutations.
python3 - "$PACKAGE_DIRECTORY/install.sh" <<'PYUPGRADE'
from pathlib import Path
import sys
s=Path(sys.argv[1]).read_text()
start=s.index('# Existing installations use the transactional helper upgrade')
end=s.index('# A partial installation',start)
branch=s[start:end]
assert 'cfppctl" upgrade' in branch and 'exit $?' in branch
assert '/usr/local/sbin/cf-pp-gateway' not in branch
PYUPGRADE
if grep -q '^__CFPP_ARCHIVE_BELOW__$' "$PACKAGE_DIRECTORY/install.sh"; then fail 'directory installer contains embedded archive marker'; fi
if grep -Eq '156\.246\.(69|70|71)' "$PACKAGE_DIRECTORY/install.sh" "$PACKAGE_DIRECTORY/config.json"; then fail 'hard-coded deployment address remains'; fi

grep -q '^CFPP_CAPTURE_PORTS=1:65535$' "$PACKAGE_DIRECTORY/network.env"
grep -q '^CFPP_DESTINATION_ADDRESSES_FILE=/etc/cf-pp-gateway/local-destinations.ipv4$' "$PACKAGE_DIRECTORY/network.env"
grep -Fq 'CFPP_DESTINATION_V4_SET=cfppgw_dst4' "$PACKAGE_DIRECTORY/network-common"
grep -Fq 'for interface in "${CFPP_INGRESS_DEVICES[@]}"' "$PACKAGE_DIRECTORY/network-apply"
grep -Fq -- '--match-set "$CFPP_DESTINATION_V4_SET" dst' "$PACKAGE_DIRECTORY/network-apply"
grep -Fq -- '-j DNAT --to-destination "$CFPP_INGRESS_ADDRESS:$CFPP_INGRESS_PORT"' "$PACKAGE_DIRECTORY/network-apply"
grep -q 'network_lock' "$PACKAGE_DIRECTORY/network-apply"
grep -q 'network-reconcile --apply' "$PACKAGE_DIRECTORY/cf-pp-gateway-reconcile.service"
grep -q '^OnUnitActiveSec=' "$PACKAGE_DIRECTORY/cf-pp-gateway-reconcile.timer"
grep -Fq 'max_connections_default=32768' "$PACKAGE_DIRECTORY/BUILD-INFO.txt"
grep -Fq 'all-global-ipv4-on-selected-ingress-interfaces' "$PACKAGE_DIRECTORY/BUILD-INFO.txt"
grep -q 'long_cc' "$PACKAGE_DIRECTORY/README.txt"
grep -Fq 'CFPP_MAX_CONNECTIONS' "$PACKAGE_DIRECTORY/README.txt"
grep -Fq 'network-helper-upgrade-preserve-config-and-main-daemon' "$PACKAGE_DIRECTORY/BUILD-INFO.txt"

python3 - "$PACKAGE_DIRECTORY/config.json" "$WORK_DIRECTORY/config.json" <<'PYCONFIG'
import json,sys
from pathlib import Path
s=Path(sys.argv[1]).read_text().replace('@@ORIGIN_IP@@','203.0.113.10')
s=s.replace('@@ALLOWED_PROXY_DESTINATIONS@@','    "203.0.113.10",\n    "203.0.113.11"')
assert '@@' not in s
config=json.loads(s)
assert config['allowed_origin_cidrs']==['203.0.113.10/32']
assert config['allowed_proxy_destinations']==['203.0.113.10','203.0.113.11']
assert config['listeners'][0]['listen']=='203.0.113.10:39000'
assert config['listeners'][0]['missing_proxy_policy']=='passthrough-original'
assert config['max_connections']==32768
Path(sys.argv[2]).write_text(s)
PYCONFIG

[[ -r $ARCHIVE && -r $ARCHIVE.sha256 ]]
(cd "$(dirname -- "$ARCHIVE")" && sha256sum --check "$(basename -- "$ARCHIVE").sha256")
tar -tzf "$ARCHIVE" | awk -v prefix="$PACKAGE_NAME/" 'index($0,prefix)!=1 {exit 1} END {if(NR==0) exit 1}'

[[ -x $UPLOAD_DIRECTORY/install.sh && -x $UPLOAD_DIRECTORY/uninstall.sh && -r $UPLOAD_DIRECTORY/SHA256SUMS ]]
(cd "$UPLOAD_DIRECTORY" && sha256sum --check SHA256SUMS)
for name in host-install.sh cfppctl network-common network-apply network-check network-remove network-diagnose network-reconcile uninstall.sh; do
  [[ -r $UPLOAD_DIRECTORY/$name ]] || fail "online payload missing: $name"
  bash -n "$UPLOAD_DIRECTORY/$name"
done
for name in topology-plan.py cf-pp-gateway-reconcile.service cf-pp-gateway-reconcile.timer; do [[ -r $UPLOAD_DIRECTORY/$name ]]; done
bash -n "$UPLOAD_DIRECTORY/install.sh"
grep -Fq 'readonly PACKAGE_NAME="'"$PACKAGE_NAME"'"' "$UPLOAD_DIRECTORY/install.sh"
grep -Fq 'readonly BASE_URL="'"$EXPECTED_ONLINE_BASE_URL"'"' "$UPLOAD_DIRECTORY/install.sh"
grep -q 'downloading checksum manifest' "$UPLOAD_DIRECTORY/install.sh"
grep -Fq 'bash "$WORK_DIRECTORY/$HOST_INSTALLER"' "$UPLOAD_DIRECTORY/install.sh"
if find "$UPLOAD_DIRECTORY" -maxdepth 1 -type f -name '*.tar.gz' | grep -q .; then fail 'flat upload contains tar archive'; fi
awk '/^readonly PACKAGE_FILES=\(/ {inside=1;next} inside&&/^\)/ {exit} inside {gsub(/^[[:space:]]+|[[:space:]]+$/,"");print}' "$UPLOAD_DIRECTORY/install.sh" | LC_ALL=C sort >"$WORK_DIRECTORY/online-files.actual"
awk '{name=$2;sub(/^\.\//,"",name);print name}' "$UPLOAD_DIRECTORY/SHA256SUMS" | LC_ALL=C sort >"$WORK_DIRECTORY/online-files.expected"
diff -u "$WORK_DIRECTORY/online-files.expected" "$WORK_DIRECTORY/online-files.actual"
if grep -q '@@' "$UPLOAD_DIRECTORY/install.sh"; then fail 'online installer has unreplaced placeholder'; fi
[[ -r $UPLOAD_BUNDLE && -r $UPLOAD_BUNDLE.sha256 ]]
(cd "$(dirname -- "$UPLOAD_BUNDLE")" && sha256sum --check "$(basename -- "$UPLOAD_BUNDLE").sha256")
unzip -Z1 "$UPLOAD_BUNDLE" | awk 'index($0,"cfpp/")!=1 {exit 1} END {if(NR==0) exit 1}'
if unzip -Z1 "$UPLOAD_BUNDLE" | grep -Eq '\.tar\.gz$'; then fail 'website bundle contains target-side tar archive'; fi
printf 'unified directory package verification passed; Linux network and customer-login tests are separate\n'
