#!/usr/bin/env bash

# ACME-DNS Docker Setup Script

set -euo pipefail
IFS=$'\n\t'

# ======================
# CONFIGURATION (EDIT THESE)
# ======================
SETUP_DIR="/var/tmp/acme-dns-setup"
MAIN_DOMAIN="example.com" # Base domain for certificates (e.g., yoursite.com)
ACME_DNS_SUBDOMAIN="auth" # Subdomain for acme-dns (creates auth.yoursite.com)
ACME_DNS_FQDN="${ACME_DNS_SUBDOMAIN}.${MAIN_DOMAIN}"
PUBLIC_IP="1.2.3.4"                          # Public IP of THIS server
ADMIN_EMAIL="admin@${MAIN_DOMAIN}"           # For SOA record (converted to DNS format: admin.yoursite.com)
LE_NOTIFICATION_EMAIL="admin@${MAIN_DOMAIN}" # Let's Encrypt notifications (if using LE modes)

# API Configuration
API_PORT="443"                                                                # Host port for API (443 recommended for TLS modes)
API_TLS_MODE="letsencryptstaging"                                             # Options: none, letsencrypt, letsencryptstaging, cert
API_CORS_ORIGINS="[\"https://*.${MAIN_DOMAIN}\", \"https://${MAIN_DOMAIN}\"]" # SECURE DEFAULT
CUSTOM_TLS_CERT_PRIVKEY="/etc/acme-dns/certs/privkey.pem"
CUSTOM_TLS_CERT_FULLCHAIN="/etc/acme-dns/certs/fullchain.pem"

# DNS & Logging
DNS_PORT="53"    # MUST be 53 for public DNS delegation
LOG_LEVEL="info" # error, warning, info, debug

# ======================
# SAFETY VALIDATIONS
# ======================
if ! command -v docker &>/dev/null; then
  echo "❌ ERROR: Docker not found. Install Docker first." >&2
  exit 1
fi

# Critical placeholder check
if [[ "${MAIN_DOMAIN}" == "example.com" || "${PUBLIC_IP}" == "1.2.3.4" ]]; then
  echo "⚠️  WARNING: Using default placeholder values!" >&2
  echo "   Edit CONFIGURATION section before proceeding." >&2
  read -p "Continue anyway? (y/N): " -r || exit 1
  [[ ! "$REPLY" =~ ^[Yy]$ ]] && exit 1
fi

# Port conflict detection (HOST level)
for port in "${DNS_PORT}" "${API_PORT}"; do
  if ss -tuln 2>/dev/null | grep -qE ":${port}[^0-9]"; then
    echo "⚠️  WARNING: Host port ${port} is already in use!" >&2
    echo "   acme-dns may fail to start. Resolve conflicts first." >&2
    read -p "Continue? (y/N): " -r || exit 1
    [[ ! "$REPLY" =~ ^[Yy]$ ]] && exit 1
  fi
done

# ======================
# HELPER FUNCTIONS
# ======================
cleanup() {
  [[ -d "${SETUP_DIR}" ]] && rm -rf "${SETUP_DIR}" && echo "🧹 Cleaned existing setup directory"
}

generate_config() {
  # Convert email to DNS SOA format (admin@domain.com → admin.domain.com)
  local nsadmin="${ADMIN_EMAIL//@/.}"

  cat <<EOF
[general]
listen = "0.0.0.0:${DNS_PORT}"
protocol = "both"
domain = "${ACME_DNS_FQDN}"
nsname = "${ACME_DNS_FQDN}"
nsadmin = "${nsadmin}"
records = [
    "${ACME_DNS_FQDN}. A ${PUBLIC_IP}",
    "${ACME_DNS_FQDN}. NS ${ACME_DNS_FQDN}."
]
debug = false

[database]
engine = "sqlite3"
connection = "/var/lib/acme-dns/acme-dns.db"

[api]
ip = "0.0.0.0"
port = "${API_PORT}"
disable_registration = false
tls = "${API_TLS_MODE}"
EOF

  # Conditional TLS configuration
  case "${API_TLS_MODE}" in
  letsencrypt | letsencryptstaging)
    cat <<EOF
notification_email = "${LE_NOTIFICATION_EMAIL}"
EOF
    ;;
  cert)
    cat <<EOF
tls_cert_privkey = "${CUSTOM_TLS_CERT_PRIVKEY}"
tls_cert_fullchain = "${CUSTOM_TLS_CERT_FULLCHAIN}"
EOF
    ;;
  esac

  cat <<EOF
corsorigins = ${API_CORS_ORIGINS}
use_header = false
header_name = "X-Forwarded-For"

