#!/bin/bash
################################################################################
# Script Name: setup-web-server.sh
# Version: 1.1
# Description: Production setup for a dedicated nginx web server hosting a
#              Hugo static site with GeoIP2 enriched logging. Subcommand-
#              based: base hardening, nginx install, Hugo,
#              TLS, security toolkit, and status dashboard.
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Subcommands:
#   base     — Server hardening (firewall, fail2ban, sysctl, unattended-upgrades)
#   nginx    — Install nginx from official repo + production config
#   hugo     — Install Hugo extended + site directory + deploy script
#   tls      — Let's Encrypt certificates
#   security — Download and run nginx-security.sh toolkit
#   status   — Dashboard showing all component status
#   all      — Run base → nginx → hugo → tls → security
#
# Usage:
#   sudo ./setup-web-server.sh base [OPTIONS]
#   sudo ./setup-web-server.sh nginx --domain mylinux.work
#   sudo ./setup-web-server.sh hugo --domain mylinux.work
#   sudo ./setup-web-server.sh tls --domain mylinux.work --email admin@example.com
#   sudo ./setup-web-server.sh security
#   sudo ./setup-web-server.sh status
#   sudo ./setup-web-server.sh all --domain mylinux.work
#
################################################################################

set -euo pipefail

# =============================================================================
# SHARED HELPERS
# =============================================================================

# --- Colors (TTY-aware) ---
if [[ -t 1 ]]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[0;33m'
    CYAN='\033[0;36m'
    BOLD='\033[1m'
    NC='\033[0m'
else
    RED="" GREEN="" YELLOW="" CYAN="" BOLD="" NC=""
fi

# --- Logging (prefixed with # for Prometheus comment compatibility) ---
info()  { echo -e "# ${GREEN}[OK]${NC} $*"; }
warn()  { echo -e "# ${YELLOW}[WARN]${NC} $*"; }
die()   { echo -e "# ${RED}[FATAL]${NC} $*" >&2; exit 1; }
step()  { echo -e "# ${CYAN}[STEP]${NC} $*"; }
banner() {
    echo
    echo -e "# ${BOLD}════════════════════════════════════════${NC}"
    echo -e "# ${BOLD}  $*${NC}"
    echo -e "# ${BOLD}════════════════════════════════════════${NC}"
    echo
}

# --- Root check ---
require_root() {
    [[ $EUID -eq 0 ]] || die "This script must be run as root (or via sudo)"
}

# --- OS detection ---
OS="" OSVER="" PKG_MGR=""
detect_os() {
    if [[ -f /etc/os-release ]]; then
        OS=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"' | tr '[:upper:]' '[:lower:]')
        OSVER=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"' | cut -d. -f1)
    fi
    case "$OS" in
        ubuntu|debian)
            PKG_MGR="apt -y"
            ;;
        rocky|almalinux|centos|rhel|ol)
            PKG_MGR=$(command -v dnf >/dev/null 2>&1 && echo "dnf -y" || echo "yum -y")
            ;;
        *)
            die "Unsupported OS: ${OS:-unknown}. Requires Ubuntu/Debian or RHEL/Rocky."
            ;;
    esac
    info "Detected ${OS} ${OSVER}, using ${PKG_MGR}"
}

# --- Architecture ---
detect_arch() {
    local arch
    arch=$(uname -m)
    case "$arch" in
        x86_64)  echo "amd64" ;;
        aarch64) echo "arm64" ;;
        *)       die "Unsupported architecture: $arch" ;;
    esac
}

# --- Backup file with timestamp ---
backup_file() {
    local file="$1"
    if [[ -f "$file" ]]; then
        local ts
        ts=$(date +%Y%m%d-%H%M%S)
        cp "$file" "${file}.bak-${ts}"
        info "Backed up ${file}"
    fi
}

