#!/usr/bin/env bash

# This script must run as root
user=$(whoami)
if [ $user != "root" ]; then
  echo "Not root user, please re-run as root"
  exit 1
fi

# Optional flags
#   --encrypt-docker DEV   Provision a LUKS2 volume on DEV; mount /docker on it.
#                          A 32-char passphrase is generated and printed once.
#                          DEV must be empty unless --force is also given.
#   --force                Wipe existing filesystem signatures on DEV.
ENCRYPT_DOCKER_DEVICE=""
FORCE_ENCRYPT=0
while [ $# -gt 0 ]; do
    case "$1" in
        --encrypt-docker)
            if [ -z "${2:-}" ]; then
                echo "ERROR: --encrypt-docker requires a device argument (e.g. /dev/sdb)" >&2
                exit 1
            fi
            ENCRYPT_DOCKER_DEVICE="$2"
            shift 2
            ;;
        --force)
            FORCE_ENCRYPT=1
            shift
            ;;
        -h|--help)
            echo "Usage: $0 [--encrypt-docker /dev/<device>] [--force]"
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            echo "Usage: $0 [--encrypt-docker /dev/<device>] [--force]" >&2
            exit 1
            ;;
    esac
done

# Check if WHP is already installed
if [ -f /etc/whp-version ] || [ -f /root/whp/VERSION ] || [ -d /docker/whp ]; then
    echo "Error: WHP appears to be already installed."
    
    # Try to get current version
    if [ -f /etc/whp-version ]; then
        echo "Current version: $(cat /etc/whp-version)"
    elif [ -f /root/whp/VERSION ]; then
        echo "Current version: $(cat /root/whp/VERSION)"
    else
        echo "Current version: unknown"
    fi
    
    echo ""
    echo "To update an existing installation, please run:"
    echo "  cd /root/whp && ./scripts/download-update.sh"
    echo ""
    echo "Or manually download the latest update from:"
    echo "  https://whp-releases.cloud-hosting.io"
    echo ""
    echo "If you're sure you want to reinstall, remove these first:"
    echo "  rm -f /etc/whp-version"
    echo "  rm -rf /docker/whp"
    echo "  rm -rf /root/whp"
    exit 1
fi

# Give people a chance to back out
echo "Getting ready to install the control panel, waiting 10 seconds"
sleep 10
# Do initial setup and install Docker
setenforce 0
sed -i -e '/SELINUX=/ s/=.*/=disabled/' /etc/selinux/config
dnf install -y qemu-guest-agent git yum-utils rsync wget curl tar zip whois sysstat which cryptsetup sqlite openssl
dnf install -y epel-release
dnf config-manager --set-enabled crb
dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
dnf install ffmpeg ffmpeg-devel -y
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11
dnf install -y mariadb
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable qemu-guest-agent

