#!/usr/bin/env bash
# =============================================================================
# setup-apache2.sh — Configure Apache 2 to serve this Bagisto install.
#
# Serves from public/ with AllowOverride All so public/.htaccess can rewrite
# to index.php (Bagisto's documented Apache layout). Safe to re-run.
#
# Usage:
#   sudo ./scripts/setup-apache2.sh
#   sudo ./scripts/setup-apache2.sh --domain shop.example.com
#   sudo ./scripts/setup-apache2.sh --domain shop.example.com --ssl
#   sudo ./scripts/setup-apache2.sh --port 8080
#
# Environment overrides (same names as flags):
#   BAGISTO_DOMAIN  BAGISTO_PORT  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}"
SITE_NAME="bagisto"
ENABLE_SSL=0
KEEP_DEFAULT=0
SKIP_PERMISSIONS=0
SKIP_INSTALL=0
DRY_RUN=0
WEB_USER=""

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

Options:
  --domain NAME          ServerName (default: localhost)
  --port N               listen port (default: 80)
  --app-dir PATH         Bagisto root (default: parent of this script)
  --site-name NAME       Apache 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 Apache's default site
  --skip-permissions     Do not chown/chmod storage or bootstrap/cache
  --skip-install         Do not apt/brew install apache2 or PHP
  --dry-run              Print actions without writing files or reloading Apache
  -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_version() {
    if command -v php >/dev/null 2>&1; then
        php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' 2>/dev/null || true
        return
    fi
    if apt-cache show php8.4-cli >/dev/null 2>&1; then
        echo "8.4"
        return
    fi
    echo "8.3"
}

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 apache >/dev/null 2>&1; then
        echo "apache"
        return
    fi
    if id _www >/dev/null 2>&1; then
        echo "_www"
        return
    fi
    echo "$(logname 2>/dev/null || echo "${SUDO_USER:-${USER}}")"
}