# --- nginx test and reload ---
nginx_test_and_reload() {
    if nginx -t 2>&1; then
        if systemctl is-active --quiet nginx 2>/dev/null; then
            systemctl reload nginx
            info "nginx config valid — reloaded"
        else
            systemctl start nginx
            info "nginx config valid — started"
        fi
    else
        die "nginx config test failed — not reloading"
    fi
}

# --- Generate password ---
generate_password() {
    openssl rand -base64 24 | tr -d '/+=' | head -c 24
}

# =============================================================================
# DEFAULTS
# =============================================================================

DOMAIN="mylinux.work"
HOSTNAME_SET="web01"
TIMEZONE="America/Chicago"
ADMIN_EMAIL="contact@mylinux.work"
ADMIN_NAME="chiefgeek"
ADMIN_PASS=""
DB_PASS=""
CERTBOT_EMAIL="contact@mylinux.work"
HUGO_VERSION="latest"
DRY_RUN=false
SKIP_HUGO=false
SKIP_TLS=false

# =============================================================================
# ARGUMENT PARSING
# =============================================================================

SUBCOMMAND=""

parse_args() {
    if [[ $# -lt 1 ]]; then
        show_help
        exit 0
    fi

    # Handle help before subcommand assignment
    case "$1" in
        --help|-h|help) show_help; exit 0 ;;
        --*) die "Missing subcommand. Usage: $0 <subcommand> [OPTIONS]\n#        Run '$0 --help' for available subcommands." ;;
    esac

    SUBCOMMAND="$1"
    shift

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --domain)       DOMAIN="$2"; shift 2 ;;
            --hostname)     HOSTNAME_SET="$2"; shift 2 ;;
            --timezone)     TIMEZONE="$2"; shift 2 ;;
            --admin-email)  ADMIN_EMAIL="$2"; shift 2 ;;
            --admin-name)   ADMIN_NAME="$2"; shift 2 ;;
            --admin-pass)   ADMIN_PASS="$2"; shift 2 ;;
            --db-pass)      DB_PASS="$2"; shift 2 ;;
            --email)        CERTBOT_EMAIL="$2"; shift 2 ;;
            --hugo-version) HUGO_VERSION="$2"; shift 2 ;;
            --skip-hugo)    SKIP_HUGO=true; shift ;;
            --skip-tls)     SKIP_TLS=true; shift ;;
            --dry-run)      DRY_RUN=true; shift ;;
            --help|-h)      show_help; exit 0 ;;
            *)              die "Unknown option: $1" ;;
        esac
    done
}

show_help() {
    cat <<'EOF'
setup-web-server.sh — Production web server setup

USAGE:
    sudo ./setup-web-server.sh SUBCOMMAND [OPTIONS]

SUBCOMMANDS:
    base       Server hardening (firewall, fail2ban, sysctl, updates)
    nginx      Install nginx from official repo + production config
    hugo       Install Hugo extended + site directory + deploy script
    tls        Let's Encrypt certificates
    security   Download and run nginx-security.sh toolkit
    status     Show all component status
    all        Run everything: base → nginx → hugo → tls → security

OPTIONS:
    --domain DOMAIN         Primary domain (default: mylinux.work)
    --hostname NAME         Server hostname (default: web01)
    --timezone ZONE         Timezone (default: America/Chicago)
    --admin-email EMAIL     Admin email (default: contact@mylinux.work)
    --admin-name NAME       Admin username (default: chiefgeek)
    --admin-pass PASS       Admin password (generated if not set)
    --db-pass PASS          MySQL password (generated if not set)
    --email EMAIL           Certbot email (default: contact@mylinux.work)
    --hugo-version VERSION  Hugo version (default: latest)
    --skip-hugo             Skip Hugo install in 'all' (for rsync-based deploys)
    --skip-tls              Skip TLS setup in 'all' (DNS not ready yet)
    --dry-run               Show what would be done
    --help                  Show this help

EXAMPLES:
    sudo ./setup-web-server.sh base --hostname web01 --timezone America/Chicago
    sudo ./setup-web-server.sh nginx --domain mylinux.work
    sudo ./setup-web-server.sh all --domain mylinux.work
    sudo ./setup-web-server.sh status
EOF
}

