#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# ORDRAT.IO — issue the wildcard certificate and serve tenant menu subdomains
#
# Run ON the server (188.245.117.146) as root:
#     export HOSTINGER_Token="<hPanel API token>"
#     bash enable-menu-subdomains.sh
#
# Assumes the wildcard DNS record already exists:
#     dig +short anything.oordarat.com   ->  188.245.117.146
#
# This box hosts other customers' sites. The script therefore only ever ADDS:
# it writes one new nginx site, issues one new certificate, and touches no
# existing vhost, no existing certificate and no default_server. It refuses to
# reload nginx if the config does not test clean.
#
# The wildcard lands in /etc/ssl/ordrat/, never in /etc/letsencrypt/live/.
# That directory belongs to certbot - symlinks into archive/, a renewal config,
# and the certificate currently serving the apex. Two issuers, two directories,
# no shared state.
#
# It does NOT set MENU_DOMAIN. That is the switch that changes what links and
# QR codes say, it is instant and reversible, and it belongs to a separate
# decision once this has been eyeballed in a browser.
#
# Safe to re-run: every step checks for its own result first.
# ─────────────────────────────────────────────────────────────────────────────

set -euo pipefail

DOMAIN="oordarat.com"
SITE="wildcard.${DOMAIN}"
CERT_DIR="/etc/ssl/ordrat/wildcard.${DOMAIN}"
CONF_SRC="$(dirname "$(readlink -f "$0")")/../nginx/${SITE}.conf"

log()  { echo "[$(date '+%H:%M:%S')] $*"; }
die()  { echo "[$(date '+%H:%M:%S')] ERROR: $*" >&2; exit 1; }

[[ $EUID -eq 0 ]] || die "run as root"
[[ -f "$CONF_SRC" ]] || die "nginx config not found at $CONF_SRC"

# ── 1. DNS ───────────────────────────────────────────────────────────────────
log "1/5 checking the wildcard record"
RESOLVED="$(dig +short "definitely-not-a-tenant.${DOMAIN}" A | tail -1)"
[[ -n "$RESOLVED" ]] || die "*.${DOMAIN} does not resolve yet - add the A record first"
log "      *.${DOMAIN} -> ${RESOLVED}"

# ── 2. acme.sh ───────────────────────────────────────────────────────────────
log "2/5 acme.sh"
if [[ -x ~/.acme.sh/acme.sh ]]; then
    log "      already installed"
else
    curl -fsS https://get.acme.sh | sh -s email=admin@"${DOMAIN}"
fi
ACME=~/.acme.sh/acme.sh

# ── 3. Certificate ───────────────────────────────────────────────────────────
# DNS-01, because a wildcard cannot be proved over HTTP. Hostinger_Key is read
# from the environment and saved by acme.sh for unattended renewal, so
# ~/.acme.sh/account.conf becomes a secret from here on.
log "3/5 wildcard certificate"
if [[ -s "${CERT_DIR}/fullchain.pem" ]] \
   && openssl x509 -in "${CERT_DIR}/fullchain.pem" -noout -checkend 2592000 >/dev/null 2>&1 \
   && openssl x509 -in "${CERT_DIR}/fullchain.pem" -noout -text | grep -q "DNS:\*\.${DOMAIN}"; then
    log "      a valid wildcard certificate is already installed - skipping"
else
    # The variable the plugin actually reads is HOSTINGER_Token. The acme.sh wiki
    # documents it as Hostinger_Key, which is silently ignored: the run gets all
    # the way to writing the TXT record before failing.
    [[ -n "${HOSTINGER_Token:-}" ]] || die "export HOSTINGER_Token=<hPanel API token> first"
    export HOSTINGER_Token

    # Let's Encrypt explicitly. acme.sh defaults to ZeroSSL, which needs an EAB
    # account registration this deployment has no reason to depend on.
    ACME_CERT=~/.acme.sh/"${DOMAIN}"_ecc/fullchain.cer
    if [[ ! -s "$ACME_CERT" ]]; then
        "$ACME" --server letsencrypt \
                --issue --dns dns_hostinger -d "${DOMAIN}" -d "*.${DOMAIN}" || true
    fi
    # Judged on the certificate file, not on the exit code and not on the
    # directory: acme.sh creates the directory for the domain key before it ever
    # talks to the CA, so "the directory exists" says nothing about success.
    [[ -s "$ACME_CERT" ]] || die "issuance failed - no certificate at $ACME_CERT (see output above)"

    mkdir -p "$CERT_DIR"
    chmod 700 "$CERT_DIR"
    "$ACME" --install-cert -d "${DOMAIN}" \
        --key-file       "${CERT_DIR}/privkey.pem" \
        --fullchain-file "${CERT_DIR}/fullchain.pem" \
        --reloadcmd      "systemctl reload nginx"
fi

[[ -s "${CERT_DIR}/fullchain.pem" ]] || die "installed chain at ${CERT_DIR}/fullchain.pem is empty"

log "      subject alternative names:"
openssl x509 -in "${CERT_DIR}/fullchain.pem" -noout -text \
    | grep -A1 "Subject Alternative Name" | tail -1 | sed 's/^/        /'
openssl x509 -in "${CERT_DIR}/fullchain.pem" -noout -text | grep -q "DNS:\*\.${DOMAIN}" \
    || die "the certificate does not cover *.${DOMAIN}"

# ── 4. Nginx ─────────────────────────────────────────────────────────────────
log "4/5 nginx site"
install -m 0644 "$CONF_SRC" "/etc/nginx/sites-available/${SITE}"
ln -sfn "/etc/nginx/sites-available/${SITE}" "/etc/nginx/sites-enabled/${SITE}"

if ! nginx -t 2>/tmp/nginx-test.log; then
    # Leave nothing half-enabled: a broken config that never loads is better
    # than one that does, on a box serving other people's sites.
    rm -f "/etc/nginx/sites-enabled/${SITE}"
    cat /tmp/nginx-test.log >&2
    die "nginx config test failed - the new site has been removed, nothing was reloaded"
fi
systemctl reload nginx
log "      reloaded"

# ── 5. Prove it ──────────────────────────────────────────────────────────────
log "5/5 checking a tenant subdomain over TLS"
# Over the public name, not 127.0.0.1: on loopback the handshake lands on the
# default vhost whatever SNI says, which makes this report the wrong
# certificate and cry wolf on a rollout that actually worked.
SUBJECT="$(echo | openssl s_client -connect "${DOMAIN}:443" -servername "test-mohamed.${DOMAIN}" 2>/dev/null \
           | openssl x509 -noout -subject 2>/dev/null || true)"
log "      certificate served: ${SUBJECT:-none}"

CODE="$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "https://test-mohamed.${DOMAIN}/" || true)"
log "      https://test-mohamed.${DOMAIN}/ -> HTTP ${CODE}"

echo
log "Done. Menus are reachable at https://<slug>.${DOMAIN} once you set, in"
log "/opt/ordrat/backend/.env:   MENU_DOMAIN=${DOMAIN}"
log "and restart the backend. Old /m/<slug> links keep working either way."
