#!/usr/bin/env bash
# =============================================================================
#  site.sh — Site Provisioning Manager
#
#  Usage:
#    site.sh create --domain=<domain> --type=<type> [--php=<ver>] [--db-name=<n>] [--name=<n>]
#    site.sh update --domain=<domain>
#    site.sh delete --domain=<domain>
#    site.sh repair --domain=<domain>
#
#  Must be run as root (sudo).
#  All server-wide settings live in site.conf (never on the command line).
# =============================================================================

set -euo pipefail

# ─────────────────────────────────────────────────────────────────────────────
#  Paths
# ─────────────────────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/site.conf"
TYPES_DIR="${SCRIPT_DIR}/types"

# ─────────────────────────────────────────────────────────────────────────────
#  Load main configuration
# ─────────────────────────────────────────────────────────────────────────────
if [[ ! -f "$CONFIG_FILE" ]]; then
    echo "ERROR: Config file not found: $CONFIG_FILE" >&2
    echo "       Copy site.conf.example to site.conf and edit your values." >&2
    exit 1
fi
# shellcheck source=/dev/null
source "$CONFIG_FILE"

# ─────────────────────────────────────────────────────────────────────────────
#  Defaults (override in site.conf)
# ─────────────────────────────────────────────────────────────────────────────
LOG_FILE="${LOG_FILE:-/var/log/sitemanager.log}"
PHP_VERSION="${PHP_VERSION:-8.3}"
SITE_BASE_PATH="${SITE_BASE_PATH:-/var/www}"
APACHE_LOG_DIR="${APACHE_LOG_DIR:-/var/log/apache2}"
SECRETS_DIR="${SECRETS_DIR:-/etc/sites-secrets}"
CERTBOT_EMAIL="${CERTBOT_EMAIL:-webmaster@example.com}"

# ─────────────────────────────────────────────────────────────────────────────
#  Colors & helpers
# ─────────────────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'

_ts()   { date '+%Y-%m-%d %H:%M:%S'; }
log()   { echo "[$(_ts)] $*" >> "$LOG_FILE"; }
ok()    { echo -e "${GREEN}✔${NC} $*";  log "OK:   $*"; }
info()  { echo -e "${CYAN}ℹ${NC}  $*";  log "INFO: $*"; }
warn()  { echo -e "${YELLOW}⚠${NC}  $*"; log "WARN: $*"; }
err()   { echo -e "${RED}✖${NC}  $*" >&2; log "ERR:  $*"; }
step()  { echo -e "\n${BOLD}${BLUE}▶ $*${NC}"; log "STEP: $*"; }
die()   { err "$*"; exit 1; }

# ─────────────────────────────────────────────────────────────────────────────
#  Argument parsing
# ─────────────────────────────────────────────────────────────────────────────
ACTION="${1:-}"
shift || true

DOMAIN=""
APP_TYPE="salelink"
SITE_NAME=""
PHP_OVERRIDE=""
DB_NAME_OVERRIDE=""

for arg in "$@"; do
    case "$arg" in
        --domain=*)   DOMAIN="${arg#*=}"        ;;
        --type=*)     APP_TYPE="${arg#*=}"       ;;
        --name=*)     SITE_NAME="${arg#*=}"      ;;
        --php=*)      PHP_OVERRIDE="${arg#*=}"   ;;
        --db-name=*)  DB_NAME_OVERRIDE="${arg#*=}" ;;
        *) die "Unknown option: $arg" ;;
    esac
done

# ─────────────────────────────────────────────────────────────────────────────
#  Input validation
# ─────────────────────────────────────────────────────────────────────────────
validate_inputs() {
    [[ -z "$DOMAIN" ]] && die "--domain is required"
    [[ "$DOMAIN" =~ ^[a-zA-Z0-9._-]+$ ]] || die "Invalid domain: $DOMAIN"
    if [[ "$DOMAIN" =~ \.\. ]]; then die "Invalid domain: $DOMAIN"; fi
}

