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

ask() {
  local var="$1" prompt="$2" default="${3:-}"
  local value
  if [[ -n "$default" ]]; then
    read -r -p "$prompt [$default]: " value
    value="${value:-$default}"
  else
    read -r -p "$prompt: " value
  fi
  printf -v "$var" '%s' "$value"
}

ask_secret() {
  local var="$1" prompt="$2"
  local value
  read -r -s -p "$prompt: " value
  echo
  printf -v "$var" '%s' "$value"
}

render() {
  local src="$1" dst="$2"
  mkdir -p "$(dirname "$dst")"
  envsubst < "$src" > "$dst"
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || { echo "Missing required command: $1" >&2; exit 1; }
}

require_cmd envsubst

ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
OUT_DIR="$ROOT_DIR/output/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUT_DIR"

cat <<MSG
This script will generate files for:
- db1
- db2
- db3
- proxy1

Use hostnames db1/db2/db3 in /etc/hosts as planned.
MSG

ask WG_SUBNET "WireGuard subnet CIDR base (used only in templates, e.g. 10.20.0.0/24)" "10.20.0.0/24"
ask ETCD_CLUSTER_TOKEN "etcd cluster token" "pg-ha-cluster"
ask PATRONI_SCOPE "Patroni scope" "pg-ha-demo"
ask PATRONI_NAMESPACE "Patroni namespace" "/service/"
ask PATRONI_SUPERUSER_USERNAME "PostgreSQL superuser name" "postgres"
ask PATRONI_REPLICATION_USERNAME "Patroni replication username" "replicator"
ask PATRONI_DB "Application database name" "appdb"
ask_secret POSTGRES_PASSWORD "PostgreSQL superuser password"
ask_secret REPLICATION_PASSWORD "Replication password"
ask_secret APP_API_PASSWORD "PostgREST app_api password"

ask DB1_PUBLIC "db1 public IP or hostname"
ask DB2_PUBLIC "db2 public IP or hostname"
ask DB3_PUBLIC "db3 public IP or hostname"
ask PROXY1_PUBLIC "proxy1 public IP or hostname"

ask DB1_WG_IP "db1 WireGuard IP" "10.20.0.11"
ask DB2_WG_IP "db2 WireGuard IP" "10.20.0.12"
ask DB3_WG_IP "db3 WireGuard IP" "10.20.0.13"
ask PROXY1_WG_IP "proxy1 WireGuard IP" "10.20.0.21"

ask_secret DB1_WG_PRIVATE "db1 WireGuard private key"
ask DB1_WG_PUBLIC "db1 WireGuard public key"
ask_secret DB2_WG_PRIVATE "db2 WireGuard private key"
ask DB2_WG_PUBLIC "db2 WireGuard public key"
ask_secret DB3_WG_PRIVATE "db3 WireGuard private key"
ask DB3_WG_PUBLIC "db3 WireGuard public key"
ask_secret PROXY1_WG_PRIVATE "proxy1 WireGuard private key"
ask PROXY1_WG_PUBLIC "proxy1 WireGuard public key"

ask PGADMIN_DEFAULT_EMAIL "pgAdmin login email" "admin@example.com"
ask_secret PGADMIN_DEFAULT_PASSWORD "pgAdmin login password"
ask POSTGREST_DB_SCHEMAS "PostgREST DB schemas" "api"
ask POSTGREST_DB_ANON_ROLE "PostgREST anonymous role" "web_anon"
ask CADDY_EMAIL "Caddy ACME email" "you@example.com"
ask PGADMIN_DOMAIN "Domain for pgAdmin" "pgadmin.example.com"
ask API_DOMAIN "Domain for PostgREST" "api.example.com"
ask PGADMIN_BASIC_USER "Caddy basic auth username for pgAdmin" "admin"
ask_secret PGADMIN_BASIC_PASSWORD "Caddy basic auth password for pgAdmin"
ask API_BASIC_USER "Caddy basic auth username for PostgREST" "apiuser"
ask_secret API_BASIC_PASSWORD "Caddy basic auth password for PostgREST"

require_cmd docker
PGADMIN_BCRYPT_HASH="$(docker run --rm caddy:2 caddy hash-password --plaintext "$PGADMIN_BASIC_PASSWORD" 2>/dev/null)"
API_BCRYPT_HASH="$(docker run --rm caddy:2 caddy hash-password --plaintext "$API_BASIC_PASSWORD" 2>/dev/null)"