apache_paths() {
    local os="$1"
    APACHE_CONF_FILE=""
    APACHE_SITES_AVAILABLE=""
    APACHE_SITES_ENABLED=""
    APACHE_LOG_DIR=""
    APACHE_SERVICE=""
    APACHE_CTL=""

    case "${os}" in
        macos)
            local brew_prefix=""
            if command -v brew >/dev/null 2>&1; then
                brew_prefix="$(brew --prefix 2>/dev/null || true)"
            fi
            if [[ -n "${brew_prefix}" ]]; then
                APACHE_PREFIX="${brew_prefix}/etc/httpd"
            elif [[ -d /opt/homebrew/etc/httpd || -d /opt/homebrew ]]; then
                APACHE_PREFIX="/opt/homebrew/etc/httpd"
            else
                APACHE_PREFIX="/usr/local/etc/httpd"
            fi
            APACHE_CONF_FILE="${APACHE_PREFIX}/extra/${SITE_NAME}.conf"
            APACHE_LOG_DIR="${APACHE_PREFIX%/etc/httpd}/var/log/httpd"
            APACHE_SERVICE="httpd"
            APACHE_CTL="$(command -v apachectl || true)"
            if [[ -n "${brew_prefix}" && -x "${brew_prefix}/bin/apachectl" ]]; then
                APACHE_CTL="${brew_prefix}/bin/apachectl"
            elif [[ -n "${brew_prefix}" && -x "${brew_prefix}/sbin/apachectl" ]]; then
                APACHE_CTL="${brew_prefix}/sbin/apachectl"
            fi
            ;;
        fedora|rhel|centos|rocky|almalinux)
            APACHE_CONF_FILE="/etc/httpd/conf.d/${SITE_NAME}.conf"
            APACHE_LOG_DIR="/var/log/httpd"
            APACHE_SERVICE="httpd"
            APACHE_CTL="$(command -v apachectl || command -v httpd || true)"
            ;;
        *)
            if [[ -d /etc/apache2/sites-available ]]; then
                APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
                APACHE_SITES_ENABLED="/etc/apache2/sites-enabled"
                APACHE_CONF_FILE="${APACHE_SITES_AVAILABLE}/${SITE_NAME}.conf"
                APACHE_LOG_DIR="/var/log/apache2"
                APACHE_SERVICE="apache2"
                APACHE_CTL="$(command -v apache2ctl || command -v apachectl || true)"
            elif [[ -d /etc/httpd/conf.d ]]; then
                APACHE_CONF_FILE="/etc/httpd/conf.d/${SITE_NAME}.conf"
                APACHE_LOG_DIR="/var/log/httpd"
                APACHE_SERVICE="httpd"
                APACHE_CTL="$(command -v apachectl || true)"
            else
                APACHE_CONF_FILE="/etc/apache2/sites-available/${SITE_NAME}.conf"
                APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
                APACHE_SITES_ENABLED="/etc/apache2/sites-enabled"
                APACHE_LOG_DIR="/var/log/apache2"
                APACHE_SERVICE="apache2"
            fi
            ;;
    esac
}

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

    php_ver="$(detect_php_version)"

    case "${os}" in
        ubuntu|debian)
            local need_apache=0 need_php=0
            command -v apache2 >/dev/null 2>&1 || need_apache=1
            if ! dpkg -s "libapache2-mod-php${php_ver}" >/dev/null 2>&1 \
                && ! dpkg -s libapache2-mod-php >/dev/null 2>&1 \
                && ! dpkg -s libapache2-mod-php8.4 >/dev/null 2>&1 \
                && ! dpkg -s libapache2-mod-php8.3 >/dev/null 2>&1; then
                need_php=1
            fi
            if [[ "${need_apache}" -eq 1 || "${need_php}" -eq 1 ]]; then
                log "Installing Apache 2 and PHP packages..."
                run apt-get update -y
            fi
            if [[ "${need_apache}" -eq 1 ]]; then
                run env DEBIAN_FRONTEND=noninteractive apt-get install -y apache2
            fi
            if [[ "${need_php}" -eq 1 ]]; then
                if apt-cache show "libapache2-mod-php${php_ver}" >/dev/null 2>&1; then
                    run env DEBIAN_FRONTEND=noninteractive apt-get install -y \
                        "libapache2-mod-php${php_ver}" "php${php_ver}-cli" "php${php_ver}-mysql" \
                        "php${php_ver}-xml" "php${php_ver}-mbstring" "php${php_ver}-curl" \
                        "php${php_ver}-zip" "php${php_ver}-gd" "php${php_ver}-intl" \
                        "php${php_ver}-bcmath" "php${php_ver}-soap" "php${php_ver}-calendar"
                else
                    run env DEBIAN_FRONTEND=noninteractive apt-get install -y \
                        libapache2-mod-php php-cli php-mysql php-xml php-mbstring \
                        php-curl php-zip php-gd php-intl php-bcmath php-soap
                fi
            fi
            ;;
        fedora|rhel|centos|rocky|almalinux)
            if ! command -v httpd >/dev/null 2>&1; then
                log "Installing httpd and PHP..."
                if command -v dnf >/dev/null 2>&1; then
                    run dnf install -y httpd php 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 httpd php 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 httpd >/dev/null 2>&1 && ! command -v apachectl >/dev/null 2>&1; then
                log "Installing httpd via Homebrew..."
                run brew install httpd
            fi
            if ! command -v php >/dev/null 2>&1; then
                log "Installing php via Homebrew..."
                run brew install php
            fi
            ;;
        *)
            command -v apache2 >/dev/null 2>&1 || command -v httpd >/dev/null 2>&1 \
                || die "Apache is not installed. Install apache2/httpd, or re-run without --skip-install on Ubuntu/Debian/macOS."
            ;;
    esac
}

enable_modules() {
    local os="$1"
    if [[ "${os}" == "macos" ]]; then
        return
    fi
    if command -v a2enmod >/dev/null 2>&1; then
        log "Enabling Apache modules (rewrite, headers, deflate, expires)"
        run a2enmod rewrite
        run a2enmod headers || true
        run a2enmod deflate || true
        run a2enmod expires || true
        if [[ "${ENABLE_SSL}" -eq 1 ]]; then
            run a2enmod ssl
        fi
        run a2enmod php8.4 2>/dev/null || run a2enmod php8.3 2>/dev/null || run a2enmod php 2>/dev/null || true
    fi
}

start_services() {
    local os="$1"

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

    if command -v systemctl >/dev/null 2>&1; then
        if [[ -n "${APACHE_SERVICE}" ]]; then
            run systemctl enable --now "${APACHE_SERVICE}"
        fi
    elif command -v service >/dev/null 2>&1 && [[ -n "${APACHE_SERVICE}" ]]; then
        run service "${APACHE_SERVICE}" start || true
    fi
}

