#!/usr/bin/env bash
set -Eeuo pipefail

# easyconfig public bootstrap installer
# Usage:
#   curl -sSL https://get.dockercp.com/install.sh | sudo bash
#
# Optional environment variables:
#   EASYCONFIG_PACKAGE_URL
#   EASYCONFIG_INSTALL_DIR
#   EASYCONFIG_APP_URL
#   EASYCONFIG_DOMAIN
#   EASYCONFIG_ADMIN_NAME
#   EASYCONFIG_ADMIN_EMAIL
#   EASYCONFIG_ADMIN_PASSWORD
#   EASYCONFIG_DB_NAME
#   EASYCONFIG_DB_USER
#   EASYCONFIG_DB_PASSWORD
#   EASYCONFIG_OVERWRITE=1

EASYCONFIG_VERSION="1.0.0"
PACKAGE_URL="${EASYCONFIG_PACKAGE_URL:-https://get.dockercp.com/releases/easyconfig-latest.zip}"
INSTALL_DIR="${EASYCONFIG_INSTALL_DIR:-/var/www/easyconfig}"
DB_NAME="${EASYCONFIG_DB_NAME:-easyconfig}"
DB_USER="${EASYCONFIG_DB_USER:-easyconfig_user}"
DB_PASSWORD="${EASYCONFIG_DB_PASSWORD:-}"
ADMIN_NAME="${EASYCONFIG_ADMIN_NAME:-easyconfig Admin}"
ADMIN_EMAIL="${EASYCONFIG_ADMIN_EMAIL:-}"
ADMIN_PASSWORD="${EASYCONFIG_ADMIN_PASSWORD:-}"
APP_URL="${EASYCONFIG_APP_URL:-}"
DOMAIN="${EASYCONFIG_DOMAIN:-}"
OVERWRITE="${EASYCONFIG_OVERWRITE:-0}"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log() { printf "%b\n" "${BLUE}easyconfig:${NC} $*"; }
ok() { printf "%b\n" "${GREEN}✓${NC} $*"; }
warn() { printf "%b\n" "${YELLOW}!${NC} $*"; }
die() { printf "%b\n" "${RED}Error:${NC} $*" >&2; exit 1; }

cleanup() {
    if [[ -n "${TMP_DIR:-}" && -d "${TMP_DIR:-}" ]]; then
        rm -rf "$TMP_DIR"
    fi
}
trap cleanup EXIT

run() {
    log "$*"
    "$@"
}

require_root() {
    if [[ "${EUID}" -ne 0 ]]; then
        die "Please run this installer as root or with sudo."
    fi
}

detect_os() {
    if [[ -f /etc/os-release ]]; then
        # shellcheck disable=SC1091
        source /etc/os-release
        OS_ID="${ID:-unknown}"
        OS_LIKE="${ID_LIKE:-}"
    else
        die "Unable to detect operating system."
    fi

    case "$OS_ID" in
        ubuntu|debian)
            PKG_FAMILY="apt"
            ;;
        almalinux|rocky|centos|rhel|fedora)
            PKG_FAMILY="dnf"
            ;;
        *)
            if [[ "$OS_LIKE" == *"debian"* ]]; then
                PKG_FAMILY="apt"
            elif [[ "$OS_LIKE" == *"rhel"* || "$OS_LIKE" == *"fedora"* ]]; then
                PKG_FAMILY="dnf"
            else
                die "Unsupported OS: ${OS_ID}. Supported: Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS/RHEL compatible systems."
            fi
            ;;
    esac

    ok "Detected OS: ${PRETTY_NAME:-$OS_ID} (${PKG_FAMILY})"
}

ask_value() {
    local var_name="$1"
    local prompt="$2"
    local default_value="${3:-}"
    local secret="${4:-0}"
    local current_value="${!var_name:-}"

    if [[ -n "$current_value" ]]; then
        return
    fi

    if [[ ! -e /dev/tty ]]; then
        if [[ -n "$default_value" ]]; then
            printf -v "$var_name" "%s" "$default_value"
            return
        fi

        die "Missing required value: ${var_name}. Re-run with ${var_name}=value before the command."
    fi

    if [[ "$secret" == "1" ]]; then
        while [[ -z "${!var_name:-}" ]]; do
            read -r -s -p "$prompt: " "$var_name" < /dev/tty
            printf "\n" > /dev/tty
        done
    else
        if [[ -n "$default_value" ]]; then
            read -r -p "$prompt [$default_value]: " "$var_name" < /dev/tty
            if [[ -z "${!var_name:-}" ]]; then
                printf -v "$var_name" "%s" "$default_value"
            fi
        else
            while [[ -z "${!var_name:-}" ]]; do
                read -r -p "$prompt: " "$var_name" < /dev/tty
            done
        fi
    fi
}