export WG_SUBNET ETCD_CLUSTER_TOKEN PATRONI_SCOPE PATRONI_NAMESPACE PATRONI_SUPERUSER_USERNAME PATRONI_REPLICATION_USERNAME PATRONI_DB POSTGRES_PASSWORD REPLICATION_PASSWORD APP_API_PASSWORD
export DB1_PUBLIC DB2_PUBLIC DB3_PUBLIC PROXY1_PUBLIC DB1_WG_IP DB2_WG_IP DB3_WG_IP PROXY1_WG_IP
export DB1_WG_PRIVATE DB1_WG_PUBLIC DB2_WG_PRIVATE DB2_WG_PUBLIC DB3_WG_PRIVATE DB3_WG_PUBLIC PROXY1_WG_PRIVATE PROXY1_WG_PUBLIC
export PGADMIN_DEFAULT_EMAIL PGADMIN_DEFAULT_PASSWORD POSTGREST_DB_SCHEMAS POSTGREST_DB_ANON_ROLE CADDY_EMAIL PGADMIN_DOMAIN API_DOMAIN PGADMIN_BASIC_USER API_BASIC_USER PGADMIN_BCRYPT_HASH API_BCRYPT_HASH

for node in db1 db2 db3; do
  mkdir -p "$OUT_DIR/$node"
  cp "$ROOT_DIR/db-node/Dockerfile" "$OUT_DIR/$node/Dockerfile"
  cp "$ROOT_DIR/db-node/compose.yaml" "$OUT_DIR/$node/compose.yaml"
  cp "$ROOT_DIR/db-node/firewall-ufw.sh" "$OUT_DIR/$node/firewall-ufw.sh"
  case "$node" in
    db1) export NODE_NAME=db1 WG_IP="$DB1_WG_IP" ;;
    db2) export NODE_NAME=db2 WG_IP="$DB2_WG_IP" ;;
    db3) export NODE_NAME=db3 WG_IP="$DB3_WG_IP" ;;
  esac
  cat > "$OUT_DIR/$node/.env" <<ENV
NODE_NAME=$NODE_NAME
WG_IP=$WG_IP
PATRONI_SCOPE=$PATRONI_SCOPE
PATRONI_NAMESPACE=$PATRONI_NAMESPACE
PATRONI_SUPERUSER_USERNAME=$PATRONI_SUPERUSER_USERNAME
PATRONI_REPLICATION_USERNAME=$PATRONI_REPLICATION_USERNAME
PATRONI_DB=$PATRONI_DB
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
REPLICATION_PASSWORD=$REPLICATION_PASSWORD
ETCD_CLUSTER_TOKEN=$ETCD_CLUSTER_TOKEN
ENV
  envsubst < "$ROOT_DIR/db-node/patroni.yml" > "$OUT_DIR/$node/patroni.yml"
done

cat > "$OUT_DIR/db1/wg0.conf" <<WG
[Interface]
Address = ${DB1_WG_IP}/24
ListenPort = 51820
PrivateKey = ${DB1_WG_PRIVATE}
SaveConfig = false