# ─────────────────────────────────────────────────────────────────────────────
#  Load type configuration
# ─────────────────────────────────────────────────────────────────────────────
load_app_type() {
    local type_file="${TYPES_DIR}/${APP_TYPE}.conf"
    [[ -f "$type_file" ]] || {
        local available
        available=$(ls "$TYPES_DIR"/*.conf 2>/dev/null \
            | xargs -n1 basename | sed 's/\.conf//' | tr '\n' ' ')
        die "Unknown app type '${APP_TYPE}'. Available: ${available}"
    }
    # shellcheck source=/dev/null
    source "$type_file"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Derive per-site variables (called after validate + load_app_type)
# ─────────────────────────────────────────────────────────────────────────────
derive_vars() {
    # PHP version: command-line override → site.conf default
    [[ -n "$PHP_OVERRIDE" ]] && PHP_VERSION="$PHP_OVERRIDE"

    # Slug: replace . and - with _
    local slug
    slug="$(echo "$DOMAIN" | tr '.-' '__')"

    USERNAME="${TYPE_USERNAME_PREFIX}${slug}"

    # Base path: TYPE_BASE_PATH (per type) → SITE_BASE_PATH (global fallback)
    local base="${TYPE_BASE_PATH:-${SITE_BASE_PATH}}"
    PROJECT_PATH="${base}/${DOMAIN}"

    if [[ "${TYPE_IS_LARAVEL:-false}" == "true" ]]; then
        PUBLIC_PATH="${PROJECT_PATH}/public"
    else
        PUBLIC_PATH="$PROJECT_PATH"
    fi

    SOCK_PATH="/run/php/php${PHP_VERSION}-fpm-${USERNAME}.sock"
    POOL_PATH="/etc/php/${PHP_VERSION}/fpm/pool.d/${USERNAME}.conf"
    VHOST_PATH="/etc/apache2/sites-available/${DOMAIN}.conf"
    SECRETS_FILE="${SECRETS_DIR}/${DOMAIN}.conf"

    # DB name: command-line override → username (consistent default)
    if [[ -n "$DB_NAME_OVERRIDE" ]]; then
        DB_NAME="$DB_NAME_OVERRIDE"
    else
        DB_NAME="$USERNAME"
    fi
    DB_USER="$USERNAME"

    [[ -z "$SITE_NAME" ]] && SITE_NAME="$DOMAIN"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Read secrets file (used by delete to get accurate stored values)
# ─────────────────────────────────────────────────────────────────────────────
load_secrets() {
    [[ ! -f "$SECRETS_FILE" ]] && return 0
    local key val
    while IFS='=' read -r key val; do
        [[ "$key" =~ ^# ]] && continue
        [[ -z "$key"     ]] && continue
        case "$key" in
            DB_NAME)      DB_NAME="$val"      ;;
            DB_USER)      DB_USER="$val"      ;;
            PHP_VERSION)  PHP_VERSION="$val"
                          POOL_PATH="/etc/php/${PHP_VERSION}/fpm/pool.d/${USERNAME}.conf"
                          SOCK_PATH="/run/php/php${PHP_VERSION}-fpm-${USERNAME}.sock"
                          ;;
            PROJECT_PATH) PROJECT_PATH="$val" ;;
        esac
    done < "$SECRETS_FILE"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Password generator
# ─────────────────────────────────────────────────────────────────────────────
gen_pass() {
    local len="${1:-24}"
    (set +o pipefail; tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "$len")
}

# ─────────────────────────────────────────────────────────────────────────────
#  MySQL: credentials via temp cnf — password NEVER on command line
# ─────────────────────────────────────────────────────────────────────────────
_mysql_cnf() {
    local cnf
    cnf=$(mktemp)
    chmod 600 "$cnf"
    printf '[client]\nuser=%s\npassword=%s\nhost=%s\nport=%s\n' \
        "$MYSQL_ROOT_USER" "$MYSQL_ROOT_PASSWORD" \
        "${MYSQL_HOST:-127.0.0.1}" "${MYSQL_PORT:-3306}" > "$cnf"
    echo "$cnf"
}

mysql_exec() {
    local cnf
    cnf=$(_mysql_cnf)
    # shellcheck disable=SC2064
    trap "rm -f '$cnf'" RETURN
    mysql --defaults-extra-file="$cnf" "$@"
}

db_create() {
    local db="$1" user="$2" pass="$3"
    mysql_exec <<-SQL
        CREATE DATABASE IF NOT EXISTS \`${db}\`
            CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

        CREATE USER IF NOT EXISTS '${user}'@'localhost'
            IDENTIFIED BY '${pass}';

        -- صلاحيات محدودة على هذه القاعدة فقط
        GRANT SELECT, INSERT, UPDATE, DELETE,
              CREATE, DROP, INDEX, ALTER, REFERENCES,
              CREATE TEMPORARY TABLES, LOCK TABLES
          ON \`${db}\`.*
          TO '${user}'@'localhost';

        FLUSH PRIVILEGES;
SQL
}

db_drop() {
    local db="$1" user="$2"
    mysql_exec <<-SQL
        DROP DATABASE IF EXISTS \`${db}\`;
        DROP USER  IF EXISTS '${user}'@'localhost';
        FLUSH PRIVILEGES;
SQL
}

# ─────────────────────────────────────────────────────────────────────────────
#  System user: isolated, no login, no home
# ─────────────────────────────────────────────────────────────────────────────
create_system_user() {
    # مجموعة خاصة بالموقع (نفس اسم المستخدم)
    if ! getent group "$USERNAME" &>/dev/null; then
        sudo groupadd --system "$USERNAME"
        ok "Group created: $USERNAME"
    else
        warn "Group already exists: $USERNAME"
    fi

    if id "$USERNAME" &>/dev/null; then
        die "System user '$USERNAME' already exists. Aborting."
    fi

    sudo useradd \
        --system \
        --no-create-home \
        --shell /usr/sbin/nologin \
        --gid "$USERNAME" \
        --comment "Web user for $DOMAIN" \
        "$USERNAME"
    ok "User created: $USERNAME (shell: nologin, no home)"
}

delete_system_user() {
    if id "$USERNAME" &>/dev/null; then
        sudo userdel "$USERNAME" 2>/dev/null || warn "userdel failed for $USERNAME"
        ok "User removed: $USERNAME"
    else
        warn "User not found: $USERNAME"
    fi

    if getent group "$USERNAME" &>/dev/null; then
        sudo groupdel "$USERNAME" 2>/dev/null || warn "groupdel failed for $USERNAME"
        ok "Group removed: $USERNAME"
    else
        warn "Group not found: $USERNAME"
    fi
}

# ─────────────────────────────────────────────────────────────────────────────
#  PHP-FPM pool with open_basedir isolation
# ─────────────────────────────────────────────────────────────────────────────
write_pool() {
    sudo mkdir -p /var/log/php

    sudo tee "$POOL_PATH" > /dev/null <<-EOF
		; ─────────────────────────────────────────────────
		;  PHP-FPM Pool: ${USERNAME}  [${DOMAIN}]
		;  Type: ${TYPE_LABEL}  |  PHP: ${PHP_VERSION}
		; ─────────────────────────────────────────────────
		[${USERNAME}]
		user  = ${USERNAME}
		group = ${USERNAME}

		listen       = ${SOCK_PATH}
		listen.owner = www-data
		listen.group = www-data
		listen.mode  = 0660

		pm                          = ondemand
		pm.max_children             = 20
		pm.process_idle_timeout     = 15s
		pm.max_requests             = 500

		pm.status_path              = /fpm-status-${USERNAME}
		slowlog                     = /var/log/php/${USERNAME}-slow.log
		request_slowlog_timeout     = 5s

		; ── Security: restrict PHP to this site's files only ──
		php_admin_value[open_basedir]        = ${PROJECT_PATH}:/tmp:/var/lib/php/sessions
		php_admin_value[error_log]           = /var/log/php/${USERNAME}-error.log
		php_admin_value[upload_max_filesize] = 64M
		php_admin_value[post_max_size]       = 64M
		php_admin_value[session.save_path]   = /var/lib/php/sessions
		php_admin_flag[log_errors]           = on

		env[DOMAIN]    = ${DOMAIN}
		env[SITE_TYPE] = ${APP_TYPE}
	EOF
}

# ─────────────────────────────────────────────────────────────────────────────
#  Apache vhost: HTTP-only (used during certbot verification)
# ─────────────────────────────────────────────────────────────────────────────
write_vhost_http() {
    sudo tee "$VHOST_PATH" > /dev/null <<-EOF
		<VirtualHost *:80>
		    ServerName  ${DOMAIN}
		    ServerAlias www.${DOMAIN}
		    DocumentRoot ${PUBLIC_PATH}

		    <Directory ${PUBLIC_PATH}>
		        Options -Indexes +FollowSymLinks
		        AllowOverride All
		        Require all granted
		    </Directory>

		    <FilesMatch "\.php$">
		        SetHandler "proxy:unix:${SOCK_PATH}|fcgi://localhost"
		    </FilesMatch>

		    ErrorLog  ${APACHE_LOG_DIR}/${DOMAIN}-error.log
		    CustomLog ${APACHE_LOG_DIR}/${DOMAIN}-access.log combined
		</VirtualHost>
	EOF
}

# ─────────────────────────────────────────────────────────────────────────────
#  Apache vhost: full HTTPS with security headers (written after SSL issuance)
# ─────────────────────────────────────────────────────────────────────────────
write_vhost_https() {
    sudo a2enmod proxy_fcgi setenvif rewrite headers ssl > /dev/null 2>&1

    sudo tee "$VHOST_PATH" > /dev/null <<-EOF
		# ──────────────────────────────────────────────────────────
		#  VirtualHost: ${DOMAIN}  [type: ${APP_TYPE}]
		#  Pool: ${USERNAME}  |  Generated: $(_ts)
		# ──────────────────────────────────────────────────────────

		<VirtualHost *:80>
		    ServerName  ${DOMAIN}
		    ServerAlias www.${DOMAIN}
		    RewriteEngine On
		    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
		</VirtualHost>

		<VirtualHost *:443>
		    ServerName  ${DOMAIN}
		    ServerAlias www.${DOMAIN}
		    DocumentRoot ${PUBLIC_PATH}

		    # ── SSL ─────────────────────────────────────────────────
		    SSLEngine             on
		    SSLCertificateFile    /etc/letsencrypt/live/${DOMAIN}/fullchain.pem
		    SSLCertificateKeyFile /etc/letsencrypt/live/${DOMAIN}/privkey.pem
		    SSLProtocol           all -SSLv3 -TLSv1 -TLSv1.1
		    SSLHonorCipherOrder   off
		    SSLSessionTickets     off

		    # ── PHP-FPM via dedicated socket ─────────────────────────
		    <FilesMatch "\.php$">
		        SetHandler "proxy:unix:${SOCK_PATH}|fcgi://localhost"
		    </FilesMatch>

		    # ── Directory ────────────────────────────────────────────
		    <Directory ${PUBLIC_PATH}>
		        Options       -Indexes +FollowSymLinks
		        AllowOverride All
		        Require       all granted
		        FallbackResource /index.php
		    </Directory>

		    # ── Block sensitive paths ────────────────────────────────
		    <DirectoryMatch "^${PROJECT_PATH}/(storage/(?!app/public)|bootstrap/cache|vendor|\.git)">
		        Require all denied
		    </DirectoryMatch>

		    # ── Security Headers ─────────────────────────────────────
		    Header always set X-Frame-Options           "SAMEORIGIN"
		    Header always set X-XSS-Protection          "1; mode=block"
		    Header always set X-Content-Type-Options    "nosniff"
		    Header always set Referrer-Policy           "strict-origin-when-cross-origin"
		    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
		    Header always unset X-Powered-By
		    Header always unset Server

		    # ── Logs ─────────────────────────────────────────────────
		    ErrorLog  ${APACHE_LOG_DIR}/${DOMAIN}-error.log
		    CustomLog ${APACHE_LOG_DIR}/${DOMAIN}-access.log combined

		    # ── FPM status (localhost only) ──────────────────────────
		    <Location /fpm-status-${USERNAME}>
		        Require ip 127.0.0.1
		        ProxyPass "unix:${SOCK_PATH}|fcgi://localhost/fpm-status-${USERNAME}"
		    </Location>

		    # ── Static file cache ────────────────────────────────────
		    <FilesMatch "\.(jpg|jpeg|png|gif|svg|ico|css|js|woff2?)$">
		        Header set Cache-Control "max-age=2592000, public"
		    </FilesMatch>

		    LimitRequestBody 67108864
		</VirtualHost>
	EOF
}

# ─────────────────────────────────────────────────────────────────────────────
#  Permissions: site user owns files, www-data reads via ACL
# ─────────────────────────────────────────────────────────────────────────────
set_permissions() {
    sudo chown -R "${USERNAME}:${USERNAME}" "$PROJECT_PATH"

    # dirs: owner=rwx group=r-x others=---
    sudo find "$PROJECT_PATH" -type d -exec chmod 750 {} \;
    # files: owner=rw- group=r-- others=---
    sudo find "$PROJECT_PATH" -type f -exec chmod 640 {} \;

    if [[ "${TYPE_IS_LARAVEL:-false}" == "true" ]]; then
        # storage & bootstrap/cache writable by PHP-FPM (runs as USERNAME)
        sudo find "$PROJECT_PATH/storage"         -type d -exec chmod 770 {} \; 2>/dev/null || true
        sudo find "$PROJECT_PATH/storage"         -type f -exec chmod 660 {} \; 2>/dev/null || true
        sudo find "$PROJECT_PATH/bootstrap/cache" -type d -exec chmod 770 {} \; 2>/dev/null || true
        sudo find "$PROJECT_PATH/bootstrap/cache" -type f -exec chmod 660 {} \; 2>/dev/null || true
    fi

    # Extra writable dirs defined per type
    for extra in "${TYPE_WRITABLE_EXTRAS[@]+"${TYPE_WRITABLE_EXTRAS[@]}"}"; do
        [[ -d "${PROJECT_PATH}/${extra}" ]] && sudo chmod -R 770 "${PROJECT_PATH}/${extra}"
    done

    # ── www-data access via ACL (no group pollution) ──────────────────────
    if command -v setfacl &>/dev/null; then
        # read + traverse all site files (for static file serving)
        sudo setfacl -R  -m u:www-data:r-X "$PROJECT_PATH"
        sudo setfacl -Rd -m u:www-data:r-X "$PROJECT_PATH"

        if [[ "${TYPE_IS_LARAVEL:-false}" == "true" ]]; then
            # storage/app/public readable for user-uploaded files
            if [[ -d "$PROJECT_PATH/storage/app/public" ]]; then
                sudo setfacl -R  -m u:www-data:r-X "$PROJECT_PATH/storage/app/public"
                sudo setfacl -Rd -m u:www-data:r-X "$PROJECT_PATH/storage/app/public"
            fi
        fi
        ok "ACL set — www-data reads static files only"
    else
        # Fallback when acl package not installed
        warn "setfacl not found — install 'acl' package for better isolation"
        warn "Fallback: adding www-data to group ${USERNAME}"
        sudo usermod -aG "$USERNAME" www-data
    fi

    # .env must never be readable by www-data
    if [[ -f "$PROJECT_PATH/.env" ]]; then
        sudo chown "${USERNAME}:${USERNAME}" "$PROJECT_PATH/.env"
        sudo chmod 600 "$PROJECT_PATH/.env"
    fi
}

# ─────────────────────────────────────────────────────────────────────────────
#  .env: copy from .env.example and inject DB credentials
# ─────────────────────────────────────────────────────────────────────────────
setup_env() {
    local env_file="${PROJECT_PATH}/.env"

    if [[ ! -f "$env_file" ]]; then
        if [[ -f "${PROJECT_PATH}/.env.example" ]]; then
            cp "${PROJECT_PATH}/.env.example" "$env_file"
            ok ".env.example → .env"
        else
            cat > "$env_file" <<-ENVEOF
				APP_NAME="${SITE_NAME}"
				APP_ENV=production
				APP_DEBUG=false
				APP_URL=https://${DOMAIN}

				LOG_CHANNEL=stack
				LOG_LEVEL=error

				DB_CONNECTION=mysql
				DB_HOST=127.0.0.1
				DB_PORT=3306
				DB_DATABASE=${DB_NAME}
				DB_USERNAME=${DB_USER}
				DB_PASSWORD=${DB_PASSWORD}
			ENVEOF
            ok ".env created from scratch"
            return
        fi
    fi

    _env_set() {
        local key="$1" val="$2"
        if grep -qE "^${key}=" "$env_file"; then
            sed -i "s|^${key}=.*|${key}=${val}|" "$env_file"
        else
            echo "${key}=${val}" >> "$env_file"
        fi
    }

    _env_set "APP_NAME"      "\"${SITE_NAME}\""
    _env_set "APP_URL"       "https://${DOMAIN}"
    _env_set "APP_ENV"       "production"
    _env_set "APP_DEBUG"     "false"
    _env_set "DB_CONNECTION" "mysql"
    _env_set "DB_HOST"       "127.0.0.1"
    _env_set "DB_PORT"       "3306"
    _env_set "DB_DATABASE"   "${DB_NAME}"
    _env_set "DB_USERNAME"   "${DB_USER}"
    _env_set "DB_PASSWORD"   "${DB_PASSWORD}"

    ok ".env updated with DB credentials"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Artisan helpers (run as site user)
# ─────────────────────────────────────────────────────────────────────────────
php_bin() {
    local bin="php${PHP_VERSION}"
    command -v "$bin" &>/dev/null && echo "$bin" || echo "php"
}

artisan() {
    local artisan_file="${PROJECT_PATH}/artisan"
    [[ -f "$artisan_file" ]] || { warn "artisan not found — skipping"; return 0; }
    sudo runuser -u "$USERNAME" -- "$(php_bin)" "$artisan_file" "$@"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Secrets file
# ─────────────────────────────────────────────────────────────────────────────
write_secrets() {
    sudo mkdir -p "$SECRETS_DIR"
    sudo chmod 700 "$SECRETS_DIR"

    sudo tee "$SECRETS_FILE" > /dev/null <<-EOF
		# Site secrets: ${DOMAIN}
		# Created: $(_ts)
		# WARNING: Do not share this file

		DOMAIN=${DOMAIN}
		SITE_TYPE=${APP_TYPE}
		PROJECT_PATH=${PROJECT_PATH}
		USERNAME=${USERNAME}
		DB_NAME=${DB_NAME}
		DB_USER=${DB_USER}
		DB_PASSWORD=${DB_PASSWORD}
		PHP_VERSION=${PHP_VERSION}
	EOF

    sudo chmod 600 "$SECRETS_FILE"
    sudo chown root:root "$SECRETS_FILE"
    ok "Secrets saved: $SECRETS_FILE"
}

# ─────────────────────────────────────────────────────────────────────────────
#  ACTION: create
# ─────────────────────────────────────────────────────────────────────────────
cmd_create() {
    info "Creating site: ${DOMAIN}  [type: ${TYPE_LABEL}  php: ${PHP_VERSION}]"

    # ── 1. System user ────────────────────────────────────────────────────────
    step "1/11  System user"
    create_system_user

    # ── 2. Copy project files ─────────────────────────────────────────────────
    step "2/11  Project files"
    [[ -d "$PROJECT_PATH" ]] && die "Project folder already exists: $PROJECT_PATH"
    sudo mkdir -p "$PROJECT_PATH"

    if [[ -n "${TYPE_TEMPLATE:-}" ]]; then
        [[ -d "$TYPE_TEMPLATE" ]] || die "Template not found: $TYPE_TEMPLATE"
        sudo rsync -a \
            --exclude='.git' \
            --exclude='node_modules' \
            --exclude='.env' \
            "${TYPE_TEMPLATE%/}/" "$PROJECT_PATH/"

        # Clear template-specific data defined per type
        for pattern in "${TYPE_CLEAR_ON_CREATE[@]+"${TYPE_CLEAR_ON_CREATE[@]}"}"; do
            sudo rm -rf "${PROJECT_PATH:?}/${pattern}"
        done
        ok "Files copied from: $TYPE_TEMPLATE"
    else
        ok "Empty site — no template to copy"
    fi

    # ── 3. Database ───────────────────────────────────────────────────────────
    step "3/11  Database"
    DB_PASSWORD=$(gen_pass 24)
    db_create "$DB_NAME" "$DB_USER" "$DB_PASSWORD"
    ok "Database: ${DB_NAME}  user: ${DB_USER}  (restricted grants)"

    # ── 4. .env ───────────────────────────────────────────────────────────────
    step "4/11  .env"
    if [[ "${TYPE_IS_LARAVEL:-false}" == "true" ]]; then
        setup_env
    else
        ok "Non-Laravel site — skipping .env"
    fi

    # ── 5. Secrets file ───────────────────────────────────────────────────────
    step "5/11  Secrets"
    write_secrets

    # ── 6. Permissions ────────────────────────────────────────────────────────
    step "6/11  Permissions"
    set_permissions

    # ── 7. PHP-FPM pool ───────────────────────────────────────────────────────
    step "7/11  PHP-FPM pool"
    if ! dpkg -l 2>/dev/null | grep -q "php${PHP_VERSION}-fpm"; then
        warn "php${PHP_VERSION}-fpm not installed — installing..."
        sudo apt-get install -y "php${PHP_VERSION}-fpm"
    fi
    write_pool
    sudo systemctl enable  "php${PHP_VERSION}-fpm"
    sudo systemctl restart "php${PHP_VERSION}-fpm"

    # Wait for socket
    local waited=0
    until [[ -S "$SOCK_PATH" ]] || (( waited >= 15 )); do sleep 1; ((waited++)); done
    [[ -S "$SOCK_PATH" ]] && ok "FPM socket ready: $SOCK_PATH" \
                          || warn "FPM socket not found — check: journalctl -u php${PHP_VERSION}-fpm"

    # ── 8. HTTP vhost + certbot ───────────────────────────────────────────────
    step "8/11  SSL certificate"
    if ! command -v certbot &>/dev/null; then
        warn "certbot not installed — installing..."
        sudo apt-get install -y certbot python3-certbot-apache
    fi

    write_vhost_http
    sudo a2ensite "${DOMAIN}" > /dev/null 2>&1
    sudo apachectl configtest && sudo systemctl reload apache2

    sudo certbot certonly --apache \
        --non-interactive --agree-tos \
        --email "$CERTBOT_EMAIL" \
        --domains "${DOMAIN},www.${DOMAIN}" \
        && ok "SSL certificate issued" \
        || warn "SSL failed — check DNS and port 80. HTTP vhost left active."

    # ── 9. Full HTTPS vhost ───────────────────────────────────────────────────
    step "9/11  HTTPS vhost"
    if [[ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]]; then
        write_vhost_https
        sudo apachectl configtest && sudo systemctl reload apache2
        ok "HTTPS vhost active: $VHOST_PATH"
    else
        warn "SSL cert missing — keeping HTTP-only vhost"
    fi

    # ── 10. Artisan (Laravel only) ────────────────────────────────────────────
    step "10/11  Artisan"
    if [[ "${TYPE_IS_LARAVEL:-false}" == "true" && -f "${PROJECT_PATH}/artisan" ]]; then
        artisan key:generate --force --ansi
        ok "APP_KEY generated"

        # storage:link
        local link="${PROJECT_PATH}/public/storage"
        [[ -e "$link" || -L "$link" ]] && sudo rm -rf "$link"
        artisan storage:link --ansi
        ok "storage:link created"

        artisan migrate --force --ansi
        ok "Migrations done"

        artisan config:cache --ansi
        artisan route:cache  --ansi
        artisan view:cache   --ansi
        ok "Caches warmed"
    else
        ok "Non-Laravel site — skipping artisan"
    fi

    # ── 11. Final permissions pass ────────────────────────────────────────────
    step "11/11  Final permissions"
    set_permissions

    echo -e "\n${GREEN}${BOLD}✔ Site created: https://${DOMAIN}${NC}"
    echo -e "  User/Group : ${USERNAME}"
    echo -e "  DB         : ${DB_NAME}  user: ${DB_USER}"
    echo -e "  Secrets    : ${SECRETS_FILE}"
    echo -e "  Pool       : ${POOL_PATH}"
    echo -e "  Vhost      : ${VHOST_PATH}"
    log "CREATE done: domain=${DOMAIN} user=${USERNAME} db=${DB_NAME} type=${APP_TYPE}"
}

# ─────────────────────────────────────────────────────────────────────────────
#  ACTION: update
# ─────────────────────────────────────────────────────────────────────────────
cmd_update() {
    info "Updating site: ${DOMAIN}  [type: ${TYPE_LABEL}]"
    [[ -d "$PROJECT_PATH" ]] || die "Project not found: $PROJECT_PATH"

    if [[ -z "${TYPE_TEMPLATE:-}" ]]; then
        die "Type '${APP_TYPE}' has no template — cannot update"
    fi
    [[ -d "$TYPE_TEMPLATE" ]] || die "Template not found: $TYPE_TEMPLATE"

    # ── 1. Sync directories ───────────────────────────────────────────────────
    step "1/4  Sync directories"
    for dir in "${TYPE_UPDATE_DIRS[@]+"${TYPE_UPDATE_DIRS[@]}"}"; do
        sudo rm -rf "${PROJECT_PATH:?}/${dir}"
        sudo cp -a  "${TYPE_TEMPLATE}/${dir}" "${PROJECT_PATH}/${dir}"
    done
    ok "Directories updated: ${TYPE_UPDATE_DIRS[*]+"${TYPE_UPDATE_DIRS[*]}"}"

    # ── 2. Sync files ─────────────────────────────────────────────────────────
    step "2/4  Sync files"
    for file in "${TYPE_UPDATE_FILES[@]+"${TYPE_UPDATE_FILES[@]}"}"; do
        sudo cp -a "${TYPE_TEMPLATE}/${file}" "${PROJECT_PATH}/${file}"
    done
    ok "Files updated: ${TYPE_UPDATE_FILES[*]+"${TYPE_UPDATE_FILES[*]}"}"

    # ── 3. Permissions ────────────────────────────────────────────────────────
    step "3/4  Permissions"
    set_permissions

    # ── 4. Artisan ────────────────────────────────────────────────────────────
    step "4/4  Artisan caches & migrate"
    if [[ "${TYPE_IS_LARAVEL:-false}" == "true" && -f "${PROJECT_PATH}/artisan" ]]; then
        artisan config:clear  --ansi
        artisan cache:clear   --ansi
        artisan config:cache  --ansi
        artisan route:cache   --ansi
        artisan view:cache    --ansi
        artisan migrate --force --ansi
        ok "Caches cleared, re-warmed, migrations done"
    fi

    echo -e "\n${GREEN}${BOLD}✔ Site updated: ${DOMAIN}${NC}"
    log "UPDATE done: domain=${DOMAIN} user=${USERNAME}"
}

# ─────────────────────────────────────────────────────────────────────────────
#  ACTION: delete
# ─────────────────────────────────────────────────────────────────────────────
cmd_delete() {
    info "Deleting site: ${DOMAIN}"
    load_secrets   # overwrite derived vars with actual stored values

    echo -e "${RED}${BOLD}WARNING: This permanently deletes the site, database, and user.${NC}"
    read -r -p "Type the domain to confirm deletion: " confirm
    [[ "$confirm" == "$DOMAIN" ]] || die "Confirmation mismatch. Aborting."

    # ── 1. Apache vhost ───────────────────────────────────────────────────────
    step "1/6  Apache vhost"
    sudo a2dissite "${DOMAIN}"          > /dev/null 2>&1 || true
    sudo a2dissite "${DOMAIN}-le-ssl"   > /dev/null 2>&1 || true
    sudo rm -f "$VHOST_PATH" "/etc/apache2/sites-available/${DOMAIN}-le-ssl.conf"
    sudo apachectl configtest && sudo systemctl reload apache2
    ok "Vhost removed"

    # ── 2. PHP-FPM pool ───────────────────────────────────────────────────────
    step "2/6  PHP-FPM pool"
    sudo rm -f "$POOL_PATH"
    sudo systemctl restart "php${PHP_VERSION}-fpm" || warn "php-fpm restart failed"
    ok "Pool removed: $POOL_PATH"

    # ── 3. SSL certificate ────────────────────────────────────────────────────
    step "3/6  SSL certificate"
    sudo certbot delete --cert-name "$DOMAIN" --non-interactive > /dev/null 2>&1 \
        && ok "SSL certificate removed" \
        || warn "No SSL cert for $DOMAIN (or certbot failed)"

    # ── 4. Database ───────────────────────────────────────────────────────────
    step "4/6  Database"
    db_drop "$DB_NAME" "$DB_USER"
    ok "Database '${DB_NAME}' and user '${DB_USER}' dropped"

    # ── 5. System user + group ────────────────────────────────────────────────
    step "5/6  System user & group"
    delete_system_user

    # ── 6. Files + secrets ───────────────────────────────────────────────────
    step "6/6  Files & secrets"
    sudo rm -rf "$PROJECT_PATH"
    sudo rm -f  "$SECRETS_FILE"
    ok "Project files and secrets removed"

    echo -e "\n${GREEN}${BOLD}✔ Site deleted: ${DOMAIN}${NC}"
    log "DELETE done: domain=${DOMAIN} user=${USERNAME}"
}

# ─────────────────────────────────────────────────────────────────────────────
#  ACTION: repair (permissions only)
# ─────────────────────────────────────────────────────────────────────────────
cmd_repair() {
    info "Repairing permissions: ${DOMAIN}"
    [[ -d "$PROJECT_PATH" ]] || die "Project not found: $PROJECT_PATH"
    set_permissions
    ok "Permissions repaired: $PROJECT_PATH"
    log "REPAIR done: domain=${DOMAIN} user=${USERNAME}"
}

# ─────────────────────────────────────────────────────────────────────────────
#  Usage
# ─────────────────────────────────────────────────────────────────────────────
usage() {
    local types
    types=$(ls "$TYPES_DIR"/*.conf 2>/dev/null \
        | xargs -n1 basename | sed 's/\.conf//' | tr '\n' ' ')
    cat <<-USAGE

		${BOLD}Site Manager${NC}

		Usage: site.sh <action> --domain=<domain> [options]

		Actions:
		  create  --domain=<d> --type=<t> [--php=<ver>] [--db-name=<n>] [--name=<n>]
		  update  --domain=<d>
		  delete  --domain=<d>
		  repair  --domain=<d>

		Available types: ${types}

		Examples:
		  sudo site.sh create --domain=shop.example.com --type=salelink
		  sudo site.sh create --domain=app.example.com  --type=laravel --php=8.3
		  sudo site.sh update --domain=shop.example.com
		  sudo site.sh delete --domain=shop.example.com
		  sudo site.sh repair --domain=shop.example.com

	USAGE
    exit 1
}

# ─────────────────────────────────────────────────────────────────────────────
#  Entry point
# ─────────────────────────────────────────────────────────────────────────────
#[[ "$(id -u)" -eq 0 ]] || die "Must be run as root:  sudo $0 $ACTION ..."
[[ -z "$ACTION"      ]] && usage

validate_inputs
load_app_type
derive_vars

case "$ACTION" in
    create) cmd_create ;;
    update) cmd_update ;;
    delete) cmd_delete ;;
    repair) cmd_repair ;;
    *)      usage      ;;
esac