random_password() {
    tr -dc 'A-Za-z0-9_@%+=.-' < /dev/urandom | head -c 24 || true
}

server_ip() {
    local ip
    ip="$(curl -fsSL --max-time 5 https://api.ipify.org 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}' || true)"
    printf "%s" "${ip:-127.0.0.1}"
}

install_packages_apt() {
    export DEBIAN_FRONTEND=noninteractive

    run apt-get update -y
    run apt-get install -y \
        ca-certificates curl wget unzip tar git rsync gnupg lsb-release software-properties-common \
        nginx mariadb-server \
        php-cli php-fpm php-mysql php-mbstring php-curl php-xml php-zip php-gd php-intl php-bcmath php-common
}

install_packages_dnf() {
    run dnf install -y dnf-plugins-core epel-release || true
    run dnf install -y \
        ca-certificates curl wget unzip tar git rsync nginx mariadb-server \
        php php-cli php-fpm php-mysqlnd php-mbstring php-curl php-xml php-zip php-gd php-intl php-bcmath php-common
}

install_system_packages() {
    if [[ "$PKG_FAMILY" == "apt" ]]; then
        install_packages_apt
    else
        install_packages_dnf
    fi

    systemctl enable --now mariadb >/dev/null 2>&1 || systemctl enable --now mysql >/dev/null 2>&1 || true
    systemctl enable --now nginx >/dev/null 2>&1 || true
    systemctl enable --now php-fpm >/dev/null 2>&1 || true

    ok "System packages installed"
}

install_docker() {
    if command -v docker >/dev/null 2>&1; then
        ok "Docker CLI is already installed"
    else
        log "Installing Docker"
        curl -fsSL https://get.docker.com -o /tmp/easyconfig-get-docker.sh
        sh /tmp/easyconfig-get-docker.sh
        rm -f /tmp/easyconfig-get-docker.sh
    fi

    systemctl enable --now docker >/dev/null 2>&1 || true

    if docker compose version >/dev/null 2>&1; then
        ok "Docker Compose plugin is available"
    elif command -v docker-compose >/dev/null 2>&1; then
        ok "Docker Compose standalone is available"
    else
        warn "Docker Compose plugin was not detected after Docker installation. Install docker-compose-plugin if your OS did not install it automatically."
    fi

    docker network create easyconfig-public >/dev/null 2>&1 || true
}

prepare_values() {
    if [[ -z "$DB_PASSWORD" ]]; then
        DB_PASSWORD="$(random_password)"
    fi

    if [[ -z "$APP_URL" ]]; then
        if [[ -n "$DOMAIN" ]]; then
            APP_URL="http://${DOMAIN}"
        else
            APP_URL="http://$(server_ip)"
        fi
    fi

    ask_value "ADMIN_EMAIL" "Admin email"
    ask_value "ADMIN_PASSWORD" "Admin password" "" "1"
    ask_value "ADMIN_NAME" "Admin name" "$ADMIN_NAME"

    ok "Installation URL: $APP_URL"
}

create_database() {
    log "Creating database and database user"

    mysql -u root <<SQL
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
ALTER USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
SQL

    ok "Database ready: ${DB_NAME}"
}

download_release() {
    TMP_DIR="$(mktemp -d)"
    PACKAGE_FILE="${TMP_DIR}/easyconfig.zip"
    EXTRACT_DIR="${TMP_DIR}/extract"

    log "Downloading easyconfig package from ${PACKAGE_URL}"
    curl -fL "$PACKAGE_URL" -o "$PACKAGE_FILE"

    mkdir -p "$EXTRACT_DIR"
    unzip -q "$PACKAGE_FILE" -d "$EXTRACT_DIR"

    SOURCE_DIR="$(find "$EXTRACT_DIR" -type f -path "*/install/cli-install.php" -printf '%h\n' | sed 's#/install$##' | head -n 1)"

    if [[ -z "${SOURCE_DIR:-}" || ! -d "$SOURCE_DIR" ]]; then
        die "Downloaded package does not contain install/cli-install.php. Check EASYCONFIG_PACKAGE_URL."
    fi

    ok "Package downloaded and verified"
}

