#!/usr/bin/env bash
# =============================================================================
# setup-nginx.sh — Configure Nginx + PHP-FPM to serve this Bagisto install.
#
# Serves from public/, using Bagisto's documented Nginx layout (themes cache,
# image-cache fallback, PHP 8.3/8.4 FPM). Safe to re-run.
#
# Usage:
#   sudo ./scripts/setup-nginx.sh
#   sudo ./scripts/setup-nginx.sh --domain shop.example.com
#   sudo ./scripts/setup-nginx.sh --domain shop.example.com --ssl
#   sudo ./scripts/setup-nginx.sh --port 8080 --php-socket /run/php/php8.3-fpm.sock
#
# Environment overrides (same names as flags):
#   BAGISTO_DOMAIN  BAGISTO_PORT  BAGISTO_PHP_SOCKET  BAGISTO_APP_DIR
# =============================================================================
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_DIR="${BAGISTO_APP_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}"
PUBLIC_DIR="${APP_DIR}/public"

DOMAIN="${BAGISTO_DOMAIN:-localhost}"
PORT="${BAGISTO_PORT:-80}"
PHP_SOCKET="${BAGISTO_PHP_SOCKET:-}"
SITE_NAME="bagisto"
ENABLE_SSL=0
KEEP_DEFAULT=0
SKIP_PERMISSIONS=0
SKIP_INSTALL=0
DRY_RUN=0
WEB_USER=""

usage() {
    cat <<'EOF'
Configure Nginx to serve this Bagisto project from public/.

Options:
  --domain NAME          server_name (default: localhost)
  --port N               listen port (default: 80)
  --php-socket PATH      PHP-FPM unix socket or host:port
  --app-dir PATH         Bagisto root (default: parent of this script)
  --site-name NAME       Nginx config filename (default: bagisto)
  --web-user USER        Owner of storage/ and bootstrap/cache (auto-detected)
  --ssl                  Issue/renew Let's Encrypt cert with certbot and enable HTTPS
  --keep-default         Do not disable Nginx's default site
  --skip-permissions     Do not chown/chmod storage or bootstrap/cache
  --skip-install         Do not apt/brew install nginx or php-fpm
  --dry-run              Print actions without writing files or reloading Nginx
  -h, --help             Show this help
EOF
}

log()  { printf '==> %s\n' "$*"; }
warn() { printf 'WARN: %s\n' "$*" >&2; }
die()  { printf 'ERROR: %s\n' "$*" >&2; exit 1; }

run() {
    if [[ "${DRY_RUN}" -eq 1 ]]; then
        printf '[dry-run] %s\n' "$*"
        return 0
    fi
    "$@"
}

write_file() {
    local dest="$1"
    if [[ "${DRY_RUN}" -eq 1 ]]; then
        printf '[dry-run] write %s\n' "${dest}"
        cat
        return 0
    fi
    cat > "${dest}"
}

need_root() {
    local os="$1"
    if [[ "${DRY_RUN}" -eq 1 ]]; then
        return 0
    fi
    if [[ "${os}" == "macos" ]]; then
        return 0
    fi
    if [[ "$(id -u)" -ne 0 ]]; then
        die "Run as root (sudo $0 ...), or pass --dry-run to preview."
    fi
}

detect_os() {
    if [[ "$(uname -s)" == "Darwin" ]]; then
        echo "macos"
        return
    fi
    if [[ -f /etc/os-release ]]; then
        # shellcheck disable=SC1091
        . /etc/os-release
        echo "${ID:-linux}"
        return
    fi
    echo "linux"
}