# ===== LUKS data-drive provisioning (opt-in via --encrypt-docker) =====
# Runs BEFORE Docker daemon starts and BEFORE any /docker subdirectories are
# created — so once /docker is mounted on the encrypted volume, everything
# else lands on it. With no --encrypt-docker flag this whole block is a no-op
# and the existing non-LUKS flow continues unchanged.
if [ -n "$ENCRYPT_DOCKER_DEVICE" ]; then
    echo
    echo ">>> Provisioning LUKS2 on $ENCRYPT_DOCKER_DEVICE"

    # Pre-flight: refuse to run if previous LUKS state exists (failed earlier run)
    if [ -f /etc/whp/luks.conf ]; then
        echo "ERROR: /etc/whp/luks.conf already exists (previous attempt or already provisioned)." >&2
        echo "  If a prior attempt failed, clean up manually before re-running:" >&2
        echo "    umount /docker; cryptsetup close docker-crypt" >&2
        echo "    rm -f /etc/whp/luks.conf /etc/whp/luks-header.backup" >&2
        echo "    rm -f /root/luks-passphrase-DELETE-AFTER-SAVING.txt" >&2
        echo "    sed -i '/^docker-crypt /d' /etc/crypttab" >&2
        echo "    sed -i '/[[:space:]]\\/docker[[:space:]]/d' /etc/fstab" >&2
        echo "    rm -f /etc/systemd/system/docker.service.d/whp-luks-mount-required.conf" >&2
        exit 1
    fi
    if [ ! -b "$ENCRYPT_DOCKER_DEVICE" ]; then
        echo "ERROR: $ENCRYPT_DOCKER_DEVICE is not a block device." >&2
        exit 1
    fi
    if findmnt -no SOURCE "$ENCRYPT_DOCKER_DEVICE" >/dev/null 2>&1; then
        echo "ERROR: $ENCRYPT_DOCKER_DEVICE is currently mounted. Unmount first." >&2
        exit 1
    fi
    # Refuse to wipe the boot disk under any circumstance
    BOOT_SRC=$(findmnt -no SOURCE / 2>/dev/null)
    BOOT_DISK=$(lsblk -no PKNAME "$BOOT_SRC" 2>/dev/null || true)
    if [ -n "$BOOT_DISK" ] && [ "/dev/$BOOT_DISK" = "$ENCRYPT_DOCKER_DEVICE" ]; then
        echo "ERROR: $ENCRYPT_DOCKER_DEVICE is the boot disk. Refusing to encrypt." >&2
        exit 1
    fi
    # Refuse to wipe a device with existing data unless --force
    if wipefs -n "$ENCRYPT_DOCKER_DEVICE" 2>/dev/null | grep -q .; then
        if [ "$FORCE_ENCRYPT" != "1" ]; then
            echo "ERROR: $ENCRYPT_DOCKER_DEVICE has existing filesystem/partition signatures:" >&2
            wipefs -n "$ENCRYPT_DOCKER_DEVICE" >&2
            echo "  Re-run with --force to wipe these and proceed." >&2
            exit 1
        fi
        echo "  --force given; wiping existing signatures on $ENCRYPT_DOCKER_DEVICE..."
        wipefs -af "$ENCRYPT_DOCKER_DEVICE"
    fi

    # Generate a 32-char hex passphrase (128 bits of entropy; LUKS re-keys to 512 internally).
    # Use /dev/urandom + od since AL10 minimal install doesn't have openssl yet.
    LUKS_PASSPHRASE=$(od -An -tx1 -N16 /dev/urandom | tr -d ' \n')
    if [ ${#LUKS_PASSPHRASE} -ne 32 ]; then
        echo "ERROR: passphrase generation failed (got ${#LUKS_PASSPHRASE} chars, expected 32)." >&2
        exit 1
    fi

    # Wrap the actual provisioning in a subshell with set -e so any failure
    # aborts cleanly. The handler below rolls back partial state so re-run
    # is possible after the admin fixes whatever caused the failure.
    if ! (
        set -e
        echo "  Formatting LUKS2..."
        printf '%s' "$LUKS_PASSPHRASE" | cryptsetup luksFormat \
            --batch-mode --type luks2 --key-file=- "$ENCRYPT_DOCKER_DEVICE"

        echo "  Opening as /dev/mapper/docker-crypt..."
        printf '%s' "$LUKS_PASSPHRASE" | cryptsetup open \
            --type luks2 --key-file=- "$ENCRYPT_DOCKER_DEVICE" docker-crypt

        echo "  Creating ext4 filesystem..."
        mkfs.ext4 -L docker-data -q /dev/mapper/docker-crypt

        echo "  Mounting on /docker..."
        mkdir -p /docker
        mount /dev/mapper/docker-crypt /docker

        # /etc/crypttab — UUID-based, noauto so systemd never auto-unlocks at boot
        LUKS_DEV_UUID=$(cryptsetup luksUUID "$ENCRYPT_DOCKER_DEVICE")
        [ -n "$LUKS_DEV_UUID" ] || { echo "ERROR: cryptsetup luksUUID returned empty"; exit 1; }
        echo "docker-crypt UUID=$LUKS_DEV_UUID none noauto,luks" >> /etc/crypttab

        # /etc/fstab — by-UUID of the mapper's filesystem, noauto+nofail
        FS_UUID=$(blkid -s UUID -o value /dev/mapper/docker-crypt)
        [ -n "$FS_UUID" ] || { echo "ERROR: blkid returned empty FS UUID"; exit 1; }
        echo "UUID=$FS_UUID /docker ext4 defaults,noauto,nofail 0 0" >> /etc/fstab

        # docker.service drop-in: refuse to start if /docker isn't mounted
        mkdir -p /etc/systemd/system/docker.service.d
        cp ./configs/docker.service.d-luks.conf \
            /etc/systemd/system/docker.service.d/whp-luks-mount-required.conf
        systemctl daemon-reload

        # Locked-state unlock daemon: standalone Python HTTPS server on port
        # 8444, running independently of Apache. Reuses Apache's SSL cert.
        # Decoupled by design — if Apache fails to start for any reason
        # (LUKS-related or not), unlock still works.
        dnf install -y python3-pam
        mkdir -p /var/lib/whp/locked-unlock-rate-limit
        chmod 0700 /var/lib/whp/locked-unlock-rate-limit
        cp ./configs/whp-locked-unlock.service /etc/systemd/system/
        systemctl daemon-reload
        # Just enable — DON'T start. Apache's self-signed cert is created
        # later in setup.sh (openssl req for localhost.crt). The daemon
        # needs that cert (or a real /etc/certs/<fqdn>.pem) to bind SSL.
        # systemd will start it on the post-setup reboot when the cert
        # exists. Admin can also start it manually before reboot if needed.
        systemctl enable whp-locked-unlock.service
        # Open firewalld port for the unlock daemon (panel admin URL).
        if systemctl is-active firewalld >/dev/null 2>&1; then
            firewall-cmd --permanent --add-port=8444/tcp >/dev/null 2>&1 || true
            firewall-cmd --reload >/dev/null 2>&1 || true
        fi

        # /etc/whp/luks.conf — single source of truth, read by panel + scripts.
        # Written LAST: presence of this file is the "successfully provisioned" marker.
        mkdir -p /etc/whp
        PROVISIONED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)

        # Header backup — required to recover from header corruption
        cryptsetup luksHeaderBackup "$ENCRYPT_DOCKER_DEVICE" \
            --header-backup-file /etc/whp/luks-header.backup
        chmod 0600 /etc/whp/luks-header.backup

        # Forcing function: write passphrase to a file admin must rm after saving
        cat > /root/luks-passphrase-DELETE-AFTER-SAVING.txt <<TXT
WHP LUKS data-drive passphrase for $ENCRYPT_DOCKER_DEVICE
Generated $PROVISIONED_AT

$LUKS_PASSPHRASE

Save this in your password manager NOW. There is NO recovery if lost.
Delete this file once stored safely:
  rm /root/luks-passphrase-DELETE-AFTER-SAVING.txt
TXT
        chmod 0600 /root/luks-passphrase-DELETE-AFTER-SAVING.txt

        cat > /etc/whp/luks.conf <<CONF
# WHP LUKS data-drive configuration.
# Created by setup.sh --encrypt-docker on $PROVISIONED_AT.
# DO NOT EDIT BY HAND.

LUKS_ENABLED=1
LUKS_BACKING_DEVICE=$ENCRYPT_DOCKER_DEVICE
LUKS_DEV_UUID=$LUKS_DEV_UUID
LUKS_MAPPER_NAME=docker-crypt
LUKS_MOUNT_POINT=/docker
LUKS_PROVISIONED_AT=$PROVISIONED_AT
CONF
        chmod 0600 /etc/whp/luks.conf
    ); then
        echo "ERROR: LUKS provisioning failed. Rolling back partial state..." >&2
        systemctl disable --now whp-locked-unlock.service 2>/dev/null || true
        umount /docker 2>/dev/null || true
        cryptsetup close docker-crypt 2>/dev/null || true
        rm -f /etc/whp/luks.conf /etc/whp/luks-header.backup
        rm -f /root/luks-passphrase-DELETE-AFTER-SAVING.txt
        rm -f /etc/systemd/system/docker.service.d/whp-luks-mount-required.conf
        rm -f /etc/systemd/system/whp-locked-unlock.service
        rm -rf /var/lib/whp/locked-unlock-rate-limit
        sed -i "/^docker-crypt /d" /etc/crypttab
        sed -i "/[[:space:]]\/docker[[:space:]]/d" /etc/fstab
        systemctl daemon-reload
        unset LUKS_PASSPHRASE
        exit 1
    fi

    # Loud banner — yellow on red, multi-line, hard to miss
    echo
    echo -e "\033[1;33;41m############################################################\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m##         LUKS DATA-DRIVE PASSPHRASE                     ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;37;41m##   $LUKS_PASSPHRASE   ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m##   >>  SAVE THIS NOW IN A PASSWORD MANAGER  <<          ##\033[0m"
    echo -e "\033[1;33;41m##   >>  LOSING IT MEANS LOSING THE DATA      <<          ##\033[0m"
    echo -e "\033[1;33;41m##   >>  NO RECOVERY EXISTS WITHOUT IT        <<          ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m##   Also saved to:                                       ##\033[0m"
    echo -e "\033[1;33;41m##     /root/luks-passphrase-DELETE-AFTER-SAVING.txt      ##\033[0m"
    echo -e "\033[1;33;41m##   Delete that file once stored externally.             ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m##   Header backup: /etc/whp/luks-header.backup           ##\033[0m"
    echo -e "\033[1;33;41m##     Ship off-host (back this up too — recovery needs it). ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m##   AFTER REBOOT — unlock via:                           ##\033[0m"
    echo -e "\033[1;33;41m##     https://$(hostname -I 2>/dev/null | awk '{print $1}'):8444/  (web)  ##\033[0m"
    echo -e "\033[1;33;41m##     ssh root@server /root/whp/scripts/whp-unlock-data-drive  (CLI)  ##\033[0m"
    echo -e "\033[1;33;41m##                                                        ##\033[0m"
    echo -e "\033[1;33;41m############################################################\033[0m"
    echo

    unset LUKS_PASSPHRASE

    echo ">>> LUKS provisioning complete. /docker is on /dev/mapper/docker-crypt."
    echo
fi
# ===== end LUKS block =====

# Configure Docker to use a different data root directory instead of bind mounting overlay2
mkdir -p /docker/docker-data
# Create Docker daemon configuration
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << EOF
{
  "data-root": "/docker/docker-data"
}
EOF
systemctl daemon-reload
systemctl enable --now docker
dnf update -y
mkdir -p /docker/{users,mysql}
mkdir -p /docker/whp/{haproxy,ssl,tasks,sql,backups,web}
# Add WHP user
adduser -M -d "/docker/whp"  -c "Webhosting Panel User" whp
chown -R whp:whp "/docker/whp"
usermod -aG docker whp
# Install WHP Elements
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(rpm -E %rhel).noarch.rpm -y
dnf install -y https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
dnf install -y httpd mod_ssl wget procps
# Defensive: ensure openssl CLI is present before generating the localhost cert.
# mod_ssl pulls in openssl-libs but not necessarily the openssl CLI on AL10.
if ! command -v openssl >/dev/null 2>&1; then
    dnf install -y openssl >/dev/null
fi
openssl req -newkey rsa:2048 -nodes -keyout /etc/pki/tls/private/localhost.key -x509 -days 3650 -subj "/CN=localhost" -out /etc/pki/tls/certs/localhost.crt
dnf module enable php:remi-8.2 -y
dnf install -y php php-fpm php-mysqlnd php-pgsql php-xml php-pecl-zip php-sodium php-soap php-pecl-xmlrpc \
php-pecl-redis5 php-pecl-memcached php-pecl-memcache php-pecl-ip2location php-pecl-imagick php-pecl-geoip \
php-mysqlnd php-mbstring php-intl php-gd libzip php-cli php-pecl-pam

# Allow the local PHP as root via a systemd drop-in so dnf updates can't clobber it
mkdir -p /etc/systemd/system/php-fpm.service.d
cat > /etc/systemd/system/php-fpm.service.d/override.conf <<'EOF'
[Service]
ExecStart=
ExecStart=/usr/sbin/php-fpm -R --nodaemonize
User=root
Group=root
EOF
systemctl daemon-reload
# Clean up default Files
rm -f /etc/httpd/conf/httpd.conf
# Empty (don't delete) www.conf so %config(noreplace) keeps it empty across dnf updates
: > /etc/php-fpm.d/www.conf
rm -f /etc/httpd/conf.d/ssl.conf
# Add default Configs
cp ./configs/httpd.conf /etc/httpd/conf/
cp ./configs/php-fpm-whp.conf /etc/php-fpm.d/
cp ./configs/ssl.conf /etc/httpd/conf.d/
cp ./configs/php.pam /etc/pam.d/php

# Install sudoers configuration for whp user
cp ./configs/whp-sudoers /etc/sudoers.d/whp
chmod 440 /etc/sudoers.d/whp

# Create private network for namebased resolution
docker network create --driver bridge client-net
docker_host_ip=$(docker network inspect client-net |grep Subnet|awk -F '"' {'print $4'})
echo "RemoteIPHeader X-Forward-For" > /etc/httpd/conf.modules.d/remoteip.conf 
echo "RemoteIPTrustedProxy $docker_host_ip" >> /etc/httpd/conf.modules.d/remoteip.conf 

systemctl enable --now httpd
sleep 5
systemctl enable --now php-fpm

# Copy Web Files (including dot files)
rsync -auzvp ./web-files/ /docker/whp/web/

# Copy all release files to /root/whp/ for script access
echo "Copying release files to /root/whp/..."
mkdir -p /root/whp
rsync -auzvp --exclude='web-files/' ./ /root/whp/
echo "✓ Release files copied to /root/whp/"

# Download Adminer for database management
echo "Installing Adminer for database management..."
if ! wget -q -O /docker/whp/web/adminer.php "https://www.adminer.org/latest.php"; then
    echo "⚠ Failed to download latest Adminer, trying alternative URL..."
    if ! wget -q -O /docker/whp/web/adminer.php "https://github.com/vrana/adminer/releases/download/v4.8.1/adminer-4.8.1.php"; then
        echo "⚠ Failed to download Adminer - database management interface may not work"
    else
        echo "✓ Adminer downloaded successfully (fallback version)"
    fi
else
    echo "✓ Adminer downloaded successfully (latest version)"
fi

# Install ProFTPD package (configuration happens after database setup)
echo "Installing ProFTPD..."
dnf install -y proftpd proftpd-mysql

# Create ProFTPD system user (without password prompt)
useradd -r -d /var/lib/proftpd -s /bin/false --system proftpd 2>/dev/null || true

echo "✓ ProFTPD package installed (configuration will happen after database setup)"

# Install rclone for backup uploads
echo "Installing rclone for backup uploads..."
curl -sL https://rclone.org/install.sh | bash
if command -v rclone &>/dev/null; then
    echo "✓ rclone installed successfully"
else
    echo "⚠ rclone installation failed - backup uploads will use PHP fallback"
fi

# Enable Ports in the server
firewall-cmd --permanent --add-port 22/tcp
firewall-cmd --permanent --add-port 53/tcp
firewall-cmd --permanent --add-port 53/udp
firewall-cmd --permanent --add-port 80/tcp
firewall-cmd --permanent --add-port 443/tcp
firewall-cmd --permanent --add-port 8080/tcp
firewall-cmd --permanent --add-port 8081/tcp
firewall-cmd --permanent --add-port 8123/tcp
firewall-cmd --permanent --add-port 8443/tcp
firewall-cmd --permanent --add-port 9393/tcp
# FTP ports
firewall-cmd --permanent --add-port 21/tcp
firewall-cmd --permanent --add-port 60000-60100/tcp
firewall-cmd --reload

#Pull the Docker Images
# System-level images that aren't tracked in container_types and must be
# pulled before their respective `docker run` lines further down.
docker pull repo.anhonesthost.net/cloud-hosting-platform/cmc:latest
docker pull repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest

# NOTE: customer-site images (cac, cac-fpm, whp-default, cnoc) are pulled
# AFTER the WHP database is seeded — see the dynamic pull loop below the
# "Canonical schema applied" line. The DB's container_types table is the
# single source of truth for which images a new server should ship with,
# so adding PHP 8.6 (or Node 24, or any other variant) is a two-step
# change — push the image to the registry, then add a seed migration row
# for it — without touching setup.sh.

# pull MariaDB image
docker pull mariadb:10.11
# Generate root MySQL Password
mysql_password=$(tr -dc 'A-Za-z0-9!?&()-+=' </dev/urandom | head -c 18)
# Start MariaDB (bind to localhost for host access, accessible via container name within Docker network)
docker run -d --network client-net --name mysql --env MARIADB_ROOT_PASSWORD="$mysql_password" -v /docker/mysql:/var/lib/mysql:Z -p 127.0.0.1:3306:3306 --restart unless-stopped mariadb:10.11
# Store mysql login details (use localhost for host-based connections)
echo "[client]" > /root/.my.cnf
echo "host=127.0.0.1" >> /root/.my.cnf
echo "user=root" >> /root/.my.cnf
echo "password=$mysql_password" >> /root/.my.cnf

# Pull PostgreSQL image
docker pull postgres:latest
# Generate PostgreSQL root password
postgres_password=$(tr -dc 'A-Za-z0-9!?&()-+=' </dev/urandom | head -c 18)
# Create PostgreSQL data directory
mkdir -p /docker/postgresql
# Start PostgreSQL (bind to localhost for host access, accessible via container name within Docker network)
docker run -d --network client-net --name postgresql --env POSTGRES_PASSWORD="$postgres_password" -v /docker/postgresql:/var/lib/postgresql:Z -p 127.0.0.1:5432:5432 --restart unless-stopped postgres:latest
# Store PostgreSQL login details in .pgpass format (use localhost for host-based connections)
echo "127.0.0.1:5432:*:postgres:$postgres_password" > /root/.pgpass
chmod 600 /root/.pgpass

# Wait for MySQL to be ready
echo "Waiting for MySQL to be ready..."
while ! mysql -e "SELECT 1" >/dev/null 2>&1; do
    echo "MySQL is not ready yet, waiting 5 seconds..."
    sleep 5
done
echo "✓ MySQL is ready!"

# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to be ready..."
while ! docker exec postgresql pg_isready -U postgres >/dev/null 2>&1; do
    echo "PostgreSQL is not ready yet, waiting 5 seconds..."
    sleep 5
done
echo "✓ PostgreSQL is ready!"

# Initialize database schemas directly with MySQL
echo "Setting up complete WHP database structure..."
mysql -e "CREATE DATABASE IF NOT EXISTS whp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql < ./sql/complete-database-setup.sql
if [ $? -eq 0 ]; then
    echo "✓ Database setup completed successfully"
    
    # Verify critical tables were created
    echo "Verifying database structure..."
    tables_check=$(mysql -e "USE whp; SHOW TABLES;" 2>/dev/null | wc -l)
    if [ $tables_check -gt 5 ]; then
        echo "✓ Database tables created successfully ($((tables_check-1)) tables)"
    else
        echo "⚠ Warning: Expected more tables, only found $((tables_check-1))"
    fi
else
    echo "✗ Database setup failed"
    exit 1
fi

# Set up phpMyAdmin configuration storage
echo "Setting up phpMyAdmin configuration storage..."
mysql < ./web-files/phpMyAdmin/sql/create_tables.sql
if [ $? -eq 0 ]; then
    echo "✓ phpMyAdmin configuration storage created"
else
    echo "⚠ phpMyAdmin setup failed - basic functionality will still work"
fi

# Create the `pma` controluser that config.inc.php expects, and drop the
# random password at /etc/whp/pma-password (mode 0600 root:root). Without
# this, phpMyAdmin shows the "configuration storage is not completely
# configured" notice on every page even though the schema is in place.
# Idempotent: ALTER USER if it exists, CREATE if not; GRANT is a no-op if
# already granted.
echo "Setting up phpMyAdmin controluser..."
pma_password=$(openssl rand -hex 16)
mkdir -p /etc/whp
printf '%s' "$pma_password" > /etc/whp/pma-password
chmod 600 /etc/whp/pma-password
chown root:root /etc/whp/pma-password
mysql -e "
    CREATE USER IF NOT EXISTS 'pma'@'%' IDENTIFIED BY '$pma_password';
    ALTER USER 'pma'@'%' IDENTIFIED BY '$pma_password';
    GRANT SELECT, INSERT, UPDATE, DELETE ON \`phpmyadmin\`.* TO 'pma'@'%';
    FLUSH PRIVILEGES;
" && echo "✓ phpMyAdmin controluser created (password in /etc/whp/pma-password)" \
  || echo "⚠ phpMyAdmin controluser setup failed — alert will still show in the UI"
unset pma_password

# complete-database-setup.sql is the canonical schema -- it represents the
# end-state of all historical migrations applied in order. Fresh installs
# only need this one file. Migrations are kept around in sql/migrations/
# so existing servers can upgrade through update.sh, but a new install
# does not replay them (which used to fail on long histories with
# collation mismatches and missing prerequisite tables).
#
# Adding new schema: write a migration in sql/migrations/staging/ AND
# update sql/complete-database-setup.sql to reflect the final state.
# scripts/regenerate-canonical-schema.sh does this for you. build-release
# runs a lint check that fails the build when the two diverge.
echo "✓ Canonical schema applied (no migration replay needed for fresh install)."

# Pre-pull every customer-site image registered in container_types so a new
# server can provision sites without waiting on a first-use registry round-
# trip. Sourcing the image list from the DB means new images (e.g. cac:php86
# when PHP 8.6 ships, or cnoc:node24) are picked up automatically — push the
# image to the registry, add a seed migration row, and the next fresh install
# pulls it. No hand-maintained list to drift.
#
# Pulls that fail are logged but don't abort the install — older PHP tags
# can be deregistered from the registry while still being valid container_type
# rows for migration compatibility, and a transient registry outage shouldn't
# block server setup. A site whose image is missing will surface the error
# at panel/import time, where the importer's image-miss fallback kicks in.
echo ""
echo "Pre-pulling customer-site container images registered in container_types..."
echo "============================================================================"
ct_pull_ok=0
ct_pull_fail=0
while IFS= read -r ct_image; do
    [ -z "$ct_image" ] && continue
    echo "  pulling $ct_image..."
    if docker pull "$ct_image" >/dev/null 2>&1; then
        echo "    ✓"
        ct_pull_ok=$((ct_pull_ok + 1))
    else
        echo "    ⚠ pull failed — image not in registry or transient error; continuing"
        ct_pull_fail=$((ct_pull_fail + 1))
    fi
done < <(mysql whp -Nse "SELECT base_image FROM container_types WHERE active = 1 ORDER BY id" 2>/dev/null)
echo "  → $ct_pull_ok pulled, $ct_pull_fail skipped"
echo ""

# Create security database directory in target location
mkdir -p /docker/whp/sql
chown -R whp:whp /docker/whp/sql

# Ensure sql directory exists in source
mkdir -p /root/whp/sql
chmod 750 /root/whp/sql

# Ensure all scripts are executable
echo "Setting executable permissions on scripts..."
chmod +x scripts/*.sh scripts/*.php 2>/dev/null || true

# Set up user quotas
echo "Setting up user quotas..."
./scripts/setup-quotas.sh setup -p "$mysql_password"
if [ $? -eq 0 ]; then
    echo "✓ User quota setup completed"
else
    echo "⚠ User quota setup failed - continuing without quotas"
fi

# Configure ProFTPD with MySQL integration (now that database is ready)
echo "Configuring ProFTPD with MySQL virtual users integration..."

# Configure ProFTPD with MySQL integration
cp ./configs/proftpd.conf /etc/proftpd.conf

# Create required directories
mkdir -p /var/log/proftpd
mkdir -p /etc/proftpd/conf.d
chown -R proftpd:proftpd /var/log/proftpd

# Create welcome messages
echo "Welcome to WHP FTP Server" > /etc/proftpd/welcome.msg
echo "Login successful. Files are located in your home directory." > /etc/proftpd/login.msg

# Determine which ProFTPD configuration to use
echo "Configuring FTP authentication method..."
echo "Options:"
echo "  1) PAM Authentication (recommended - integrates with system users and quotas)"
echo "  2) MySQL Authentication (database-driven virtual users)"
echo ""

# For automated setup, default to PAM (option 1)
# In interactive mode, you could prompt: read -p "Choose authentication method (1 or 2): " auth_method
auth_method=1

if [ "$auth_method" = "1" ]; then
    echo "✓ Using PAM authentication (integrates with system users)"
    cp ./configs/proftpd-pam.conf /etc/proftpd.conf
    
    # Get server IP for passive mode configuration  
    # For testing environments, prefer internal IP
    server_ip=""
    
    # Method 1: Try to get IP from main network interface (will be internal for testing)
    server_ip=$(ip route get 8.8.8.8 | awk '{print $7}' | head -n1 2>/dev/null)
    
    # Method 2: If that fails, try hostname -I
    if [ -z "$server_ip" ] || [ "$server_ip" = "127.0.0.1" ]; then
        server_ip=$(hostname -I | awk '{print $1}' 2>/dev/null)
    fi
    
    # For testing: prefer internal IPs, don't try to get external IP
    if [ -n "$server_ip" ]; then
        echo "✓ Configured FTP passive mode with IP: $server_ip (using internal IP for testing)"
        # Update the MasqueradeAddress in the config file
        sed -i "s/MasqueradeAddress.*192\.168\.1\.105/MasqueradeAddress               $server_ip/" /etc/proftpd.conf
    else
        # Fallback to default testing IP
        server_ip="192.168.1.105"
        echo "⚠ Using default testing IP: $server_ip"
    fi
    
    echo "✓ ProFTPD configured with PAM authentication - uses system users and existing quotas"
else
    echo "✓ Using MySQL authentication (database-driven virtual users)"
    cp ./configs/proftpd.conf /etc/proftpd.conf
    
    # Get server IP for passive mode configuration
    server_ip=$(ip route get 8.8.8.8 | awk '{print $7}' | head -n1 2>/dev/null)
    if [ -z "$server_ip" ] || [ "$server_ip" = "127.0.0.1" ]; then
        server_ip=$(hostname -I | awk '{print $1}' 2>/dev/null)
    fi
    
    if [ -n "$server_ip" ]; then
        # Replace placeholders in MySQL config
        sed -i "s|\$PUBLIC_IP|$server_ip|g" /etc/proftpd.conf
        echo "✓ Configured FTP passive mode with IP: $server_ip"
        
        # Set up MySQL authentication
        actual_mysql_password=$(grep '^password=' /root/.my.cnf | cut -d'=' -f2-)
        sed -i "s|\$MYSQL_PASSWORD|$actual_mysql_password|g" /etc/proftpd.conf
        
        # Create dedicated MySQL user for ProFTPD
        mysql -e "
        CREATE USER IF NOT EXISTS 'proftpd'@'localhost' IDENTIFIED BY 'proftpd_secure_pass';
        GRANT SELECT, INSERT, UPDATE, DELETE ON whp.ftp_users TO 'proftpd'@'localhost';
        GRANT SELECT, INSERT, UPDATE, DELETE ON whp.ftp_groups TO 'proftpd'@'localhost';
        GRANT SELECT, INSERT, UPDATE, DELETE ON whp.ftp_access_log TO 'proftpd'@'localhost';
        GRANT SELECT, INSERT, UPDATE, DELETE ON whp.ftp_quotas TO 'proftpd'@'localhost';
        FLUSH PRIVILEGES;
        "
        echo "✓ Created dedicated MySQL user for ProFTPD"
    else
        echo "⚠ Could not determine server IP - FTP passive mode may need manual configuration"
    fi
fi

# Set proper permissions
chmod 644 /etc/proftpd.conf
chmod 644 /etc/proftpd/welcome.msg
chmod 644 /etc/proftpd/login.msg

# Enable and start ProFTPD service
systemctl enable proftpd
systemctl start proftpd

echo "✓ ProFTPD configured with MySQL virtual users integration"

# Set up PowerDNS with secure password management
echo "Setting up PowerDNS DNS server..."
if [ -f "./scripts/setup-powerdns-enhanced.sh" ]; then
    chmod +x ./scripts/setup-powerdns-enhanced.sh
    ./scripts/setup-powerdns-enhanced.sh
    if [ $? -eq 0 ]; then
        echo "✓ PowerDNS setup completed successfully"
        
        # Make password manager executable
        if [ -f "./scripts/powerdns-password-manager.sh" ]; then
            chmod +x ./scripts/powerdns-password-manager.sh
            echo "✓ PowerDNS password manager configured"
        fi
        
        # Verify PowerDNS installation
        echo "Verifying PowerDNS installation..."
        if [ -f "./scripts/verify-powerdns.sh" ]; then
            chmod +x ./scripts/verify-powerdns.sh
            ./scripts/verify-powerdns.sh
            if [ $? -eq 0 ]; then
                echo "✓ PowerDNS verification completed successfully"
            else
                echo "⚠ PowerDNS verification failed - check logs for details"
            fi
        fi
    else
        echo "⚠ PowerDNS setup failed - continuing without DNS server"
    fi
else
    echo "⚠ PowerDNS setup script not found - skipping DNS server setup"
fi

#start HAPROXY Container
# Check if API key exists in settings
haproxy_api_key=""
if [ -f "/docker/whp/settings.json" ]; then
    haproxy_api_key=$(grep -o '"haproxy_api_key":"[^"]*"' /docker/whp/settings.json | cut -d'"' -f4 2>/dev/null || echo "")
fi

# Build environment variable string
api_key_env=""
if [ -n "$haproxy_api_key" ]; then
    api_key_env="-e HAPROXY_API_KEY=$haproxy_api_key "
fi

docker run -d --network=client-net --add-host=host.docker.internal:host-gateway \
    -p 80:80 -p 443:443 -p 8000:8000 \
    -v lets-encrypt:/etc/letsencrypt -v haproxy:/etc/haproxy \
    --memory=1g --memory-swap=2g \
    --health-cmd="curl -s -o /dev/null --max-time 10 http://localhost/ || exit 1" \
    --health-interval=30s \
    --health-start-period=60s \
    --health-timeout=10s \
    --health-retries=3 \
    $api_key_env--name haproxy-manager --restart unless-stopped \
    repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest

#start memcache Container
docker run -d --network=client-net --add-host=host.docker.internal:host-gateway -p 11211:11211  --restart unless-stopped --name memcache repo.anhonesthost.net/cloud-hosting-platform/cmc:latest

# Set up password encryption
echo ""
echo "Setting up password encryption..."
echo "================================="

# Generate encryption key for password storage. Belt-and-suspenders openssl
# install: AL10 minimal predates openssl in the base image and a prior version
# of this script silently wrote a zero-byte key when openssl was missing, so
# defend against both the missing-binary case AND a wedged empty-key state.
KEY_FILE="/etc/whp/encryption.key"
if ! command -v openssl >/dev/null 2>&1; then
    echo "  openssl CLI missing — installing"
    dnf install -y openssl >/dev/null
fi
# Re-generate if the file is missing OR present-but-too-short (a previous run
# without openssl can leave a 0-byte file that the simple -f check would skip).
if [ ! -s "$KEY_FILE" ] || [ "$(wc -c < "$KEY_FILE" 2>/dev/null || echo 0)" -lt 32 ]; then
    if [ -e "$KEY_FILE" ]; then
        echo "  existing encryption key looks invalid (too short or empty) — regenerating"
    fi
    mkdir -p /etc/whp
    NEW_KEY=$(openssl rand -base64 32)
    if [ -z "$NEW_KEY" ]; then
        echo "ERROR: openssl produced no output; encryption key NOT written." >&2
        exit 1
    fi
    echo "$NEW_KEY" > "$KEY_FILE"
    chmod 600 "$KEY_FILE"
    chown root:root "$KEY_FILE"
    echo "✓ Encryption key generated: $KEY_FILE"
    echo ""
    echo "⚠️  IMPORTANT: Save this encryption key securely!"
    echo "📋 Key: $NEW_KEY"
    echo ""
else
    echo "✓ Encryption key already exists"
fi

# Final verification
echo ""
echo "Performing final system verification..."
echo "======================================"

# Check service status
echo "Checking service status:"
systemctl is-active httpd >/dev/null 2>&1 && echo "✓ Apache is running" || echo "✗ Apache is not running"
systemctl is-active php-fpm >/dev/null 2>&1 && echo "✓ PHP-FPM is running" || echo "✗ PHP-FPM is not running"
systemctl is-active docker >/dev/null 2>&1 && echo "✓ Docker is running" || echo "✗ Docker is not running"
systemctl is-active proftpd >/dev/null 2>&1 && echo "✓ FTP server is running" || echo "✗ FTP server is not running"
systemctl is-enabled whp-backup-upload >/dev/null 2>&1 && echo "✓ Backup upload daemon is enabled" || echo "✗ Backup upload daemon is not enabled"
systemctl is-active haproxy-crash-monitor >/dev/null 2>&1 && echo "✓ HAProxy crash monitor is running" || echo "✗ HAProxy crash monitor is not running"

# Check container status
echo ""
echo "Checking container status:"
docker ps | grep mysql >/dev/null 2>&1 && echo "✓ MySQL container is running" || echo "✗ MySQL container is not running"
docker ps | grep postgresql >/dev/null 2>&1 && echo "✓ PostgreSQL container is running" || echo "✗ PostgreSQL container is not running"
docker ps | grep haproxy-manager >/dev/null 2>&1 && echo "✓ HAProxy container is running" || echo "✗ HAProxy container is not running"
docker ps | grep memcache >/dev/null 2>&1 && echo "✓ Memcache container is running" || echo "✗ Memcache container is not running"
docker ps | grep powerdns >/dev/null 2>&1 && echo "✓ PowerDNS container is running" || echo "✗ PowerDNS container is not running"

# Check database connectivity
echo ""
echo "Checking database connectivity:"
mysql -e "SELECT 1" >/dev/null 2>&1 && echo "✓ MySQL connection successful" || echo "✗ MySQL connection failed"
docker exec postgresql psql -U postgres -c "SELECT 1" >/dev/null 2>&1 && echo "✓ PostgreSQL connection successful" || echo "✗ PostgreSQL connection failed"

# Set up SSH chroot jail for users in /docker/users/
if ! grep -q 'Match User \*,!root' /etc/ssh/sshd_config; then
  cat << 'EOF' >> /etc/ssh/sshd_config

# WHP: Chroot all non-root users with home in /docker/users/ to their home directory and force SFTP
Match User *,!root
    ChrootDirectory /docker/users/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
EOF
  systemctl restart sshd
  echo "✓ SSH chroot jail for /docker/users/ configured and sshd restarted"
else
  echo "✓ SSH chroot jail already configured in sshd_config"
fi
mkdir -p /etc/certs

# Install KSM (Kernel Samepage Merging) systemd service
echo "Installing KSM (Kernel Samepage Merging) service..."
cp ./configs/ksm.systemd /etc/systemd/system/ksm.service
chmod 644 /etc/systemd/system/ksm.service
systemctl daemon-reload
systemctl enable ksm.service
systemctl start ksm.service
echo "✓ KSM service installed and started"

# Check web access
echo ""
echo "Checking web access:"
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q "200\|302" && echo "✓ Web interface accessible" || echo "✗ Web interface not accessible"

echo ""
echo "======================================"
# Setup backup system cron jobs
echo "Setting up backup system cron jobs..."
(crontab -l 2>/dev/null; echo "# WHP Backup System - Cleanup old backups daily at 2 AM") | crontab -
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/bin/php /root/whp/scripts/cleanup-backups.php >/dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# WHP Backup System - Process scheduled backups every 5 minutes") | crontab -
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/bin/php /root/whp/scripts/backup-scheduler.php >/dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# WHP Backup System - Process pending backup creations every minute") | crontab -
(crontab -l 2>/dev/null; echo "* * * * * /usr/bin/php /docker/whp/web/scripts/backup-worker.php >/dev/null 2>&1") | crontab -
echo "✓ Backup system cron jobs configured"

# Setup backup upload daemon (systemd service instead of cron)
echo "Setting up backup upload daemon..."
cp ./configs/whp-backup-upload.service /etc/systemd/system/
chmod 644 /etc/systemd/system/whp-backup-upload.service
systemctl daemon-reload
systemctl enable whp-backup-upload.service
touch /var/log/whp-backup.log
chmod 640 /var/log/whp-backup.log
echo "✓ Backup upload daemon configured (will start after reboot)"

# Setup cPanel-import worker isolation (sandboxed systemd template unit).
# See configs/whp-cpanel-import@.service and
# scripts/updates/staging/setup-cpanel-import-isolation.sh for the rationale -
# enforces the import worker's blast radius at the kernel layer.
echo "Setting up cPanel import worker isolation..."
mkdir -p /var/log/whp
mkdir -p /var/lib/whp
mkdir -p /var/lib/whp/cpanel-imports
chmod 700 /var/lib/whp/cpanel-imports
mkdir -p /docker/powerdns/logs
cp ./configs/whp-cpanel-import@.service /etc/systemd/system/
chmod 644 /etc/systemd/system/whp-cpanel-import@.service
systemctl daemon-reload
# Logrotate for /var/log/cpanel-import-*.log produced by the unit.
cat > /etc/logrotate.d/whp-cpanel-import <<'CPIMP_LOGROTATE_EOF'
/var/log/cpanel-import-*.log {
    weekly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}
CPIMP_LOGROTATE_EOF
echo "✓ cPanel import worker isolation configured"

# Setup bulk-sites worker isolation (sandboxed systemd template unit).
# See configs/whp-bulk-sites@.service and
# scripts/updates/staging/setup-bulk-sites-worker.sh for the rationale -
# the multi-site bulk-management endpoint spawns this unit per job rather
# than running the work inline (which used to block the HTTP request for
# up to WHP_BULK_MAX_SITES * per-op duration).
echo "Setting up bulk-sites worker isolation..."
mkdir -p /var/lib/whp/bulk-sites
chmod 700 /var/lib/whp/bulk-sites
mkdir -p /docker/whp/sql/bulk-jobs
chmod 750 /docker/whp/sql/bulk-jobs
cp ./configs/whp-bulk-sites@.service /etc/systemd/system/
chmod 644 /etc/systemd/system/whp-bulk-sites@.service
systemctl daemon-reload
# Logrotate for /var/log/whp-bulk-sites-*.log produced by the unit.
cat > /etc/logrotate.d/whp-bulk-sites <<'BULKSITES_LOGROTATE_EOF'
/var/log/whp-bulk-sites-*.log {
    weekly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}
BULKSITES_LOGROTATE_EOF
echo "✓ bulk-sites worker isolation configured"

# Setup SSL certificate refresh for system services
echo "Setting up SSL certificate refresh for system services..."
(crontab -l 2>/dev/null; echo "# WHP System Service SSL - Refresh certificates daily at 3 AM") | crontab -
(crontab -l 2>/dev/null; echo "0 3 * * * /usr/bin/php /root/whp/scripts/refresh-system-ssl.php >/dev/null 2>&1") | crontab -

echo "✓ SSL certificate refresh cron job configured"

# Setup WordPress auto-update system
echo "Setting up WordPress auto-update system..."
# Create log file with proper permissions
touch /var/log/wordpress-auto-update.log
chmod 644 /var/log/wordpress-auto-update.log
chown root:root /var/log/wordpress-auto-update.log

# Add WordPress auto-update cron job (every 6 hours: 2 AM, 8 AM, 2 PM, 8 PM)
(crontab -l 2>/dev/null; echo "# WHP WordPress Auto-Update System - Run updates every 6 hours") | crontab -
(crontab -l 2>/dev/null; echo "0 2,8,14,20 * * * /usr/bin/php /root/whp/scripts/wordpress-auto-update.php >/dev/null 2>&1") | crontab -

echo "✓ WordPress auto-update system configured"

# Setup HAProxy watchdog cron job
echo "Setting up HAProxy watchdog cron job..."
if ! crontab -l 2>/dev/null | grep -q "haproxy-watchdog"; then
    (crontab -l 2>/dev/null; echo "# WHP HAProxy Watchdog - Monitor and auto-recover HAProxy every minute") | crontab -
    (crontab -l 2>/dev/null; echo "* * * * * /root/whp/scripts/haproxy-watchdog.sh >> /var/log/haproxy-watchdog.log 2>&1") | crontab -
    echo "✓ HAProxy watchdog cron job configured"
else
    echo "✓ HAProxy watchdog cron job already exists"
fi

# Set up logrotate for HAProxy watchdog log
cat > /etc/logrotate.d/haproxy-watchdog <<'EOF'
/var/log/haproxy-watchdog.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    size 10M
}
EOF
echo "✓ HAProxy watchdog logrotate configured"

# Ensure watchdog script is executable
if [ -f /root/whp/scripts/haproxy-watchdog.sh ]; then
    chmod +x /root/whp/scripts/haproxy-watchdog.sh
fi

# Setup HAProxy crash monitor daemon (real-time docker events listener)
echo "Setting up HAProxy crash monitor daemon..."
cp ./configs/haproxy-crash-monitor.service /etc/systemd/system/
chmod 644 /etc/systemd/system/haproxy-crash-monitor.service
systemctl daemon-reload

# Create log files
touch /var/log/haproxy-crash-monitor.log
chmod 640 /var/log/haproxy-crash-monitor.log
touch /var/log/haproxy-crash-events.json
chmod 640 /var/log/haproxy-crash-events.json

# Set up logrotate for crash monitor logs
cat > /etc/logrotate.d/haproxy-crash-monitor <<'EOF'
/var/log/haproxy-crash-monitor.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    size 10M
}

/var/log/haproxy-crash-events.json {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    size 10M
}
EOF
echo "✓ HAProxy crash monitor logrotate configured"

# Ensure crash monitor script is executable
if [ -f /root/whp/scripts/haproxy-crash-monitor.sh ]; then
    chmod +x /root/whp/scripts/haproxy-crash-monitor.sh
fi

# Enable and start the crash monitor service
systemctl enable haproxy-crash-monitor
systemctl start haproxy-crash-monitor
echo "✓ HAProxy crash monitor daemon configured"

# Setup DNS challenge verification cron job
echo "Setting up DNS challenge verification cron job..."
mkdir -p /var/log/whp
if ! crontab -l 2>/dev/null | grep -q "verify-dns-challenges"; then
    (crontab -l 2>/dev/null; echo "# WHP DNS Challenge Verification - Verify pending DNS-01 ACME challenges every minute") | crontab -
    (crontab -l 2>/dev/null; echo "* * * * * /usr/bin/php /root/whp/scripts/verify-dns-challenges.php >> /var/log/whp/dns-challenges.log 2>&1") | crontab -
    echo "✓ DNS challenge verification cron job configured"
else
    echo "✓ DNS challenge verification cron job already exists"
fi

# Create backup system directories
mkdir -p /tmp/whp-backups
chown whp:whp /tmp/whp-backups
chmod 755 /tmp/whp-backups

echo "✓ Backup system directories created"

# Setup WebSocket Terminal Server
echo ""
echo "======================================"
echo "Setting up WebSocket Terminal Server..."

# Install build dependencies for node-pty
echo "Installing build dependencies..."
dnf groupinstall -y "Development Tools"
dnf install -y python3-devel

# Install Node.js via NVM if not present
if ! command -v node &> /dev/null; then
    echo "Installing Node.js 22 LTS via NVM..."
    
    # Download and install nvm
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
    
    # Source nvm for current session
    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
    
    # Install Node.js 22 LTS
    nvm install 22
    nvm use 22
    nvm alias default 22
    
    echo "✓ Node.js $(node -v) installed via NVM"
    echo "✓ npm $(npm -v) available"
else
    echo "✓ Node.js already installed: $(node -v)"
fi

# Download xterm.js files and sync to web root
echo "Downloading xterm.js files..."
chmod +x /root/whp/scripts/download-xterm.sh
if ! /root/whp/scripts/download-xterm.sh; then
    echo "Error: Failed to download xterm.js files"
    exit 1
fi
echo "✓ xterm.js files downloaded and synced successfully"

# Install terminal server dependencies
echo "Installing terminal server dependencies..."
cd /root/whp/terminal-server
if ! npm install --production; then
    echo "Error: Failed to install terminal server dependencies"
    echo "Check that build dependencies are installed and Node.js is working"
    exit 1
fi
echo "✓ Terminal server dependencies installed successfully"

# Copy Apache WebSocket configuration
echo "Configuring Apache WebSocket proxy..."
cp /root/whp/configs/whp-terminal-websocket.conf /etc/httpd/conf.d/

# Install and enable systemd service
echo "Installing terminal server service..."
chmod +x /root/whp/scripts/create-terminal-service.sh
if ! /root/whp/scripts/create-terminal-service.sh; then
    echo "Error: Failed to create terminal service"
    exit 1
fi

systemctl daemon-reload
if ! systemctl enable whp-terminal; then
    echo "Error: Failed to enable terminal service"
    exit 1
fi

if ! systemctl start whp-terminal; then
    echo "Error: Failed to start terminal service"
    echo "Check service logs: journalctl -u whp-terminal -f"
    exit 1
fi

echo "✓ Terminal service installed and started successfully"

echo "✓ WebSocket Terminal Server configured"
cd /root/whp

# Setup SSH/FTP Log Monitoring System
echo ""
echo "======================================"
echo "Setting up SSH/FTP Log Monitoring..."

# Create necessary directories
mkdir -p /var/log/proftpd
mkdir -p /docker/whp/sql
chmod 755 /var/log/proftpd
chmod 755 /docker/whp/sql

# Make log monitor cron script executable
if [ -f "/root/whp/scripts/log_monitor_cron.sh" ]; then
    chmod +x /root/whp/scripts/log_monitor_cron.sh
    
    # Add cron job for log monitoring (runs every 5 minutes)
    if ! crontab -l 2>/dev/null | grep -q "log_monitor_cron.sh"; then
        (crontab -l 2>/dev/null; echo "# WHP SSH/FTP Log Monitoring - Check logs every 5 minutes") | crontab -
        (crontab -l 2>/dev/null; echo "*/5 * * * * /root/whp/scripts/log_monitor_cron.sh >/dev/null 2>&1") | crontab -
        echo "✓ SSH/FTP log monitoring cron job configured"
    else
        echo "✓ SSH/FTP log monitoring cron job already exists"
    fi
else
    echo "⚠ SSH/FTP log monitoring script not found - skipping"
fi

echo "✓ SSH/FTP Log Monitoring configured"

# Setup AI-Powered Log Monitor
echo ""
echo "======================================"
echo "Setting up AI-Powered Log Monitor..."

# Install Python 3 and pip if not present
if ! command -v python3 &>/dev/null || ! command -v pip3 &>/dev/null; then
    echo "Installing Python 3 and pip..."
    dnf install -y python3 python3-pip
else
    echo "✓ Python 3 and pip already installed"
fi

# Install the openai SDK
echo "Installing openai SDK for AI log analysis..."
pip3 install --quiet openai || echo "⚠ openai SDK installation failed - AI log monitor will need manual setup"

# Install dependencies for the minute-cadence monitor poll (whp-monitor-poll.py
# uses requests for the health-API call, urllib3 to suppress self-signed cert
# warnings, pymysql for the issue-emit path in whp_monitor_core.py).
echo "Installing monitor poll Python dependencies..."
pip3 install --quiet urllib3 requests pymysql || echo "⚠ Monitor poll deps install failed - whp-monitor-poll.py will not run"

# Create directories
mkdir -p /var/lib/whp-ai-monitor/backups/
chmod 700 /var/lib/whp-ai-monitor/backups/
# Daily AI report output dir (whp-log-monitor-ai.py --mode daily writes here).
mkdir -p /var/lib/whp-ai-monitor/reports/
chmod 755 /var/lib/whp-ai-monitor/reports/
mkdir -p /etc/whp/

# Copy default config if not exists
if [ ! -f /etc/whp/ai-monitor.conf ]; then
    if [ -f /root/whp/configs/ai-monitor.conf.default ]; then
        echo "Installing default AI monitor config..."
        cp /root/whp/configs/ai-monitor.conf.default /etc/whp/ai-monitor.conf
        chmod 600 /etc/whp/ai-monitor.conf
        echo "✓ AI monitor config installed"
    else
        echo "⚠ Default AI monitor config not found - will need manual configuration"
    fi
else
    echo "✓ AI monitor config already exists"
fi

# Create API key placeholder if not exists
if [ ! -f /etc/whp/ai-monitor.key ]; then
    touch /etc/whp/ai-monitor.key
    chmod 600 /etc/whp/ai-monitor.key
    echo "✓ AI monitor API key placeholder created"
else
    echo "✓ AI monitor API key file already exists"
fi

# Create log files
touch /var/log/whp-ai-monitor.log
chmod 640 /var/log/whp-ai-monitor.log
touch /var/log/whp-ai-monitor-daily.log
chmod 640 /var/log/whp-ai-monitor-daily.log

# Install logrotate config
cat > /etc/logrotate.d/whp-ai-monitor <<'EOF'
/var/log/whp-ai-monitor.log /var/log/whp-ai-monitor-daily.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    size 50M
}
EOF
echo "✓ AI monitor logrotate configured"

# Install cron jobs (idempotent)
if ! crontab -l 2>/dev/null | grep -qF 'whp-log-monitor-ai.py --mode critical'; then
    (crontab -l 2>/dev/null; echo "# WHP AI Log Monitor - Critical mode scan every 5 minutes") | crontab -
    (crontab -l 2>/dev/null; echo "*/5 * * * * /usr/bin/python3 /root/whp/scripts/whp-log-monitor-ai.py --mode critical >> /var/log/whp-ai-monitor.log 2>&1") | crontab -
    echo "✓ AI monitor critical mode cron job configured"
else
    echo "✓ AI monitor critical mode cron job already exists"
fi

if ! crontab -l 2>/dev/null | grep -qF 'whp-log-monitor-ai.py --mode daily'; then
    (crontab -l 2>/dev/null; echo "# WHP AI Log Monitor - Daily summary at 6 AM") | crontab -
    (crontab -l 2>/dev/null; echo "0 6 * * * /usr/bin/python3 /root/whp/scripts/whp-log-monitor-ai.py --mode daily >> /var/log/whp-ai-monitor-daily.log 2>&1") | crontab -
    echo "✓ AI monitor daily mode cron job configured"
else
    echo "✓ AI monitor daily mode cron job already exists"
fi

# Ensure the main script is executable if present
if [ -f /root/whp/scripts/whp-log-monitor-ai.py ]; then
    chmod +x /root/whp/scripts/whp-log-monitor-ai.py
fi

# Minute-cadence health + haproxy stats poll. Records state transitions,
# emits diagnosis-bearing Issues, and (when enabled) drives auto-remediation.
echo "Setting up AI Monitor minute-cadence poll..."
touch /var/log/whp-monitor-poll.log
chmod 640 /var/log/whp-monitor-poll.log
cat > /etc/logrotate.d/whp-monitor-poll <<'EOF'
/var/log/whp-monitor-poll.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    size 10M
}
EOF
if ! crontab -l 2>/dev/null | grep -qF 'whp-monitor-poll.py'; then
    (crontab -l 2>/dev/null; echo "# WHP AI Monitor - Minute-cadence health + HAProxy stats poll") | crontab -
    (crontab -l 2>/dev/null; echo "* * * * * /usr/bin/python3 /root/whp/scripts/whp-monitor-poll.py >> /var/log/whp-monitor-poll.log 2>&1") | crontab -
    echo "✓ Monitor poll cron job configured"
else
    echo "✓ Monitor poll cron job already exists"
fi
if [ -f /root/whp/scripts/whp-monitor-poll.py ]; then
    chmod +x /root/whp/scripts/whp-monitor-poll.py
fi

# Disk-usage snapshot (Server Admin > Disk Usage page). Cron every 6h plus
# on-demand refresh via the page button. The page itself is gated to root.
echo "Setting up Server Admin disk-usage snapshot cron..."
if ! crontab -l 2>/dev/null | grep -qF 'disk-usage-snapshot.php'; then
    (crontab -l 2>/dev/null; echo "# WHP Server Admin - Disk usage snapshot every 6 hours") | crontab -
    (crontab -l 2>/dev/null; echo "0 */6 * * * /usr/bin/php /root/whp/scripts/disk-usage-snapshot.php >/dev/null 2>&1") | crontab -
    echo "✓ Disk-usage snapshot cron job configured"
else
    echo "✓ Disk-usage snapshot cron job already exists"
fi
if [ -f /root/whp/scripts/disk-usage-snapshot.php ]; then
    chmod +x /root/whp/scripts/disk-usage-snapshot.php
fi

echo "✓ AI-Powered Log Monitor configured"
echo ""
echo "  NOTE: To activate AI analysis, set your API key:"
echo "    echo 'your-api-key' > /etc/whp/ai-monitor.key"
echo "  Review config: /etc/whp/ai-monitor.conf"

echo ""
echo "🎉 Setup completed!"
echo ""
echo "⚠️  IMPORTANT SECURITY NOTES:"
echo "   1. Save the encryption key from /etc/whp/encryption.key"
echo "   2. Store server settings securely"
echo "   3. Update DNS records for your domains"
echo ""
echo "Password encryption is enabled by default for all user passwords."
echo ""
echo "Access your control panel at: http://your-server-ip:8080"
echo "Default login uses system PAM authentication"
echo ""
echo "Next steps:"
echo "1. Create system users for hosting clients"
echo "2. Database structure is fully configured and verified"
echo "3. Run 'php /root/whp/scripts/setup-mysql-users.php' to set up MySQL users for existing system users"
echo "4. User quotas are configured - use '/root/whp/scripts/quota-helper.sh' for quota management"
echo "5. ProFTPD is configured with MySQL virtual users - hosting users automatically get FTP access when created"
echo "6. PowerDNS DNS server is configured with secure password management"
echo "7. Use '/root/whp/scripts/powerdns-password-manager.sh' for DNS password management"
echo "8. Configure backup targets in the WHP control panel under 'Backup Management'"
echo "9. WordPress auto-updates are configured to run every 6 hours (2 AM, 8 AM, 2 PM, 8 PM)"
echo "10. WordPress security settings include comprehensive options: file editor blocking, XML-RPC protection, version hiding, security headers, and more"
echo "11. WebSocket terminal server provides full terminal access to containers via the web interface"
echo "12. PostgreSQL database server is running on port 5432 with root credentials stored in /root/.pgpass"
echo "13. Adminer is available for PostgreSQL management via the WHP interface"
echo "14. SSH/FTP log monitoring automatically blocks IPs after failed login attempts (configurable via Security Management)"
echo "15. Log monitoring runs every 5 minutes checking /var/log/auth.log (SSH) and /var/log/proftpd/proftpd.log (FTP)"
echo "16. SSL certificates for system services (httpd, proftpd) are automatically refreshed daily at 3 AM from HAProxy"
echo "17. Health check endpoint available at /api/health.php for monitoring (unauthenticated, configurable thresholds)"
echo "18. HAProxy watchdog monitors container health every minute with auto-recovery"
echo "19. HAProxy crash monitor daemon captures real-time crash forensics via docker events (see /var/log/haproxy-crash-events.json)"
echo "20. DNS-01 ACME challenge verification runs every minute for wildcard SSL certificates"
echo "21. AI-powered log monitor scans system logs every 5 minutes with daily summary at 6 AM (requires API key in /etc/whp/ai-monitor.key)"
echo ""
echo "Troubleshooting:"
echo "- Check logs in /var/log/httpd/ for Apache issues"
echo "- Use 'docker logs mysql' for MySQL database issues"
echo "- Use 'docker logs postgresql' for PostgreSQL database issues"
echo "- Ensure .user.ini is copied to web directory if authentication fails"
echo "- Check backup logs in /var/log/whp-backup-*.log"
echo "- Check WordPress auto-update logs in /var/log/wordpress-auto-update.log"
echo "- WordPress management includes WP-CLI integration, security settings, auto-updates, and backup functionality"
echo "- Check terminal server logs: journalctl -u whp-terminal -f"
echo "- Verify WebSocket proxy: curl -I http://localhost:8080/terminal-ws"
echo "- Check backup upload daemon: systemctl status whp-backup-upload"
echo "- Check SSH/FTP log monitoring: /var/log/whp-log-monitor.log"
echo "- Manually trigger log check: /root/whp/scripts/log_monitor_cron.sh"
echo "- Check HAProxy watchdog logs: /var/log/haproxy-watchdog.log"
echo "- Check HAProxy crash events: /var/log/haproxy-crash-events.json (JSONL, one crash per line)"
echo "- Check HAProxy crash monitor daemon: systemctl status haproxy-crash-monitor"
echo "- Check DNS challenge verification logs: /var/log/whp/dns-challenges.log"
echo "- Check AI log monitor: /var/log/whp-ai-monitor.log and /var/log/whp-ai-monitor-daily.log"
echo "- AI monitor config: /etc/whp/ai-monitor.conf | Test: python3 /root/whp/scripts/whp-log-monitor-ai.py --dry-run"
echo ""
echo "FIRST-DAY: Issue a Let's Encrypt cert for the panel hostname via the admin"
echo "  panel (Server Settings → Network & SSL → System Service SSL Certificates)."
echo "  The panel sends Strict-Transport-Security with includeSubDomains on every"
echo "  response. If you ever rebuild this VM, the new self-signed cert won't be"
echo "  trusted by browsers that already have the year-long HSTS rule cached, and"
echo "  you'll be locked out of the hostname until you either issue a real cert"
echo "  (via IP:8080) or clear the HSTS entry in your browser. See:"
echo "  https://kb.anhonesthost.com/whp/admin/server-settings/#network--ssl"
echo ""
echo "REBOOT THE SERVER NOW"
echo ""

# Create version file to mark installation complete
echo "$(cat /root/whp/VERSION)" > /etc/whp-version
echo "WHP version $(cat /etc/whp-version) installed successfully"