# =============================================================================
# SUBCOMMAND: base
# =============================================================================

cmd_base() {
    banner "Base Server Setup"

    # --- Hostname ---
    step "Setting hostname to ${HOSTNAME_SET}"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would set hostname to ${HOSTNAME_SET}"
    else
        hostnamectl set-hostname "$HOSTNAME_SET"
        info "Hostname set to ${HOSTNAME_SET}"
    fi

    # --- Timezone ---
    step "Setting timezone to ${TIMEZONE}"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would set timezone to ${TIMEZONE}"
    else
        timedatectl set-timezone "$TIMEZONE"
        info "Timezone set to ${TIMEZONE}"
    fi

    # --- Update packages ---
    step "Updating packages"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would update packages"
    else
        case "$OS" in
            ubuntu|debian)
                apt update && apt upgrade -y
                ;;
            *)
                $PKG_MGR update
                ;;
        esac
        info "Packages updated"
    fi

    # --- Base tools ---
    step "Installing base tools"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would install base packages"
    else
        case "$OS" in
            ubuntu|debian)
                apt install -y curl wget git htop tmux unzip jq tree ncdu \
                    fail2ban ufw net-tools dnsutils software-properties-common \
                    gnupg2 ca-certificates lsb-release openssl
                ;;
            *)
                $PKG_MGR install curl wget git htop tmux unzip jq tree ncdu \
                    fail2ban firewalld net-tools bind-utils openssl
                ;;
        esac
        info "Base tools installed"
    fi

    # --- Firewall ---
    step "Configuring firewall"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would configure firewall (allow 22, 80, 443)"
    else
        case "$OS" in
            ubuntu|debian)
                ufw default deny incoming 2>/dev/null || true
                ufw default allow outgoing 2>/dev/null || true
                ufw allow 22/tcp comment 'SSH' 2>/dev/null || true
                ufw allow 80/tcp comment 'HTTP' 2>/dev/null || true
                ufw allow 443/tcp comment 'HTTPS' 2>/dev/null || true
                ufw --force enable
                ;;
            *)
                systemctl enable --now firewalld
                firewall-cmd --permanent --add-service=ssh
                firewall-cmd --permanent --add-service=http
                firewall-cmd --permanent --add-service=https
                firewall-cmd --reload
                ;;
        esac
        info "Firewall configured"
    fi

    # --- fail2ban ---
    step "Configuring fail2ban"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would configure fail2ban for SSH"
    else
        local logpath="/var/log/auth.log"
        [[ "$OS" =~ ^(rocky|almalinux|centos|rhel|ol)$ ]] && logpath="/var/log/secure"

        cat > /etc/fail2ban/jail.local <<JAILEOF
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
logpath = ${logpath}
JAILEOF

        systemctl enable --now fail2ban
        info "fail2ban configured for SSH"
    fi

    # --- Unattended upgrades ---
    step "Configuring unattended upgrades"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would enable unattended upgrades"
    else
        case "$OS" in
            ubuntu|debian)
                apt install -y unattended-upgrades
                dpkg-reconfigure -plow unattended-upgrades 2>/dev/null || true
                ;;
            *)
                $PKG_MGR install dnf-automatic
                sed -i 's/apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf 2>/dev/null || true
                systemctl enable --now dnf-automatic.timer
                ;;
        esac
        info "Unattended upgrades enabled"
    fi

    # --- Sysctl tuning ---
    step "Applying sysctl tuning for web serving"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would write /etc/sysctl.d/99-web-tuning.conf"
    else
        cat > /etc/sysctl.d/99-web-tuning.conf <<'SYSEOF'