detect_php_socket() {
    local candidate sock

    if [[ -n "${PHP_SOCKET}" ]]; then
        echo "${PHP_SOCKET}"
        return
    fi

    for candidate in \
        /run/php/php8.4-fpm.sock \
        /run/php/php8.3-fpm.sock \
        /var/run/php/php8.4-fpm.sock \
        /var/run/php/php8.3-fpm.sock \
        /run/php/php-fpm.sock \
        /var/run/php/php-fpm.sock \
        /opt/homebrew/var/run/php-fpm.sock \
        /usr/local/var/run/php-fpm.sock
    do
        if [[ -S "${candidate}" ]]; then
            echo "${candidate}"
            return
        fi
    done

    sock="$(find /run/php /var/run/php /opt/homebrew/var/run /usr/local/var/run \
        \( -name 'php*-fpm.sock' -o -name 'php-fpm.sock' \) 2>/dev/null | head -n 1 || true)"
    if [[ -n "${sock}" && -S "${sock}" ]]; then
        echo "${sock}"
        return
    fi

    if command -v php-fpm8.4 >/dev/null 2>&1 || command -v php-fpm8.3 >/dev/null 2>&1 || command -v php-fpm >/dev/null 2>&1 || command -v php >/dev/null 2>&1; then
        if (command -v nc >/dev/null 2>&1 && nc -z 127.0.0.1 9000 >/dev/null 2>&1) \
            || (command -v lsof >/dev/null 2>&1 && lsof -iTCP:9000 -sTCP:LISTEN >/dev/null 2>&1); then
            echo "127.0.0.1:9000"
            return
        fi
    fi

    return 1
}

