#!/bin/bash
# ============================================================
# alpha-setdomain — attach (or change) the panel's domain AFTER
# installation. Written for the common case: the panel was
# installed on the server's IP, a domain was bought later.
#
#   alpha-setdomain example.com                  # keeps/creates a random panel host
#   alpha-setdomain example.com panel-abc123.example.com
#   alpha-setdomain example.com panel-abc123.example.com admin@example.com
#   alpha-setdomain test.example.com "" "" example.com   # live on a SUBDOMAIN;
#                                    # the 4th argument is the zone you edit at
#                                    # the registrar (DNS_ZONE)
#
# Checks DNS first, rewrites the panel config and vhosts, requests
# Let's Encrypt certificates, then reloads. Refuses to break a
# working panel: nginx is tested before anything is applied and the
# previous vhosts are restored if the test fails.
# ============================================================
set -uo pipefail

b(){ [ -t 1 ] && printf '\033[%sm' "$1" || true; }
say(){ printf '  %s·%s %s\n' "$(b '0;36')" "$(b 0)" "$*"; }
ok(){  printf '  %s✓%s %s\n' "$(b '0;32')" "$(b 0)" "$*"; }
warn(){ printf '  %s⚠%s %s\n' "$(b '1;33')" "$(b 0)" "$*" >&2; }
die(){ printf '  %s✗%s %s\n' "$(b '1;31')" "$(b 0)" "$*" >&2; exit 1; }

[ "$(id -u)" -eq 0 ] || die "run as root (sudo alpha-setdomain …)"

DOMAIN="${1:-}"
[ -n "$DOMAIN" ] || die "usage: alpha-setdomain <domain> [panel-hostname] [admin-email]"
printf '%s' "$DOMAIN" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9-]+)+$' || die "\"$DOMAIN\" does not look like a domain."

[ -r /etc/alpha-panel.conf ] && . /etc/alpha-panel.conf
OLD_PANEL="${PANEL_SUBDOMAIN:-}"
PANEL="${2:-}"
[ -n "$PANEL" ] || PANEL="panel-$(openssl rand -hex 3).$DOMAIN"
EMAIL="${3:-${ADMIN_EMAIL:-admin@$DOMAIN}}"
# The registrar zone. A subdomain install (test.example.com) keeps its records
# in example.com. Explicit 4th argument wins; else the recorded zone if the new
# domain still sits inside it; else the domain itself.
ZONE="${4:-}"
if [ -z "$ZONE" ]; then
  case "$DOMAIN" in "${DNS_ZONE:-}"|*".${DNS_ZONE:-#}") ZONE="$DNS_ZONE" ;; *) ZONE="$DOMAIN" ;; esac
fi
case "$DOMAIN" in "$ZONE"|*".$ZONE") ;; *) die "$DOMAIN is not inside the DNS zone $ZONE." ;; esac
if [ "$DOMAIN" = "$ZONE" ]; then RH="@"; WH="*"; else RH="${DOMAIN%.$ZONE}"; WH="*.$RH"; fi

IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
PROV=""
for d in /opt/alpha-panel/provisioning /opt/alpha-install/provisioning /root/alpha/provisioning; do
  [ -f "$d/nginx/admin.tpl" ] && { PROV="$d"; break; }
done
[ -n "$PROV" ] || die "provisioning/ not found — reinstall or pass its path in \$PROV."

echo
say "domain    $DOMAIN$([ "$DOMAIN" != "$ZONE" ] && echo "  (in the DNS zone $ZONE)")"
say "panel     $PANEL"
say "email     $EMAIL"
say "server IP ${IP:-unknown}"
echo

# ---- every host this panel serves under the new domain ----
# The panel itself only needs the first three, but the features that hang off a
# domain each answer on their own name. They are created here so the user never
# has to hand-build a vhost: accounts (sign-in portal), api (mobile/REST host),
# pay (checkout), licenses (licence portal), share (expiring download links),
# mail + webmail (only if the mail server is installed).
CORE_HOSTS="$PANEL www.$DOMAIN $DOMAIN"
DEP_HOSTS="accounts.$DOMAIN api.$DOMAIN pay.$DOMAIN licenses.$DOMAIN share.$DOMAIN"
[ -f /usr/sbin/postfix ] && DEP_HOSTS="$DEP_HOSTS mail.$DOMAIN"
[ -d /var/www/webmail ] && DEP_HOSTS="$DEP_HOSTS webmail.$DOMAIN"

# ---- DNS must already point here, or the certificate request fails ----
dns_of(){ getent ahostsv4 "$1" 2>/dev/null | awk '{print $1; exit}'; }
BAD=0
for h in $CORE_HOSTS; do
  got="$(dns_of "$h")"
  if [ -z "$got" ]; then warn "$h does not resolve yet"; BAD=1
  elif [ -n "$IP" ] && [ "$got" != "$IP" ]; then warn "$h resolves to $got, not $IP"; BAD=1
  else ok "$h → $got"; fi