[Peer]
PublicKey = ${DB2_WG_PUBLIC}
AllowedIPs = ${DB2_WG_IP}/32
Endpoint = ${DB2_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${DB3_WG_PUBLIC}
AllowedIPs = ${DB3_WG_IP}/32
Endpoint = ${DB3_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${PROXY1_WG_PUBLIC}
AllowedIPs = ${PROXY1_WG_IP}/32
Endpoint = ${PROXY1_PUBLIC}:51820
PersistentKeepalive = 25
WG

cat > "$OUT_DIR/db2/wg0.conf" <<WG
[Interface]
Address = ${DB2_WG_IP}/24
ListenPort = 51820
PrivateKey = ${DB2_WG_PRIVATE}
SaveConfig = false

[Peer]
PublicKey = ${DB1_WG_PUBLIC}
AllowedIPs = ${DB1_WG_IP}/32
Endpoint = ${DB1_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${DB3_WG_PUBLIC}
AllowedIPs = ${DB3_WG_IP}/32
Endpoint = ${DB3_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${PROXY1_WG_PUBLIC}
AllowedIPs = ${PROXY1_WG_IP}/32
Endpoint = ${PROXY1_PUBLIC}:51820
PersistentKeepalive = 25
WG

cat > "$OUT_DIR/db3/wg0.conf" <<WG
[Interface]
Address = ${DB3_WG_IP}/24
ListenPort = 51820
PrivateKey = ${DB3_WG_PRIVATE}
SaveConfig = false

[Peer]
PublicKey = ${DB1_WG_PUBLIC}
AllowedIPs = ${DB1_WG_IP}/32
Endpoint = ${DB1_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${DB2_WG_PUBLIC}
AllowedIPs = ${DB2_WG_IP}/32
Endpoint = ${DB2_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${PROXY1_WG_PUBLIC}
AllowedIPs = ${PROXY1_WG_IP}/32
Endpoint = ${PROXY1_PUBLIC}:51820
PersistentKeepalive = 25
WG

mkdir -p "$OUT_DIR/proxy1"
cp "$ROOT_DIR/proxy/compose.yaml" "$OUT_DIR/proxy1/compose.yaml"
cp "$ROOT_DIR/proxy/firewall-ufw.sh" "$OUT_DIR/proxy1/firewall-ufw.sh"
cat > "$OUT_DIR/proxy1/.env" <<ENV
PGADMIN_DEFAULT_EMAIL=$PGADMIN_DEFAULT_EMAIL
PGADMIN_DEFAULT_PASSWORD=$PGADMIN_DEFAULT_PASSWORD
POSTGREST_DB_URI=postgres://app_api:$APP_API_PASSWORD@127.0.0.1:5432/$PATRONI_DB
POSTGREST_DB_SCHEMAS=$POSTGREST_DB_SCHEMAS
POSTGREST_DB_ANON_ROLE=$POSTGREST_DB_ANON_ROLE
ENV
cat > "$OUT_DIR/proxy1/haproxy.cfg" <<CFG
global
    log stdout format raw local0

defaults
    log global
    mode tcp
    timeout connect 5s
    timeout client  1m
    timeout server  1m

frontend postgres_write
    bind *:5432
    default_backend patroni_primary

backend patroni_primary
    mode tcp
    option httpchk GET /primary
    http-check expect status 200
    server db1 db1:5432 check port 8008
    server db2 db2:5432 check port 8008
    server db3 db3:5432 check port 8008
CFG
cat > "$OUT_DIR/proxy1/Caddyfile" <<CFG
{
    email $CADDY_EMAIL
}

$PGADMIN_DOMAIN {
    basic_auth {
        $PGADMIN_BASIC_USER $PGADMIN_BCRYPT_HASH
    }
    reverse_proxy 127.0.0.1:8080
}

$API_DOMAIN {
    basic_auth {
        $API_BASIC_USER $API_BCRYPT_HASH
    }
    reverse_proxy 127.0.0.1:3000
}
CFG
cat > "$OUT_DIR/proxy1/wg0.conf" <<WG
[Interface]
Address = ${PROXY1_WG_IP}/24
ListenPort = 51820
PrivateKey = ${PROXY1_WG_PRIVATE}
SaveConfig = false

[Peer]
PublicKey = ${DB1_WG_PUBLIC}
AllowedIPs = ${DB1_WG_IP}/32
Endpoint = ${DB1_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${DB2_WG_PUBLIC}
AllowedIPs = ${DB2_WG_IP}/32
Endpoint = ${DB2_PUBLIC}:51820
PersistentKeepalive = 25

[Peer]
PublicKey = ${DB3_WG_PUBLIC}
AllowedIPs = ${DB3_WG_IP}/32
Endpoint = ${DB3_PUBLIC}:51820
PersistentKeepalive = 25
WG

cp "$ROOT_DIR/db-node/init-appdb.sql" "$OUT_DIR/db1/init-appdb.sql"
sed -i "s/change-this-api-password/$APP_API_PASSWORD/g" "$OUT_DIR/db1/init-appdb.sql"

cat > "$OUT_DIR/DEPLOYMENT.md" <<EOF2
# Deployment

## On db1, db2, db3

1. Copy each host's generated directory to the matching VPS.
2. Install WireGuard and place \
   /etc/wireguard/wg0.conf from the generated file.
3. Bring up WireGuard:
   systemctl enable --now wg-quick@wg0
4. Optionally apply firewall rules with firewall-ufw.sh
5. Add /etc/hosts entries for db1 db2 db3 on all four hosts.
6. Start Docker stack:
   docker compose up -d --build

## On proxy1

1. Copy proxy1 directory to the proxy VPS.
2. Install WireGuard and place /etc/wireguard/wg0.conf.
3. Bring up WireGuard.
4. Add /etc/hosts entries for db1 db2 db3.
5. Start Docker stack:
   docker compose up -d

## Initialize database

Run on the current primary:
psql -h 127.0.0.1 -U $PATRONI_SUPERUSER_USERNAME -d postgres -f init-appdb.sql

Use the init-appdb.sql from db1 output.
EOF2

echo "Generated files in: $OUT_DIR"