fastcgi_pass_value() {
    local socket="$1"
    if [[ "${socket}" == *:* && "${socket}" != /* ]]; then
        echo "${socket}"
    else
        echo "unix:${socket}"
    fi
}

detect_web_user() {
    if [[ -n "${WEB_USER}" ]]; then
        echo "${WEB_USER}"
        return
    fi
    if id www-data >/dev/null 2>&1; then
        echo "www-data"
        return
    fi
    if id nginx >/dev/null 2>&1; then
        echo "nginx"
        return
    fi
    if id _www >/dev/null 2>&1; then
        echo "_www"
        return
    fi
    echo "$(logname 2>/dev/null || echo "${SUDO_USER:-${USER}}")"
}

nginx_paths() {
    local os="$1"
    NGINX_BIN="$(command -v nginx || true)"
    NGINX_SITES_AVAILABLE=""
    NGINX_SITES_ENABLED=""
    NGINX_CONF_D=""
    NGINX_CONF_FILE=""

    case "${os}" in
        macos)
            if [[ -d /opt/homebrew/etc/nginx ]]; then
                NGINX_PREFIX="/opt/homebrew/etc/nginx"
            elif [[ -d /usr/local/etc/nginx ]]; then
                NGINX_PREFIX="/usr/local/etc/nginx"
            else
                NGINX_PREFIX=""
            fi
            if [[ -n "${NGINX_PREFIX}" ]]; then
                if [[ "${DRY_RUN}" -eq 0 ]]; then
                    mkdir -p "${NGINX_PREFIX}/servers"
                fi
                NGINX_CONF_FILE="${NGINX_PREFIX}/servers/${SITE_NAME}.conf"
            fi
            ;;
        *)
            if [[ -d /etc/nginx/sites-available ]]; then
                NGINX_SITES_AVAILABLE="/etc/nginx/sites-available"
                NGINX_SITES_ENABLED="/etc/nginx/sites-enabled"
                NGINX_CONF_FILE="${NGINX_SITES_AVAILABLE}/${SITE_NAME}.conf"
            elif [[ -d /etc/nginx/conf.d ]]; then
                NGINX_CONF_D="/etc/nginx/conf.d"
                NGINX_CONF_FILE="${NGINX_CONF_D}/${SITE_NAME}.conf"
            fi
            ;;
    esac
}

ensure_packages() {
    local os="$1"
    if [[ "${SKIP_INSTALL}" -eq 1 ]]; then
        return
    fi

    case "${os}" in
        ubuntu|debian)
            local need_nginx=0 need_php=0
            command -v nginx >/dev/null 2>&1 || need_nginx=1
            if ! command -v php-fpm8.4 >/dev/null 2>&1 \
                && ! command -v php-fpm8.3 >/dev/null 2>&1 \
                && ! command -v php-fpm >/dev/null 2>&1; then
                need_php=1
            fi
            if [[ "${need_nginx}" -eq 1 || "${need_php}" -eq 1 ]]; then
                log "Installing nginx and PHP-FPM packages..."
                run apt-get update -y
            fi
            if [[ "${need_nginx}" -eq 1 ]]; then
                run env DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
            fi
            if [[ "${need_php}" -eq 1 ]]; then
                if apt-cache show php8.4-fpm >/dev/null 2>&1; then
                    run env DEBIAN_FRONTEND=noninteractive apt-get install -y \
                        php8.4-fpm php8.4-cli php8.4-mysql php8.4-xml php8.4-mbstring \
                        php8.4-curl php8.4-zip php8.4-gd php8.4-intl php8.4-bcmath php8.4-soap
                else
                    run env DEBIAN_FRONTEND=noninteractive apt-get install -y \
                        php8.3-fpm php8.3-cli php8.3-mysql php8.3-xml php8.3-mbstring \
                        php8.3-curl php8.3-zip php8.3-gd php8.3-intl php8.3-bcmath php8.3-soap
                fi
            fi
            ;;
        fedora|rhel|centos|rocky|almalinux)
            if ! command -v nginx >/dev/null 2>&1; then
                log "Installing nginx..."
                if command -v dnf >/dev/null 2>&1; then
                    run dnf install -y nginx php-fpm php-cli php-mysqlnd php-xml php-mbstring php-curl php-zip php-gd php-intl php-bcmath php-soap
                else
                    run yum install -y nginx php-fpm php-cli php-mysqlnd php-xml php-mbstring php-curl php-zip php-gd php-intl php-bcmath php-soap
                fi
            fi
            ;;
        macos)
            if ! command -v brew >/dev/null 2>&1; then
                die "Homebrew is required on macOS. Install it from https://brew.sh then re-run."
            fi
            if ! command -v nginx >/dev/null 2>&1; then
                log "Installing nginx via Homebrew..."
                run brew install nginx
            fi
            if ! command -v php-fpm >/dev/null 2>&1 && ! command -v php >/dev/null 2>&1; then
                log "Installing php via Homebrew..."
                run brew install php
            fi
            ;;
        *)
            command -v nginx >/dev/null 2>&1 || die "nginx is not installed. Install it, or re-run without --skip-install on Ubuntu/Debian/macOS."
            ;;
    esac
}

start_services() {
    local os="$1"

    if [[ "${os}" == "macos" ]]; then
        if command -v brew >/dev/null 2>&1; then
            run brew services start php || true
            run brew services start nginx || true
        fi
        return
    fi

    if command -v systemctl >/dev/null 2>&1; then
        if systemctl list-unit-files | grep -q '^php8.4-fpm'; then
            run systemctl enable --now php8.4-fpm
        elif systemctl list-unit-files | grep -q '^php8.3-fpm'; then
            run systemctl enable --now php8.3-fpm
        elif systemctl list-unit-files | grep -q '^php-fpm'; then
            run systemctl enable --now php-fpm
        fi
        run systemctl enable --now nginx
    elif command -v service >/dev/null 2>&1; then
        run service php8.3-fpm start 2>/dev/null || run service php-fpm start 2>/dev/null || true
        run service nginx start 2>/dev/null || true
    fi
}

render_nginx_conf() {
    local root="$1"
    local server_name="$2"
    local listen_port="$3"
    local fastcgi="$4"

    cat <<EOF
# Generated by scripts/setup-nginx.sh — Bagisto Nginx vhost
# Document root must stay public/. Do not point this at the project root.

server {
    listen ${listen_port};
    listen [::]:${listen_port};
    server_name ${server_name};
    root ${root};

    client_max_body_size 100M;

    index index.php;
    charset utf-8;

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

    # Built admin/shop assets
    location ^~ /themes/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
        try_files \$uri =404;
    }

    # Image cache is generated by PHP, not static files
    location ^~ /cache/ {
        access_log off;
        try_files \$uri /index.php?\$query_string;
    }

    location ^~ /storage/ {
        expires 30d;
        add_header Cache-Control "public";
        access_log off;
        try_files \$uri /index.php?\$query_string;
    }

    location / {
        try_files \$uri \$uri/ /index.php?\$query_string;
    }

    location ~* ^\/(?!cache).*\.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|webp|woff|woff2)\$ {
        expires max;
        access_log off;
        add_header Cache-Control "public";
    }

    location ~* \.(?:woff2?|ttf|eot|otf|ico|svg|jpe?g|png|gif|webp|avif|css|js)\$ {
        expires 30d;
        add_header Cache-Control "public";
        access_log off;
        try_files \$uri \$uri/ /index.php?\$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ ^/index\\.php(/|\$) {
        fastcgi_pass ${fastcgi};
        fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_hide_header X-Powered-By;

        fastcgi_buffering on;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 16 16k;
        fastcgi_connect_timeout 60s;
        fastcgi_send_timeout 300s;
        fastcgi_read_timeout 300s;
    }

    location ~ /\\.(?!well-known).* {
        deny all;
    }
}
EOF
}

enable_site() {
    local os="$1"

    if [[ -n "${NGINX_SITES_AVAILABLE}" && -n "${NGINX_SITES_ENABLED}" ]]; then
        run ln -sfn "${NGINX_CONF_FILE}" "${NGINX_SITES_ENABLED}/${SITE_NAME}.conf"
        if [[ "${KEEP_DEFAULT}" -eq 0 && "${PORT}" == "80" ]]; then
            run rm -f "${NGINX_SITES_ENABLED}/default"
        fi
    fi

    if [[ "${os}" == "macos" ]]; then
        local prefix
        prefix="$(dirname "$(dirname "${NGINX_CONF_FILE}")")"
        if [[ -f "${prefix}/nginx.conf" ]] && ! grep -q 'include servers/\*' "${prefix}/nginx.conf"; then
            warn "Add this line inside the http {} block of ${prefix}/nginx.conf if the vhost is not loaded:"
            warn "    include servers/*;"
        fi
    fi
}

fix_permissions() {
    local user="$1"
    if [[ "${SKIP_PERMISSIONS}" -eq 1 ]]; then
        return
    fi
    if [[ "$(id -u)" -ne 0 ]]; then
        warn "Not root; skipped chown of storage/ and bootstrap/cache/. Run with sudo if PHP cannot write uploads."
        return
    fi

    log "Setting writable permissions for ${user} on storage/ and bootstrap/cache/"
    run chown -R "${user}:${user}" "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"
    run chmod -R 775 "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"

    if [[ -e "${PUBLIC_DIR}/storage" ]]; then
        return
    fi
    if command -v php >/dev/null 2>&1; then
        log "Creating public/storage link"
        (cd "${APP_DIR}" && run php artisan storage:link --force --no-interaction) || true
    fi
}

reload_nginx() {
    if command -v nginx >/dev/null 2>&1; then
        run nginx -t
    fi

    if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet nginx 2>/dev/null; then
        run systemctl reload nginx
        return
    fi

    if command -v nginx >/dev/null 2>&1; then
        if nginx -s reload 2>/dev/null; then
            return
        fi
        run nginx
    fi
}

maybe_ssl() {
    if [[ "${ENABLE_SSL}" -eq 0 ]]; then
        return
    fi
    if [[ "${DOMAIN}" == "localhost" || "${DOMAIN}" == "_" ]]; then
        die "--ssl requires a public --domain (not localhost)."
    fi
    if ! command -v certbot >/dev/null 2>&1; then
        local os
        os="$(detect_os)"
        case "${os}" in
            ubuntu|debian)
                run apt-get update -y
                run env DEBIAN_FRONTEND=noninteractive apt-get install -y certbot python3-certbot-nginx
                ;;
            *)
                die "certbot is not installed. Install certbot with the nginx plugin, then re-run --ssl."
                ;;
        esac
    fi
    log "Requesting Let's Encrypt certificate for ${DOMAIN}"
    run certbot --nginx -d "${DOMAIN}" --non-interactive --agree-tos --register-unsafely-without-email --redirect || \
        warn "certbot failed. HTTP vhost is still in place; run certbot --nginx -d ${DOMAIN} later."
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --domain) DOMAIN="$2"; shift 2 ;;
        --port) PORT="$2"; shift 2 ;;
        --php-socket) PHP_SOCKET="$2"; shift 2 ;;
        --app-dir) APP_DIR="$2"; PUBLIC_DIR="${APP_DIR}/public"; shift 2 ;;
        --site-name) SITE_NAME="$2"; shift 2 ;;
        --web-user) WEB_USER="$2"; shift 2 ;;
        --ssl) ENABLE_SSL=1; shift ;;
        --keep-default) KEEP_DEFAULT=1; shift ;;
        --skip-permissions) SKIP_PERMISSIONS=1; shift ;;
        --skip-install) SKIP_INSTALL=1; shift ;;
        --dry-run) DRY_RUN=1; shift ;;
        -h|--help) usage; exit 0 ;;
        *) die "Unknown option: $1 (see --help)" ;;
    esac
done

[[ -f "${PUBLIC_DIR}/index.php" ]] || die "Bagisto public/index.php not found at ${PUBLIC_DIR}. Pass --app-dir."

OS="$(detect_os)"
need_root "${OS}"
log "OS: ${OS}"
log "App dir: ${APP_DIR}"
log "Domain: ${DOMAIN}"
log "Port: ${PORT}"

ensure_packages "${OS}"
start_services "${OS}"

nginx_paths "${OS}"
[[ -n "${NGINX_CONF_FILE}" ]] || die "Could not locate Nginx config directory. Is nginx installed?"

WEB_USER="$(detect_web_user)"

log "Waiting for PHP-FPM..."
DETECTED_SOCKET=""
for _ in $(seq 1 15); do
    if DETECTED_SOCKET="$(detect_php_socket)"; then
        break
    fi
    DETECTED_SOCKET=""
    sleep 1
done
PHP_SOCKET="${DETECTED_SOCKET}"
[[ -n "${PHP_SOCKET}" ]] || die "Could not find a PHP-FPM socket. Install php-fpm (8.3 or 8.4) or pass --php-socket."
FASTCGI="$(fastcgi_pass_value "${PHP_SOCKET}")"

log "Nginx config: ${NGINX_CONF_FILE}"
log "PHP-FPM: ${FASTCGI}"
log "Web user: ${WEB_USER}"

if [[ "${DRY_RUN}" -eq 0 ]]; then
    mkdir -p "$(dirname "${NGINX_CONF_FILE}")"
fi

render_nginx_conf "${PUBLIC_DIR}" "${DOMAIN}" "${PORT}" "${FASTCGI}" | write_file "${NGINX_CONF_FILE}"
enable_site "${OS}"
fix_permissions "${WEB_USER}"
reload_nginx
maybe_ssl

log "Bagisto Nginx vhost is ready."
if [[ "${PORT}" == "80" ]]; then
    log "Storefront: http://${DOMAIN}/"
    log "Admin:      http://${DOMAIN}/admin"
else
    log "Storefront: http://${DOMAIN}:${PORT}/"
    log "Admin:      http://${DOMAIN}:${PORT}/admin"
fi
log "Point APP_URL in .env at that storefront URL, then run: php artisan optimize:clear"