[logconfig]
loglevel = "${LOG_LEVEL}"
logtype = "stdout"
logformat = "text"
EOF
}

generate_docker_compose() {
  cat <<EOF
version: '3.8'
services:
  acme-dns:
    image: joohoi/acme-dns:latest
    container_name: acme-dns
    restart: unless-stopped
    ports:
      - "${DNS_PORT}:${DNS_PORT}/udp"
      - "${DNS_PORT}:${DNS_PORT}/tcp"
      - "${API_PORT}:${API_PORT}/tcp"
    volumes:
      - ./config:/etc/acme-dns:ro
      - ./data:/var/lib/acme-dns
    # SECURITY HARDENING (critical for public-facing DNS)
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
    # Prevent container from accessing host network
    network_mode: "default"
EOF
}

# ======================
# EXECUTION FLOW
# ======================
echo "🔧 ACME-DNS Setup Generator (Unified Edition)"
echo "   Setup Directory  : ${SETUP_DIR}"
echo "   ACME-DNS Domain  : ${ACME_DNS_FQDN}"
echo "   Public IP        : ${PUBLIC_IP}"
echo "   Admin Contact    : ${ADMIN_EMAIL} (SOA record)"
echo "   API Mode         : ${API_TLS_MODE} @ port ${API_PORT}"
echo "   CORS Policy      : ${API_CORS_ORIGINS}"
echo

# Interactive confirmation with context
read -p "⚠️  This will create/overwrite ${SETUP_DIR}. Confirm configuration above. Proceed? (y/N): " -r || exit 1
[[ ! "$REPLY" =~ ^[Yy]$ ]] && echo "Aborted by user" && exit 0

cleanup
mkdir -p "${SETUP_DIR}"/{config,data}
docker pull joohoi/acme-dns:latest >/dev/null 2>&1

generate_config >"${SETUP_DIR}/config/config.cfg"
generate_docker_compose >"${SETUP_DIR}/docker-compose.yml"

# ======================
# ACTIONABLE NEXT STEPS (CRITICAL)
# ======================
cat <<EOF

✅ Configuration generated successfully in ${SETUP_DIR}

❗❗ CRITICAL NEXT STEPS (DO NOT SKIP) ❗❗

1. 🌐 CONFIGURE DNS AT REGISTRAR (for ${MAIN_DOMAIN}):
   | Type | Host          | Value                              | TTL  |
   |------|---------------|------------------------------------|------|
   | A    | ${ACME_DNS_SUBDOMAIN} | ${PUBLIC_IP}                       | Auto |
   | NS   | ${ACME_DNS_SUBDOMAIN} | ${ACME_DNS_FQDN}. | Auto |
   
   🔑 MUST-HAVES:
   - NS record VALUE MUST END WITH TRAILING DOT (.)
   - Verify propagation: dig NS ${ACME_DNS_FQDN}
   - Wait 5-60 mins for global propagation

2. 🔒 SECURITY REVIEW (BEFORE STARTING):
   - [ ] Edit config/config.cfg:
        • Restrict corsorigins to YOUR actual client origins (current: ${MAIN_DOMAIN} subdomains)
        • For production: Change API_TLS_MODE to "letsencrypt" (staging avoids rate limits during testing)
        • After initial registrations: Set disable_registration=true
   - [ ] Verify docker-compose.yml security settings (capabilities restricted)
   - [ ] If using "cert" mode: Mount certificate volume and validate paths

3. 🚀 DEPLOY:
   cd "${SETUP_DIR}"
   docker compose up -d          # Modern Docker (space)
   # OR
   docker-compose up -d          # Legacy CLI (hyphen)

4. ✅ VERIFY:
   docker logs -f acme-dns       # Confirm "Serving DNS" and "API listening on :${API_PORT}"
   curl -k https://localhost:${API_PORT}/health  # Ignore cert warning for staging

💡 TROUBLESHOOTING:
- Port 53 conflict? → sudo systemctl stop systemd-resolved; sudo systemctl disable systemd-resolved
- DNS not resolving? → Check NS record has trailing dot; use https://dnschecker.org
- API unreachable? → sudo ufw allow ${API_PORT}/tcp; check reverse proxy config
- Staging certs? → Remember to switch to "letsencrypt" mode before production issuance

📌 PRO TIPS:
• Use "letsencryptstaging" for all testing (avoids Let's Encrypt rate limits)
• Firewall MUST allow UDP/TCP 53 and TCP ${API_PORT}
• Backup ./data/acme-dns.db regularly (contains registration tokens)
• This server is a certificate authority component - secure like vault!
EOF