render_apache_conf() {
    local root="$1"
    local server_name="$2"
    local listen_port="$3"
    local log_dir="$4"

    cat <<EOF
# Generated by scripts/setup-apache2.sh — Bagisto Apache vhost
# Document root must stay public/. Do not point this at the project root.
# Routing is handled by public/.htaccess (AllowOverride All + mod_rewrite).

<VirtualHost *:${listen_port}>
    ServerName ${server_name}
    ServerAdmin webmaster@${server_name}
    DocumentRoot ${root}

    <Directory ${root}>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
        DirectoryIndex index.php
    </Directory>

    <FilesMatch "^\\.env">
        Require all denied
    </FilesMatch>

    LimitRequestBody 104857600

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

write_php_limits() {
    local os="$1"
    local php_ver dest=""
    php_ver="$(detect_php_version)"

    case "${os}" in
        ubuntu|debian)
            if [[ -d "/etc/php/${php_ver}/apache2/conf.d" ]]; then
                dest="/etc/php/${php_ver}/apache2/conf.d/99-bagisto.ini"
            elif [[ -d /etc/php/apache2/conf.d ]]; then
                dest="/etc/php/apache2/conf.d/99-bagisto.ini"
            fi
            ;;
        fedora|rhel|centos|rocky|almalinux)
            if [[ -d /etc/php.d ]]; then
                dest="/etc/php.d/99-bagisto.ini"
            fi
            ;;
    esac

    [[ -n "${dest}" ]] || return 0

    log "Writing PHP upload limits to ${dest}"
    write_file "${dest}" <<'EOF'
; Generated by scripts/setup-apache2.sh
memory_limit = 512M
upload_max_filesize = 100M
post_max_size = 110M
max_execution_time = 300
max_input_time = 300
EOF
}

enable_site() {
    local os="$1"

    if command -v a2ensite >/dev/null 2>&1 && [[ -n "${APACHE_SITES_AVAILABLE}" ]]; then
        run a2ensite "${SITE_NAME}.conf"
        if [[ "${KEEP_DEFAULT}" -eq 0 && "${PORT}" == "80" ]]; then
            run a2dissite 000-default.conf || run a2dissite 000-default || true
        fi
    fi

    if [[ "${os}" == "macos" && -n "${APACHE_CONF_FILE}" ]]; then
        local httpd_conf
        httpd_conf="$(dirname "$(dirname "${APACHE_CONF_FILE}")")/httpd.conf"
        if [[ -f "${httpd_conf}" ]] && ! grep -q "extra/${SITE_NAME}.conf" "${httpd_conf}"; then
            warn "Add this line to ${httpd_conf} so the vhost is loaded:"
            warn "    Include extra/${SITE_NAME}.conf"
        fi
        if [[ -f "${httpd_conf}" ]] && ! grep -qE '^LoadModule rewrite_module' "${httpd_conf}"; then
            warn "Uncomment LoadModule rewrite_module in ${httpd_conf} (required for .htaccess)."
        fi
        if [[ -f "${httpd_conf}" ]] && ! grep -qE '^LoadModule php' "${httpd_conf}"; then
            warn "Load the Homebrew PHP module in ${httpd_conf}, e.g. LoadModule php_module /opt/homebrew/opt/php/lib/httpd/modules/libphp.so"
        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_apache() {
    if [[ -n "${APACHE_CTL}" ]]; then
        run "${APACHE_CTL}" configtest
    fi

    if command -v systemctl >/dev/null 2>&1 && [[ -n "${APACHE_SERVICE}" ]] && systemctl is-active --quiet "${APACHE_SERVICE}" 2>/dev/null; then
        run systemctl reload "${APACHE_SERVICE}"
        return
    fi

    if [[ -n "${APACHE_CTL}" ]]; then
        run "${APACHE_CTL}" graceful || run "${APACHE_CTL}" restart || true
        return
    fi

    if command -v service >/dev/null 2>&1 && [[ -n "${APACHE_SERVICE}" ]]; then
        run service "${APACHE_SERVICE}" reload || run service "${APACHE_SERVICE}" restart || true
    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-apache
                ;;
            *)
                die "certbot is not installed. Install certbot with the apache plugin, then re-run --ssl."
                ;;
        esac
    fi
    log "Requesting Let's Encrypt certificate for ${DOMAIN}"
    run certbot --apache -d "${DOMAIN}" --non-interactive --agree-tos --register-unsafely-without-email --redirect || \
        warn "certbot failed. HTTP vhost is still in place; run certbot --apache -d ${DOMAIN} later."
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --domain) DOMAIN="$2"; shift 2 ;;
        --port) PORT="$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."
[[ -f "${PUBLIC_DIR}/.htaccess" ]] || warn "public/.htaccess is missing; Apache rewrites will not work."

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

ensure_packages "${OS}"
apache_paths "${OS}"
[[ -n "${APACHE_CONF_FILE}" ]] || die "Could not locate Apache config directory. Is apache2/httpd installed?"

enable_modules "${OS}"
start_services "${OS}"

WEB_USER="$(detect_web_user)"

log "Apache config: ${APACHE_CONF_FILE}"
log "Web user: ${WEB_USER}"

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

render_apache_conf "${PUBLIC_DIR}" "${DOMAIN}" "${PORT}" "${APACHE_LOG_DIR}" | write_file "${APACHE_CONF_FILE}"
write_php_limits "${OS}"
enable_site "${OS}"
fix_permissions "${WEB_USER}"
reload_apache
maybe_ssl

log "Bagisto Apache 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"