done
if [ "$BAD" = 1 ]; then
  echo
  warn "DNS is not fully pointing at this server."
  warn "At the registrar for $ZONE, add two A records pointing at ${IP:-this server}:"
  warn "  host \"$RH\"  and  host \"$WH\"   (the wildcard covers the panel, accounts, pay, mail …)"
  warn "then wait for them to propagate."
  if [ "${FORCE:-0}" = 1 ]; then
    warn "FORCE=1 — configuring the names anyway. No certificates will be requested;"
    warn "re-run without FORCE once DNS resolves here and they will be issued then."
    echo
  else
    die "nothing changed. Re-run with FORCE=1 to configure anyway (no certificates)."
  fi
fi

# ---- persist the new identity ----
cp -f /etc/alpha-panel.conf "/etc/alpha-panel.conf.bak.$(date +%s)" 2>/dev/null || true
# ⚠️ Key by key. Rewriting the file dropped every other key (DNS_ZONE,
# DIST_BUILD_DIR, PORTAL_ROOT) that other tools keep there.
conf_set(){ local f=/etc/alpha-panel.conf; touch "$f"
  if grep -q "^$1=" "$f"; then sed -i "s|^$1=.*|$1=$2|" "$f"; else printf '%s=%s\n' "$1" "$2" >> "$f"; fi; }
conf_set PRIMARY_DOMAIN  "$DOMAIN"
conf_set PANEL_SUBDOMAIN "$PANEL"
conf_set ADMIN_EMAIL     "$EMAIL"
conf_set DNS_ZONE        "$ZONE"
chmod 644 /etc/alpha-panel.conf
ok "wrote /etc/alpha-panel.conf"

if [ -f "$PROV/provision.env" ]; then
  cp -f "$PROV/provision.env" "$PROV/provision.env.bak.$(date +%s)"
  sed -i -e "s|^PRIMARY_DOMAIN=.*|PRIMARY_DOMAIN=$DOMAIN|" \
         -e "s|^PANEL_SUBDOMAIN=.*|PANEL_SUBDOMAIN=$PANEL|" \
         -e "s|^ADMIN_EMAIL=.*|ADMIN_EMAIL=$EMAIL|" "$PROV/provision.env"
  if grep -q '^DNS_ZONE=' "$PROV/provision.env"; then sed -i "s|^DNS_ZONE=.*|DNS_ZONE=$ZONE|" "$PROV/provision.env"
  else echo "DNS_ZONE=$ZONE" >> "$PROV/provision.env"; fi
  [ "$BAD" = 0 ] && sed -i "s|^ENABLE_TLS=.*|ENABLE_TLS=1|" "$PROV/provision.env"
  ok "updated provision.env"
fi

# ---- re-render the vhosts (snakeoil first; certbot swaps them after) ----
BK="/root/nginx-setdomain.bak.$(date +%s)"; mkdir -p "$BK"
cp -f /etc/nginx/sites-available/admin "$BK/" 2>/dev/null || true
cp -f /etc/nginx/sites-available/alpha "$BK/" 2>/dev/null || true

render(){ # <tpl> <out> <vars…>
  local tpl="$1" out="$2"; shift 2
  envsubst "$*" < "$tpl" > "$out"
}
export PANEL_SUBDOMAIN="$PANEL" PRIMARY_DOMAIN="$DOMAIN"
export PANEL_SSL_CERT=/etc/ssl/certs/ssl-cert-snakeoil.pem PANEL_SSL_KEY=/etc/ssl/private/ssl-cert-snakeoil.key
export LANDING_SSL_CERT=/etc/ssl/certs/ssl-cert-snakeoil.pem LANDING_SSL_KEY=/etc/ssl/private/ssl-cert-snakeoil.key
LE=/etc/letsencrypt/live
[ -f "$LE/$PANEL/fullchain.pem" ] && { PANEL_SSL_CERT="$LE/$PANEL/fullchain.pem"; PANEL_SSL_KEY="$LE/$PANEL/privkey.pem"; }
[ -f "$LE/www.$DOMAIN/fullchain.pem" ] && { LANDING_SSL_CERT="$LE/www.$DOMAIN/fullchain.pem"; LANDING_SSL_KEY="$LE/www.$DOMAIN/privkey.pem"; }