install_files() {
    if [[ -d "$INSTALL_DIR" && "$(find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)" -gt 0 && "$OVERWRITE" != "1" ]]; then
        die "Install directory is not empty: $INSTALL_DIR. Re-run with EASYCONFIG_OVERWRITE=1 to replace it."
    fi

    mkdir -p "$INSTALL_DIR"
    rsync -a --delete "$SOURCE_DIR"/ "$INSTALL_DIR"/

    chown -R www-data:www-data "$INSTALL_DIR" 2>/dev/null || chown -R nginx:nginx "$INSTALL_DIR" 2>/dev/null || true
    chmod -R 755 "$INSTALL_DIR"

    mkdir -p \
        "$INSTALL_DIR/storage" \
        "$INSTALL_DIR/storage/logs" \
        "$INSTALL_DIR/storage/cache" \
        "$INSTALL_DIR/storage/sessions" \
        "$INSTALL_DIR/storage/uploads" \
        "$INSTALL_DIR/storage/uploads/logos" \
        "$INSTALL_DIR/storage/uploads/blog" \
        "$INSTALL_DIR/storage/backups" \
        "$INSTALL_DIR/storage/snapshots"

    chmod -R 775 "$INSTALL_DIR/storage"
    chown -R www-data:www-data "$INSTALL_DIR/storage" 2>/dev/null || chown -R nginx:nginx "$INSTALL_DIR/storage" 2>/dev/null || true

    ok "Application files installed at $INSTALL_DIR"
}

run_application_installer() {
    local database_sql_path="install/database/database.sql"

    if [[ ! -f "$INSTALL_DIR/$database_sql_path" ]]; then
        database_sql_path="database.sql"
    fi

    if [[ ! -f "$INSTALL_DIR/$database_sql_path" ]]; then
        die "database.sql was not found in the installed package."
    fi

    log "Running easyconfig application installer"

    cd "$INSTALL_DIR"

    php install/cli-install.php \
        --app-url="${APP_URL}" \
        --db-host="localhost" \
        --db-port="3306" \
        --db-database="${DB_NAME}" \
        --db-username="${DB_USER}" \
        --db-password="${DB_PASSWORD}" \
        --admin-name="${ADMIN_NAME}" \
        --admin-email="${ADMIN_EMAIL}" \
        --admin-password="${ADMIN_PASSWORD}" \
        --admin-timezone="UTC" \
        --database-sql-path="${database_sql_path}"

    {
        echo ""
        echo "EASYCONFIG_DEFAULT_PLAN=free"
        echo "EASYCONFIG_INSTALL_CHANNEL=bootstrap"
        echo "EASYCONFIG_FREE_PLAN_ENABLED=true"
    } >> "$INSTALL_DIR/.env"

    mysql -u"${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" -e \
        "UPDATE pricing_plans SET is_active = 1 WHERE LOWER(name) = 'free' OR slug = 'free';" >/dev/null 2>&1 || true

    ok "Application installer completed"
}

detect_php_fpm_socket() {
    local socket

    socket="$(find /run/php -name 'php*-fpm.sock' 2>/dev/null | sort -V | tail -n 1 || true)"

    if [[ -n "$socket" ]]; then
        PHP_FPM_TARGET="unix:${socket}"
        return
    fi

    PHP_FPM_TARGET="127.0.0.1:9000"
}

configure_nginx() {
    detect_php_fpm_socket

    local server_name="_"
    if [[ -n "$DOMAIN" ]]; then
        server_name="$DOMAIN"
    fi

    cat > /etc/nginx/conf.d/dockercp.comnf <<NGINX
server {
    listen 80;
    server_name ${server_name};

    root ${INSTALL_DIR};
    index index.php index.html;

    client_max_body_size 100M;

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

    location ~ \.php\$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT \$document_root;
        fastcgi_pass ${PHP_FPM_TARGET};
    }

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

    location ~* /(\.env|storage/installed\.lock|composer\.(json|lock)|package(-lock)?\.json)$ {
        deny all;
    }

    location ^~ /storage/ {
        deny all;
    }

    location ^~ /install/database/ {
        deny all;
    }
}
NGINX

    nginx -t
    systemctl reload nginx

    ok "Nginx configured"
}

print_success() {
    cat <<EOF

${GREEN}easyconfig installation completed successfully.${NC}

Login URL:
  ${APP_URL}/app/auth/login

Admin email:
  ${ADMIN_EMAIL}

Install directory:
  ${INSTALL_DIR}

Plan:
  Free plan enabled

Important:
  Point your domain DNS to this server IP before enabling SSL.
  Keep /storage and /.env protected from public access.

EOF
}

main() {
    require_root

    cat <<EOF
============================================================
 easyconfig Server Control Panel Installer
 Version: ${EASYCONFIG_VERSION}
============================================================
EOF

    detect_os
    prepare_values
    install_system_packages
    install_docker
    create_database
    download_release
    install_files
    run_application_installer
    configure_nginx
    print_success
}

main "$@"