net.core.somaxconn = 65535
net.ipv4.tcp_fastopen = 3
fs.file-max = 2097152
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65535
SYSEOF
        sysctl --system >/dev/null 2>&1
        info "Sysctl tuning applied"
    fi

    # --- File limits ---
    step "Setting file descriptor limits"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would set file limits"
    else
        if ! grep -q "# web-server-setup" /etc/security/limits.conf 2>/dev/null; then
            cat >> /etc/security/limits.conf <<'LIMEOF'

# web-server-setup
* soft nofile 65536
* hard nofile 65536
root soft nofile 65536
root hard nofile 65536
LIMEOF
        fi
        info "File limits configured"
    fi

    info "Base server setup complete"
}

# =============================================================================
# SUBCOMMAND: nginx
# =============================================================================

cmd_nginx() {
    banner "Nginx Installation"

    # --- Install nginx from OS package manager ---
    step "Installing nginx"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would install nginx"
    else
        $PKG_MGR install nginx
        info "nginx installed: $(nginx -v 2>&1)"
    fi

    # --- Production nginx.conf ---
    step "Writing production nginx.conf"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would write /etc/nginx/nginx.conf"
    else
        backup_file /etc/nginx/nginx.conf

        local cores
        cores=$(nproc)
        local connections=$((cores * 4096))
        [[ $connections -gt 65536 ]] && connections=65536

        cat > /etc/nginx/nginx.conf <<NGXEOF
user www-data;
worker_processes auto;
worker_rlimit_nofile 65536;
pid /run/nginx.pid;

include /etc/nginx/modules-enabled/*.conf;

error_log /var/log/nginx/error.log warn;

events {
    worker_connections ${connections};
    multi_accept on;
    use epoll;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format main '\$remote_addr - \$remote_user [\$time_local] '
                    '"\$request" \$status \$body_bytes_sent '
                    '"\$http_referer" "\$http_user_agent" '
                    'ssl=\$ssl_protocol rt=\$request_time '
                    '\$geoip2_country_code \"\$geoip2_asn_org\"';
    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;

    client_body_timeout 30;
    client_header_timeout 30;
    send_timeout 30;
    client_body_buffer_size 16k;
    client_max_body_size 50m;

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_types text/plain text/css text/javascript
               application/javascript application/json
               application/xml application/rss+xml
               image/svg+xml;

    server_tokens off;

    # Drop requests with no matching server_name
    server {
        listen 80 default_server;
        server_name _;
        return 444;
    }

    include /etc/nginx/conf.d/*.conf;
}
NGXEOF

        # Ensure www-data user exists
        id www-data &>/dev/null || useradd -r -s /usr/sbin/nologin www-data

        info "nginx.conf written (${cores} cores, ${connections} connections)"
    fi

    # --- Site directories ---
    step "Creating nginx config structure"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would create conf.d directory and site configs"
    else
        mkdir -p /etc/nginx/conf.d
        # Remove default configs that conflict
        rm -f /etc/nginx/conf.d/default.conf
        rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
    fi

    # --- Hugo site server block ---
    step "Creating nginx config for ${DOMAIN}"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would create /etc/nginx/conf.d/${DOMAIN}.conf"
    else
        mkdir -p "/var/www/${DOMAIN}/public"

        cat > "/etc/nginx/conf.d/${DOMAIN}.conf" <<SITEEOF
server {
    listen 80;
    server_name ${DOMAIN} www.${DOMAIN};

    root /var/www/${DOMAIN}/public;
    index index.html;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location / {
        try_files \$uri \$uri/ =404;
    }

    location ~ /\. {
        deny all;
    }

    error_page 404 /404.html;
}
SITEEOF
        info "Created ${DOMAIN} server block"
    fi

    # --- Start nginx ---
    if [[ "$DRY_RUN" == "false" ]]; then
        systemctl enable nginx
        nginx_test_and_reload
        info "nginx installed and running"
    fi
}

# =============================================================================
# SUBCOMMAND: hugo
# =============================================================================

cmd_hugo() {
    banner "Hugo Installation"

    local arch
    arch=$(detect_arch)

    # --- Resolve version ---
    local version="$HUGO_VERSION"
    if [[ "$version" == "latest" ]]; then
        step "Resolving latest Hugo version"
        version=$(curl -fsSL https://api.github.com/repos/gohugoio/hugo/releases/latest | \
            grep -oP '"tag_name":\s*"v\K[^"]+' | head -1)
        [[ -z "$version" ]] && die "Could not resolve latest Hugo version"
        info "Latest Hugo version: ${version}"
    fi

    # --- Install Hugo ---
    step "Installing Hugo extended ${version}"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would install Hugo extended ${version}"
    else
        if command -v hugo &>/dev/null; then
            local current
            current=$(hugo version 2>/dev/null | grep -oP 'v\K[0-9.]+' | head -1)
            if [[ "$current" == "$version" ]]; then
                info "Hugo ${version} already installed"
            else
                info "Upgrading Hugo from ${current} to ${version}"
            fi
        fi

        local url="https://github.com/gohugoio/hugo/releases/download/v${version}/hugo_extended_${version}_linux-${arch}.tar.gz"
        local tmp="/tmp/hugo-install-$$"
        mkdir -p "$tmp"
        wget -qO "${tmp}/hugo.tar.gz" "$url" || die "Failed to download Hugo ${version}"
        tar -xzf "${tmp}/hugo.tar.gz" -C "$tmp"
        mv "${tmp}/hugo" /usr/local/bin/hugo
        chmod +x /usr/local/bin/hugo
        rm -rf "$tmp"
        info "Hugo installed: $(hugo version 2>&1 | head -1)"
    fi

    # --- Site directory ---
    step "Creating site directory /var/www/${DOMAIN}"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would create /var/www/${DOMAIN} and deploy user"
    else
        mkdir -p "/var/www/${DOMAIN}/public"
        mkdir -p "/var/www/${DOMAIN}/source"

        # Create deploy user
        if ! id www-deploy &>/dev/null; then
            useradd -r -s /bin/bash -m -d /home/www-deploy www-deploy
            info "Created www-deploy user"
        fi

        # Ensure www-data group exists
        getent group www-data &>/dev/null || groupadd -r www-data

        usermod -aG www-data www-deploy 2>/dev/null || true
        chown -R www-deploy:www-data "/var/www/${DOMAIN}"
        chmod -R 775 "/var/www/${DOMAIN}"
        info "Site directory ready"
    fi

    # --- Deploy script ---
    step "Creating deploy script"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would create /usr/local/bin/deploy-site.sh"
    else
        cat > /usr/local/bin/deploy-site.sh <<DEPLOYEOF
#!/usr/bin/env bash
set -euo pipefail

SITE_DIR="/var/www/${DOMAIN}"
BRANCH="\${1:-main}"

if [[ ! -d "\$SITE_DIR/source/.git" ]]; then
    echo "# No git repo in \$SITE_DIR/source — clone your repo first:"
    echo "#   git clone --depth 1 YOUR_REPO_URL \$SITE_DIR/source"
    exit 1
fi

cd "\$SITE_DIR/source"
git fetch origin "\$BRANCH"
git reset --hard "origin/\$BRANCH"
hugo --minify --destination "\$SITE_DIR/public"
echo "# Deploy complete: \$(date -u +%Y-%m-%dT%H:%M:%SZ)"
DEPLOYEOF
        chmod +x /usr/local/bin/deploy-site.sh
        info "Deploy script created at /usr/local/bin/deploy-site.sh"
    fi

    info "Hugo setup complete"
}

# =============================================================================
# SUBCOMMAND: tls
# =============================================================================

cmd_tls() {
    banner "TLS Certificate Setup"

    step "Installing certbot"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would install certbot and obtain certificates"
    else
        case "$OS" in
            ubuntu|debian)
                apt install -y certbot python3-certbot-nginx
                ;;
            *)
                $PKG_MGR install certbot python3-certbot-nginx
                ;;
        esac

        step "Obtaining certificate for ${DOMAIN}"
        certbot --nginx -d "${DOMAIN}" -d "www.${DOMAIN}" \
            --non-interactive --agree-tos --email "${CERTBOT_EMAIL}" \
            --redirect 2>&1 || warn "certbot for ${DOMAIN} returned non-zero (may already exist)"

        step "Verifying auto-renewal"
        certbot renew --dry-run 2>&1 || warn "Renewal dry-run had issues"

        info "TLS certificates configured"
    fi
}

# =============================================================================
# SUBCOMMAND: security
# =============================================================================

cmd_security() {
    banner "Security Toolkit"

    local script_path="/usr/local/bin/nginx-security.sh"

    step "Checking for nginx-security.sh"
    if [[ "$DRY_RUN" == "true" ]]; then
        info "[DRY RUN] Would download and run nginx-security.sh"
        return
    fi

    if [[ ! -f "$script_path" ]]; then
        step "Downloading nginx-security.sh"
        wget -qO "$script_path" "https://mylinux.work/downloads/nginx-security.sh" || \
            die "Failed to download nginx-security.sh"
        chmod +x "$script_path"
        info "Downloaded nginx-security.sh"
    else
        info "nginx-security.sh already present"
    fi

    step "Running bot-block"
    "$script_path" bot-block || warn "bot-block returned non-zero"

    step "Running js-challenge"
    "$script_path" js-challenge --db-ip || warn "js-challenge returned non-zero"

    step "Running crowdsec"
    "$script_path" crowdsec || warn "crowdsec returned non-zero"

    echo
    info "Security toolkit applied"
    info "Optional: ${script_path} block-head"
    info "Status:   ${script_path} status"
}

# =============================================================================
# SUBCOMMAND: status
# =============================================================================

cmd_status() {
    banner "Server Status Dashboard"

    echo -e "# ${BOLD}System${NC}"
    echo -e "#   Hostname:  $(hostname)"
    echo -e "#   OS:        $(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2)"
    echo -e "#   Kernel:    $(uname -r)"
    echo -e "#   Uptime:    $(uptime -p 2>/dev/null || uptime)"
    echo -e "#   CPU:       $(nproc) cores"
    echo -e "#   RAM:       $(free -h | awk '/Mem:/{print $2}') total, $(free -h | awk '/Mem:/{print $3}') used"
    echo -e "#   Disk:      $(df -h / | awk 'NR==2{print $3"/"$2" ("$5" used)"}')"
    echo

    echo -e "# ${BOLD}Nginx${NC}"
    if systemctl is-active --quiet nginx 2>/dev/null; then
        echo -e "#   Status:    ${GREEN}running${NC}"
        echo -e "#   Version:   $(nginx -v 2>&1 | cut -d/ -f2)"
        if nginx -t 2>/dev/null; then
            echo -e "#   Config:    ${GREEN}valid${NC}"
        else
            echo -e "#   Config:    ${RED}invalid${NC}"
        fi
    else
        echo -e "#   Status:    ${RED}stopped${NC}"
    fi
    echo

    echo -e "# ${BOLD}Hugo Site${NC}"
    if [[ -d "/var/www/${DOMAIN}/public" ]]; then
        local file_count
        file_count=$(find "/var/www/${DOMAIN}/public" -type f 2>/dev/null | wc -l)
        echo -e "#   Directory: /var/www/${DOMAIN}/public"
        echo -e "#   Files:     ${file_count}"
        if [[ -d "/var/www/${DOMAIN}/source/.git" ]]; then
            local last_commit
            last_commit=$(git -C "/var/www/${DOMAIN}/source" log -1 --format='%ci' 2>/dev/null || echo "unknown")
            echo -e "#   Last git:  ${last_commit}"
        fi
    else
        echo -e "#   Status:    ${YELLOW}not deployed${NC}"
    fi
    echo

    echo -e "# ${BOLD}TLS Certificates${NC}"
    for d in $DOMAIN; do
        local cert="/etc/letsencrypt/live/${d}/fullchain.pem"
        if [[ -f "$cert" ]]; then
            local expiry
            expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2)
            echo -e "#   ${d}: expires ${expiry}"
        else
            echo -e "#   ${d}: ${YELLOW}no certificate${NC}"
        fi
    done
    echo

    echo -e "# ${BOLD}Firewall${NC}"
    if command -v ufw &>/dev/null; then
        local ufw_status
        ufw_status=$(ufw status 2>/dev/null | head -1)
        echo -e "#   UFW:       ${ufw_status}"
    elif command -v firewall-cmd &>/dev/null; then
        echo -e "#   firewalld: $(firewall-cmd --state 2>/dev/null)"
    else
        echo -e "#   Status:    ${YELLOW}no firewall detected${NC}"
    fi
    echo

    echo -e "# ${BOLD}Fail2ban${NC}"
    if systemctl is-active --quiet fail2ban 2>/dev/null; then
        echo -e "#   Status:    ${GREEN}running${NC}"
        local jails
        jails=$(fail2ban-client status 2>/dev/null | grep "Jail list" | cut -d: -f2 | xargs)
        echo -e "#   Jails:     ${jails:-none}"
    else
        echo -e "#   Status:    ${RED}stopped${NC}"
    fi
    echo

    echo -e "# ${BOLD}CrowdSec${NC}"
    if systemctl is-active --quiet crowdsec 2>/dev/null; then
        echo -e "#   Engine:    ${GREEN}running${NC}"
        local decisions
        decisions=$(cscli decisions list -o raw 2>/dev/null | tail -n +2 | wc -l)
        echo -e "#   Decisions: ${decisions}"
    else
        echo -e "#   Engine:    ${YELLOW}not installed${NC}"
    fi
}

# =============================================================================
# SUBCOMMAND: all
# =============================================================================

cmd_all() {
    banner "Full Server Setup"

    if [[ -z "$DOMAIN" ]]; then
        die "--domain is required for the 'all' subcommand"
    fi

    cmd_base
    cmd_nginx
    if [[ "$SKIP_HUGO" == "true" ]]; then
        info "Skipping Hugo install (--skip-hugo) — site directory still created by nginx subcommand"
    else
        cmd_hugo
    fi
    if [[ "$SKIP_TLS" == "true" ]]; then
        info "Skipping TLS setup (--skip-tls) — run 'tls' subcommand after DNS is pointed"
    else
        cmd_tls
    fi
    cmd_security

    banner "Setup Complete"
    echo
    echo -e "# ${BOLD}Summary${NC}"
    echo -e "#   Hugo site:      https://${DOMAIN}"
    echo
    echo -e "# ${BOLD}Next Steps${NC}"
    echo -e "#   1. Clone your Hugo site repo to /var/www/${DOMAIN}/source"
    echo -e "#   2. Run deploy-site.sh to build"
    echo -e "#   3. Lock down SSH (key-only, disable root, change port)"
    echo
}

# =============================================================================
# MAIN
# =============================================================================

main() {
    parse_args "$@"

    case "$SUBCOMMAND" in
        status)
            detect_os 2>/dev/null || true
            cmd_status
            ;;
        base|nginx|hugo|tls|security|all)
            require_root
            detect_os
            case "$SUBCOMMAND" in
                base)     cmd_base ;;
                nginx)    cmd_nginx ;;
                hugo)     cmd_hugo ;;
                tls)      cmd_tls ;;
                security) cmd_security ;;
                all)      cmd_all ;;
            esac
            ;;
        *)
            die "Unknown subcommand: ${SUBCOMMAND}. Run with --help for usage."
            ;;
    esac
}

main "$@"