render "$PROV/nginx/admin.tpl"   /etc/nginx/sites-available/admin '${PANEL_SUBDOMAIN} ${PANEL_SSL_CERT} ${PANEL_SSL_KEY}'
# The landing is nginx's default server only while no other site is (same rule
# as lib.sh landing_default): an owner's own default site stays theirs.
LANDING_DEFAULT=" default_server"
for f in /etc/nginx/sites-enabled/* /etc/nginx/conf.d/*.conf; do
  [ -e "$f" ] || continue
  [ "$(basename "$f")" = alpha ] && continue
  grep -qE '^[[:space:]]*listen[^;#]*default_server' "$f" 2>/dev/null && { LANDING_DEFAULT=""; break; }
done
export LANDING_DEFAULT
render "$PROV/nginx/landing.tpl" /etc/nginx/sites-available/alpha '${PRIMARY_DOMAIN} ${LANDING_SSL_CERT} ${LANDING_SSL_KEY} ${LANDING_DEFAULT}'
ln -sf /etc/nginx/sites-available/admin /etc/nginx/sites-enabled/admin
ln -sf /etc/nginx/sites-available/alpha /etc/nginx/sites-enabled/alpha

if ! nginx -t >/dev/null 2>&1; then
  cp -f "$BK/admin" /etc/nginx/sites-available/admin 2>/dev/null || true
  cp -f "$BK/alpha" /etc/nginx/sites-available/alpha 2>/dev/null || true
  nginx -t >/dev/null 2>&1 && systemctl reload nginx
  die "nginx rejected the new vhosts — rolled back (copies in $BK)."
fi
systemctl reload nginx
ok "vhosts now serve $PANEL"

# ---- public portals, if this install has them ----
# Their vhosts carry the old domain in server_name, so re-run the step that
# builds them rather than re-implementing it here.
# Always run it, not only when it is already in ENABLE: a no-domain install uses
# the "core" profile, which skips portals entirely — so the moment a real domain
# arrives is exactly when they need creating. The step is idempotent and skips
# any portal whose source is not shipped.
say "creating the hosts that depend on the domain (accounts, api, pay, licences, share)"
( cd "$PROV" && bash ./bootstrap.sh 02-portals ) 2>&1 | sed 's/^/    /' \
  || warn "the portals step reported a problem — check $PROV/bootstrap.log"
# keep it enabled so later full runs include them
grep -qE '^\s*02-portals\s*$' "$PROV/provision.env" 2>/dev/null \
  || sed -i 's/^\(\s*\)01-panel\s*$/\1 01-panel\n\1 02-portals/' "$PROV/provision.env" 2>/dev/null || true

# ---- certificates ----
# 90-tls already knows every host that needs one (panel, landing, mail, webmail
# and each portal) and re-points the vhosts afterwards.
if [ "$BAD" = 0 ]; then
  say "requesting Let's Encrypt certificates"
  ( cd "$PROV" && bash ./bootstrap.sh 90-tls ) 2>&1 | sed 's/^/    /' \
    || warn "certificate issuance reported a problem — the panel still works on its old certificate."
  if ! nginx -t >/dev/null 2>&1; then
    cp -f "$BK/admin" /etc/nginx/sites-available/admin 2>/dev/null || true
    cp -f "$BK/alpha" /etc/nginx/sites-available/alpha 2>/dev/null || true
    die "nginx rejected the config after certificate install — rolled back (copies in $BK)."
  fi
  # a RESTART, not a reload: workers do not always pick up a freshly issued cert
  systemctl restart nginx
  [ -f "$LE/$PANEL/fullchain.pem" ] && ok "certificate active for $PANEL" \
    || warn "no certificate for $PANEL yet — it is still on the self-signed one."
else
  warn "skipping certificates — the panel keeps its self-signed one for now."
fi

cat <<DONE

  $(b '1;32')Done.$(b 0)  The panel is now at:

      https://$PANEL/login.php

  ${OLD_PANEL:+The old address ($OLD_PANEL) no longer serves the panel.}
  Keep the hostname to yourself — it is deliberately unguessable.

DONE

# ---- what each dependent host needs from DNS ----
# The panel cannot write DNS (that lives at your registrar), so the honest
# finish is to say exactly which records are still missing.
echo "  Hosts now served by this machine:"
MISSING=""
for h in $DEP_HOSTS; do
  got="$(dns_of "$h")"
  vhost="$(grep -Rls "server_name[[:space:]].*\b${h}\b" /etc/nginx/sites-enabled/ 2>/dev/null | head -1)"
  state="served"; [ -z "$vhost" ] && state="not configured"
  if [ -z "$got" ]; then
    printf '    %s%-28s%s %-15s  DNS: %smissing%s\n' "$(b '0;36')" "$h" "$(b 0)" "$state" "$(b '1;33')" "$(b 0)"
    MISSING="$MISSING $h"
  elif [ -n "$IP" ] && [ "$got" != "$IP" ]; then
    printf '    %s%-28s%s %-15s  DNS: %spoints at %s%s\n' "$(b '0;36')" "$h" "$(b 0)" "$state" "$(b '1;33')" "$got" "$(b 0)"
    MISSING="$MISSING $h"
  else
    printf '    %s%-28s%s %-15s  DNS: %sok%s\n' "$(b '0;36')" "$h" "$(b 0)" "$state" "$(b '0;32')" "$(b 0)"
  fi
done

if [ -n "$MISSING" ]; then
  echo
  warn "Add an A record → ${IP:-this server} for each of:"
  for h in $MISSING; do printf '      %s\n' "$h"; done
  warn "A wildcard  *.$DOMAIN → ${IP:-this server}  covers them all at once."
  warn "Then re-run  sudo alpha-setdomain $DOMAIN $PANEL  to pick up their certificates."
  if printf '%s' "$MISSING" | grep -q "mail\."; then
    warn "Mail also wants an MX record  $DOMAIN → mail.$DOMAIN  and a PTR (reverse DNS)"
    warn "for ${IP:-this server}, which is set at your VPS provider, not your registrar."
  fi
  echo
fi
