#!/bin/bash
# Script Name: mpsetup.sh
# Description: This script installs MultiPortal on various distribution
#              (single-node only; Galera cluster prepnodes/confignodes removed)
# Author: jbulalaque, Matthew Wood
# Date: 2026-09-15
# Version: 1.9.2
# Usage: ./mpsetup.sh [arguments]
#
# Changes in 1.9.2 - MariaDB repository handling for Debian 13 (trixie):
#   - Removed the "fall back to bookworm" path. Trixie is built against
#     OpenSSL 3.5 and the t64 ABI; bookworm packages are not a valid
#     substitute. MariaDB 11.8 has a real trixie repo - use it.
#   - Stopped passing the raw /etc/os-release ID as --os-type. It is
#     normalised to debian/ubuntu, so derivatives work too.
#   - Release codename now comes from os-release (UBUNTU_CODENAME /
#     VERSION_CODENAME) rather than lsb_release.
#   - The downloaded mariadb_repo_setup is checked for staleness. If the
#     copy served by r.mariadb.com predates trixie support, the Deb822
#     repo file is written directly instead. This was the actual bug.
#   - Falls back to the distribution's own MariaDB if upstream has nothing
#     for this release. Debian 13 ships 11.8.x natively.
#   - Set MARIADB_USE_DISTRO=1 in the environment to skip upstream entirely.

set -euo pipefail
IFS=$'\n\t'

#set -e: Exit the script immediately if any command fails.
#set -u: Treat unset variables as an error.
#set -o pipefail: Catch errors in pipelines.
#IFS=$'\n\t': Avoid issues with word splitting.

#Variables

APP_NAME="MultiPortal"
FQDN=""
INSTALL_DIR="$(pwd)"
DB_HOST="localhost"
DB_NAME="multiportal"
DB_USER="multiportal"
DB_PASS="multiportal"
ROOT_PASS="$(openssl rand -base64 12)"
PHP_VERSION_REQUIRED="8.2"
CONSOLEPORT=8081
COOKIE_VALIDATION_KEY="$(openssl rand -base64 12)"

INSTALL_DIR="$(pwd)"
LOG_FILE="$INSTALL_DIR/mpinstall.log"

# Set the CaddyServer version and architecture
CADDY_VERSION="2.11.4"
CADDY_ARCH="amd64"

# Multiportal Repo URL
REPO_URL="https://downloads.multiportal.io/installer/mpInstallation"

SSLCER=""
SSLKEY=""

DSTCER="/var/lib/caddy/sslcer.cer"
DSTKEY="/var/lib/caddy/sslkey.key"

ENV_NAME=".env"
CADDYSERVER_CONF="/etc/caddy/Caddyfile"

DISTRO=""
PKGMGR=""
SUDO=""
HTTP_ONLY=""
MP_VERSION=""

# MariaDB configuration
# 11.8 is the current LTS and the FIRST series with official Debian 13
# (trixie) repositories. Series 11.0-11.7 have no trixie repo at all and
# mariadb_repo_setup will refuse them - do not lower this on trixie.
MARIADB_SERIES="11.8"
MARIADB_REPO_BASE="https://dlm.mariadb.com/repo/mariadb-server"
MARIADB_KEYRING_URL="https://supplychain.mariadb.com/mariadb-keyring-2025.gpg"
MARIADB_KEYRING="/etc/apt/keyrings/mariadb-keyring.gpg"
MARIADB_SOURCES="/etc/apt/sources.list.d/mariadb.sources"
MARIADB_LIST_LEGACY="/etc/apt/sources.list.d/mariadb.list"
# Set to 1 to skip the upstream repo entirely and use the distro's own
# MariaDB packages (Debian 13 ships 11.8.x, which is perfectly adequate).
MARIADB_USE_DISTRO="${MARIADB_USE_DISTRO:-0}"

# Setup sudo handling - handles fresh Debian where sudo may not be installed
# Sets SUDO variable: empty when root (no sudo needed), "sudo" when not root
setup_sudo() {
    if [ "$EUID" -eq 0 ]; then
        # Running as root - no sudo prefix needed for commands
        SUDO=""

        # On Debian/Ubuntu, install sudo package if not present
        # This is needed because some commands in the script explicitly need sudo
        # for proper user context switching even when running as root
        if [ -f /etc/debian_version ]; then
            if ! command -v sudo &>/dev/null; then
                echo "Installing sudo package on Debian..."
                apt-get update -qq --allow-releaseinfo-change
                apt-get install -y -qq sudo
            fi
        fi
    else
        # Not running as root - need sudo prefix
        if ! command -v sudo &>/dev/null; then
            echo "Error: sudo is not installed and you are not running as root."
            echo "Please either run this script as root, or install sudo first."
            exit 1
        fi
        SUDO="sudo"
    fi
}

accept_eula() {
    echo "---------------------------------------------"
    echo "          Welcome to MultiPortal!"
    echo "---------------------------------------------"
    echo ""
    echo "This script will:"
    echo "  - Install required dependencies, including PHP 8.2, MariaDB, Node.js, and related packages."
    echo "  - Download and set up the MultiPortal installer."
    echo "  - Configure and secure MariaDB for MultiPortal."
    echo "  - Install and configure CaddyServer as the web server for MultiPortal."
    echo "  - Configure and enable the MultiPortal service for systemd."
    echo "  - Set up Ioncube loader for PHP."
    echo "  - Configure and set up Node.js for WebSocket handling with PM2."
    echo "  - Finalize the MultiPortal installation."
    echo ""
    echo "---------------------------------------------"
    echo "        Please read and accept the EULA"
    echo "---------------------------------------------"
    echo "By proceeding, you agree to the EULA agreement."
    echo "The EULA can be reviewed at: https://multiportal.io/end-user-license-agreement"
    echo ""
    echo "Do you agree to the EULA agreement? (yes/no): "

    read -r response

  case "$response" in
    [Yy][Ee][Ss])
      print_info "Thank you for agreeing to the EULA. Proceeding..."
      return 0
      ;;
    [Nn][Oo])
      print_error "You did not agree to the EULA. Exiting..."
      print_info "Press any key to continue..."
      read -n1 -s
      exit 1
      ;;
    *)
      print_error "Invalid input. Please type 'yes' or 'no'."
      print_info "Press any key to continue..."
      read -n1 -s
      accept_eula
      ;;
  esac
}

print_usage() {
    echo "Usage: sudo $0 -f <your_fqdn> [-H]"
    echo "       sudo $0 -f <your_fqdn> -s <cert_path> -k <key_path>"
    echo ""
    echo "Options:"
    echo "  -f, --fqdn        Fully qualified domain name"
    echo "  -H, --http-only   Serve HTTP only (no HTTPS/TLS)"
    echo "  -s, --sslcert     Path to SSL certificate"
    echo "  -k, --sslkey      Path to SSL key"
    echo "  -h, --help        Show this help message"
}

# Print a message in green for success or info
print_info() {
    echo -e "\e[32m$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1\e[0m"
    echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" >> "$LOG_FILE"
}

# Print a message in red for errors
print_error() {
    echo -e "\e[31m$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1\e[0m" >&2
    echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >> "$LOG_FILE"
}

# Create or clear the log file
initialize_log() {
    echo "=== $APP_NAME Installer Log ===" > "$LOG_FILE"
    echo "Log started at $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
}

is_root_user() {
    if [ "$EUID" -eq 0 ]; then
        echo "You are running as root."
        return 0
    else
        echo "You are not running as root."
        return 1
    fi
} 

show_loader() {
    local pid=$1
    local delay=0.2
    local spin='-\|/'

    while ps -p "$pid" > /dev/null; do
        local temp=${spin#?}
        printf " [%c] " "$spin"
        local spin=$temp${spin%"$temp"}
        sleep $delay
        printf "\b\b\b\b\b"
    done

    printf "    \b\b\b\b"
}

show_loading_with_timer() {
    local pid=$1 # Process ID of the running command
    local delay=0.1
    local spinner='|/-\'
    local start_time=$(date +%s) # Record the start time

    while kill -0 "$pid" 2>/dev/null; do
        local current_time=$(date +%s)
        local elapsed=$((current_time - start_time))
        local minutes=$((elapsed / 60))
        local seconds=$((elapsed % 60))
        
        for i in $(seq 0 $((${#spinner} - 1))); do
            printf "\r[%c] Task Elapsed Time... %02d:%02d" "${spinner:$i:1}" "$minutes" "$seconds"
            sleep $delay
        done
    done
    printf "\n"
}

read_password() {
    password=""
    while IFS= read -r -s -n 1 char; do
        # Check for Enter (ASCII code 13)
        if [[ $char == $'\0' || $char == $'\n' ]]; then
            echo    # Print a newline when done
            break
        fi

        # Handle backspace
        if [[ $char == $'\177' ]]; then
            if [[ -n $password ]]; then
                # Remove last character from password
                password=${password%?}
                # Move cursor back, overwrite with space, and move back again
                echo -ne "\b \b"
            fi
        else
            # Append character to password
            password+="$char"
            # Display asterisk
            echo -n "*"
        fi
    done
    DB_PASS=$password
}

# Function to determine the distribution type
get_linux_distribution() {
    if [[ -f /etc/os-release ]]; then
        # Parse the ID field from /etc/os-release
        . /etc/os-release
        echo "$ID"
    elif [[ -f /etc/redhat-release ]]; then
        # For Red Hat-based distributions
        awk '{print $1}' /etc/redhat-release
    elif [[ -f /etc/debian_version ]]; then
        # For Debian-based distributions
        echo "Debian"
    else
        echo "Unknown"
    fi
}

# Function to determine the package manager
get_package_manager() {
    # List of common package managers to check
    local managers=("apt" "yum" "dnf" "pacman" "zypper" "brew" "port" "emerge" "apk" "pkg")

    for manager in "${managers[@]}"; do
        if command -v "$manager" &>/dev/null; then
            echo "$manager"
            return 0
        fi
    done

    # If no package manager is found
    echo "unknown"
    return 1
}

# Helper function for fully non-interactive apt installations
apt_install_noninteractive() {
    $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y \
        -o Dpkg::Options::="--force-confdef" \
        -o Dpkg::Options::="--force-confold" \
        "$@"
}

# Configure apt for non-interactive operation (handles needrestart on Ubuntu 22.04+)
configure_noninteractive_apt() {
    export DEBIAN_FRONTEND=noninteractive
    export NEEDRESTART_MODE=a
    export NEEDRESTART_SUSPEND=1

    # Disable needrestart interactive mode if installed (Ubuntu 22.04+)
    if [ -f /etc/needrestart/needrestart.conf ]; then
        $SUDO sed -i "s/#\\\$nrconf{restart} = 'i';/\\\$nrconf{restart} = 'a';/" /etc/needrestart/needrestart.conf 2>/dev/null || true
    fi

    # Pre-configure GRUB to avoid prompts during kernel updates
    if command -v debconf-set-selections &>/dev/null; then
        echo 'grub-pc grub-pc/install_devices_empty boolean true' | $SUDO debconf-set-selections 2>/dev/null || true
    fi
}

disable_apache2() {
    # Check if the apache2 service is active
    if systemctl is-active --quiet apache2; then
        echo "Apache2 service is running. Stopping it now..."
        # Stop the apache2 service
        $SUDO systemctl stop apache2
        $SUDO systemctl disable apache2

        if [ $? -eq 0 ]; then
            echo "Apache2 service has been stopped and disabled successfully."
        else
            echo "Failed to stop and disabled the Apache2 service."
        fi
    else
        echo "Apache2 service is not installed, continuing..."
    fi
}


#############################################################
# MariaDB repository handling (Debian/Ubuntu)               #
#############################################################

# Return the upstream family this system belongs to: "debian" or "ubuntu".
# $DISTRO holds the raw ID from /etc/os-release, which is wrong to pass
# straight to mariadb_repo_setup on derivatives (raspbian, linuxmint, pop...).
detect_deb_ostype() {
    case "${DISTRO,,}" in
        debian|raspbian|devuan) echo "debian" ; return 0 ;;
        ubuntu) echo "ubuntu" ; return 0 ;;
    esac

    if [[ -r /etc/os-release ]]; then
        local id_like
        id_like="$(. /etc/os-release && echo "${ID_LIKE:-}")"
        # Check ubuntu first: Mint/Pop report ID_LIKE="ubuntu debian"
        case " ${id_like,,} " in
            *ubuntu*) echo "ubuntu" ; return 0 ;;
            *debian*) echo "debian" ; return 0 ;;
        esac
    fi

    echo "unknown"
    return 1
}

# Return the release codename to use for the MariaDB repository.
# Prefers UBUNTU_CODENAME (correct on Mint/Pop), then VERSION_CODENAME,
# then lsb_release, then /etc/debian_version. lsb_release is deliberately
# NOT the primary source - it is not guaranteed to be installed and returns
# "n/a" on testing/sid.
detect_deb_codename() {
    local codename=""

    if [[ -r /etc/os-release ]]; then
        codename="$(. /etc/os-release && echo "${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}")"
    fi

    if [[ -z "$codename" || "$codename" == "n/a" ]] && command -v lsb_release &>/dev/null; then
        codename="$(lsb_release -sc 2>/dev/null || true)"
    fi

    if [[ -z "$codename" || "$codename" == "n/a" ]] && [[ -f /etc/debian_version ]]; then
        case "$(cat /etc/debian_version)" in
            11*)            codename="bullseye" ;;
            12*)            codename="bookworm" ;;
            13*)            codename="trixie"   ;;
            14*|forky*)     codename="forky"    ;;
            trixie*)        codename="trixie"   ;;
            bookworm*)      codename="bookworm" ;;
        esac
    fi

    echo "$codename"
}

# Map a system codename onto the nearest codename MariaDB actually publishes.
# Only used for releases with no upstream repo of their own (sid, forky).
# NOTE: trixie is NOT remapped - it has its own repo from 11.8 onwards.
mariadb_repo_codename() {
    local ostype="$1" codename="$2"

    case "$ostype" in
        debian)
            case "$codename" in
                bullseye|bookworm|trixie) echo "$codename" ;;
                # Unreleased/rolling Debian: trixie is the closest published repo
                forky|sid|unstable|testing) echo "trixie" ;;
                *) echo "" ;;
            esac
            ;;
        ubuntu)
            case "$codename" in
                jammy|noble|resolute) echo "$codename" ;;
                *) echo "" ;;
            esac
            ;;
        *) echo "" ;;
    esac
}

# Confirm the MariaDB series/codename combination actually exists upstream
# before we commit to it. Cheap HEAD request against the repo's Release file.
mariadb_repo_exists() {
    local ostype="$1" codename="$2" code
    code="$(curl -LsS -o /dev/null -I -w '%{http_code}' --max-time 20 \
        "${MARIADB_REPO_BASE}/${MARIADB_SERIES}/repo/${ostype}/dists/${codename}/Release" 2>/dev/null || echo "000")"
    [[ "$code" == "200" ]]
}

# Write /etc/apt/sources.list.d/mariadb.sources directly, in the same Deb822
# format and with the same keyring path the current upstream mariadb_repo_setup
# uses. This is the fallback when the downloaded script is too old to know
# about this release.
mariadb_write_repo_native() {
    local ostype="$1" codename="$2" components="main"

    [[ "$ostype" == "ubuntu" ]] && components="main main/debug"

    echo "Writing MariaDB repository configuration for ${ostype}/${codename} directly..."

    $SUDO mkdir -p "$(dirname "$MARIADB_SOURCES")" /etc/apt/preferences.d

    # Retire any pre-existing config so apt cannot see the same suite twice
    # ("Target Packages is configured multiple times").
    local f suffix
    for f in "$MARIADB_SOURCES" "$MARIADB_LIST_LEGACY"; do
        if [[ -e "$f" ]]; then
            suffix=0
            while [[ -e "$f.old_$((++suffix)).disabled" ]]; do :; done
            echo "Found existing $f - moving to $f.old_${suffix}.disabled"
            $SUDO mv "$f" "$f.old_${suffix}.disabled"
        fi
    done

    # Import the signing key into /etc/apt/keyrings and verify its checksum.
    $SUDO mkdir -p /etc/apt/keyrings
    $SUDO chmod 755 /etc/apt/keyrings

    local keytmp
    keytmp="$(mktemp -d)"
    if ! ( cd "$keytmp" \
            && curl -LsSO --max-time 60 "$MARIADB_KEYRING_URL" \
            && curl -LsS --max-time 60 "${MARIADB_KEYRING_URL}.sha256" | sha256sum -c --quiet ); then
        echo "ERROR: could not download or verify the MariaDB signing key."
        rm -rf "$keytmp"
        return 1
    fi
    $SUDO install -m 0644 "${keytmp}/$(basename "$MARIADB_KEYRING_URL")" "$MARIADB_KEYRING"
    rm -rf "$keytmp"

    # Drop the legacy globally-trusted key, if a previous run left one behind.
    $SUDO rm -f /etc/apt/trusted.gpg.d/mariadb-keyring*.gpg

    $SUDO tee "$MARIADB_SOURCES" > /dev/null <<EOF
# MariaDB Server - written by mpsetup.sh
# To use a different major version of the server, or to pin to a specific minor version, change URI below.
X-Repolib-Name: MariaDB Server
Enabled: yes
Types: deb
URIs: ${MARIADB_REPO_BASE}/${MARIADB_SERIES}/repo/${ostype}
Suites: ${codename}
Components: ${components}
Architectures: amd64 arm64
Signed-By: ${MARIADB_KEYRING}
EOF

    # Give MariaDB packages priority over the distro's own, matching what
    # mariadb_repo_setup does.
    $SUDO tee /etc/apt/preferences.d/mariadb-enterprise.pref > /dev/null <<EOF
Package: *
Pin: origin dlm.mariadb.com
Pin-Priority: 1000
EOF

    echo "MariaDB repository written to $MARIADB_SOURCES"
    return 0
}

# Configure the MariaDB apt repository.
#
# Strategy, in order:
#   1. Download the official mariadb_repo_setup and check whether it actually
#      knows this release codename. The copy served by r.mariadb.com has
#      historically lagged behind new Debian releases - that staleness is
#      exactly what broke trixie.
#   2. If it does know the codename, run it with correctly NORMALISED
#      --os-type/--os-version (never the raw /etc/os-release ID).
#   3. If it doesn't, write the Deb822 repo file ourselves for the real
#      codename.
#   4. If the upstream repo genuinely does not exist for this combination,
#      fall back to the distro's own MariaDB packages.
#
# What this deliberately does NOT do: silently substitute bookworm on a
# trixie box. Trixie is built against OpenSSL 3.5 and the t64 ABI; bookworm
# packages either fail to resolve or produce an unsupported install.
setup_mariadb_repo() {
    local ostype codename repo_codename tmpdir script

    ostype="$(detect_deb_ostype)"
    codename="$(detect_deb_codename)"

    echo "MariaDB repo: detected os-type='${ostype}' codename='${codename}' (raw ID='${DISTRO}')"

    if [[ "$ostype" == "unknown" || -z "$codename" ]]; then
        echo "WARNING: could not identify the Debian/Ubuntu release."
        echo "Falling back to the distribution's own MariaDB packages."
        MARIADB_USE_DISTRO=1
        return 0
    fi

    if [[ "$MARIADB_USE_DISTRO" == "1" ]]; then
        echo "MARIADB_USE_DISTRO=1 - skipping the upstream repository."
        return 0
    fi

    repo_codename="$(mariadb_repo_codename "$ostype" "$codename")"

    if [[ -z "$repo_codename" ]]; then
        echo "WARNING: MariaDB publishes no repository for ${ostype}/${codename}."
        echo "Falling back to the distribution's own MariaDB packages."
        MARIADB_USE_DISTRO=1
        return 0
    fi

    if [[ "$repo_codename" != "$codename" ]]; then
        echo "No upstream repo for '${codename}'; using nearest published release '${repo_codename}'."
    fi

    if ! mariadb_repo_exists "$ostype" "$repo_codename"; then
        echo "WARNING: MariaDB ${MARIADB_SERIES} is not published for ${ostype}/${repo_codename}."
        echo "Falling back to the distribution's own MariaDB packages."
        MARIADB_USE_DISTRO=1
        return 0
    fi

    echo "Confirmed MariaDB ${MARIADB_SERIES} repository exists for ${ostype}/${repo_codename}."

    # Work in a temp dir so we stop dropping mariadb_repo_setup into $INSTALL_DIR.
    tmpdir="$(mktemp -d)"
    script="${tmpdir}/mariadb_repo_setup"

    if curl -LsS --max-time 60 -o "$script" https://r.mariadb.com/downloads/mariadb_repo_setup; then
        chmod +x "$script"

        local script_version
        script_version="$(grep -m1 '^version=' "$script" | cut -d'"' -f2 || true)"
        echo "Downloaded mariadb_repo_setup (version ${script_version:-unknown})"

        # Staleness check: if the script has never heard of this codename it
        # will error out no matter what we pass it.
        if grep -qw "$repo_codename" "$script"; then
            if $SUDO "$script" \
                --mariadb-server-version="mariadb-${MARIADB_SERIES}" \
                --os-type="$ostype" \
                --os-version="$repo_codename" \
                --skip-maxscale; then
                rm -rf "$tmpdir"
                echo "MariaDB repository configured via mariadb_repo_setup."
                return 0
            fi
            echo "mariadb_repo_setup failed; writing the repository configuration directly instead."
        else
            echo "Downloaded mariadb_repo_setup (${script_version:-unknown}) predates '${repo_codename}' support."
            echo "Writing the repository configuration directly instead."
        fi
    else
        echo "Could not download mariadb_repo_setup; writing the repository configuration directly instead."
    fi

    rm -rf "$tmpdir"

    if ! mariadb_write_repo_native "$ostype" "$repo_codename"; then
        echo "WARNING: could not configure the upstream MariaDB repository."
        echo "Falling back to the distribution's own MariaDB packages."
        MARIADB_USE_DISTRO=1
    fi

    return 0
}

# Is a .deb package installed? dpkg-query is exact; the old
# `dpkg -l | grep "^ii\s\+pkg\s"` relied on GNU grep BRE extensions and on
# dpkg's column layout, and matched substrings on narrow terminals.
deb_pkg_installed() {
    [[ "$(dpkg-query -W -f='${db:Status-Status}' "$1" 2>/dev/null || true)" == "installed" ]]
}

install_dependencies() {

    echo "Detected Linux Distribution: $DISTRO"
    echo "Detected package manager: $PKGMGR"
    echo "Installing required packages..."

    if [[ "$PKGMGR" == "apt" ]]; then

        # Configure apt for fully non-interactive operation
        configure_noninteractive_apt

        DEB_PHP_SETTINGS="/etc/php/8.2/fpm/php.ini"

        packages=(
            "debian-keyring"
            "debian-archive-keyring"
            "curl"
            "gnupg"
            "git"
            "unzip"
            "libicu-dev"
            "lsb-release"
            "ca-certificates"
            "wget"
        )

        # software-properties-common is only needed on Ubuntu (for add-apt-repository)
        # It has been removed from Debian 13 (Trixie)
        if [[ "$DISTRO" == "ubuntu" ]]; then
            packages+=("software-properties-common")
        fi

        echo "Updating package index..."
        $SUDO apt-get update -y --allow-releaseinfo-change

        echo "Installing initial packages..."
        base_to_install=()
        for package in "${packages[@]}"; do
            if deb_pkg_installed "$package"; then
                echo "$package is already installed."
            else
                base_to_install+=("$package")
            fi
        done

        if [ ${#base_to_install[@]} -gt 0 ]; then
            echo "Installing: ${base_to_install[*]}"
            if ! apt_install_noninteractive "${base_to_install[@]}"; then
                echo "ERROR: Failed to install base packages: ${base_to_install[*]}"
                exit 1
            fi
        fi

        # Configure the MariaDB repository (see setup_mariadb_repo above).
        setup_mariadb_repo

        mdb=(
            "mariadb-server"
            "mariadb-client"
            "galera-4"
            "mariadb-backup"
        )

        echo "Updating package index..."
        $SUDO apt-get update -y --allow-releaseinfo-change

        echo "Installing MariaDB packages..."
        # Install in ONE transaction. Installing mariadb-server and
        # mariadb-backup separately lets apt resolve them against different
        # versions and is a common source of dependency breakage.
        mdb_to_install=()
        for package in "${mdb[@]}"; do
            if deb_pkg_installed "$package"; then
                echo "$package is already installed."
            else
                mdb_to_install+=("$package")
            fi
        done

        if [ ${#mdb_to_install[@]} -gt 0 ]; then
            echo "Installing: ${mdb_to_install[*]}"
            if ! apt_install_noninteractive "${mdb_to_install[@]}"; then
                echo "ERROR: Failed to install MariaDB packages."
                echo "Repository configuration in use:"
                $SUDO cat "$MARIADB_SOURCES" 2>/dev/null || echo "  (using distribution packages)"
                exit 1
            fi
            echo "MariaDB packages installed successfully."
        fi

        # Report what we actually got, so the log is unambiguous.
        echo "Installed MariaDB version: $(mariadb --version 2>/dev/null || mysql --version 2>/dev/null || echo 'unknown')"

        # The dnf branch below starts and enables mariadb; the apt branch
        # never did. Debian usually auto-starts it, but not in containers or
        # when policy-rc.d is in play.
        $SUDO systemctl enable mariadb 2>/dev/null || true
        if ! systemctl is-active --quiet mariadb; then
            echo "Starting MariaDB..."
            $SUDO systemctl start mariadb
        fi
        if ! systemctl is-active --quiet mariadb; then
            echo "ERROR: MariaDB failed to start. Check 'systemctl status mariadb'."
            exit 1
        fi
        echo "MariaDB is running."

        echo "Adding Sury repository for PHP 8.2"

        # Add Sury repository for PHP 8.2
        if [[ "$DISTRO" == "debian" ]]; then
            # Use modern keyring approach (compatible with Debian 12 and 13)
            $SUDO mkdir -p /usr/share/keyrings
            $SUDO curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
            # Same codename detection as MariaDB - do not depend on lsb_release
            SURY_CODENAME="$(detect_deb_codename)"
            echo "Using Sury suite: ${SURY_CODENAME}"
            echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${SURY_CODENAME} main" \
                | $SUDO tee /etc/apt/sources.list.d/php.list

            # Pin Sury packages higher to prevent php-common conflicts on Debian 13+
            $SUDO tee /etc/apt/preferences.d/php-sury.pref > /dev/null <<PINEOF
Package: php* libapache2-mod-php*
Pin: origin packages.sury.org
Pin-Priority: 900
PINEOF
        elif [[ "$DISTRO" == "ubuntu" ]]; then
            # Ensure fully non-interactive for add-apt-repository
            $SUDO env DEBIAN_FRONTEND=noninteractive add-apt-repository ppa:ondrej/php -y
        else
            echo "Unable to identify distro"
            exit 1
        fi
        #Update APT Cache and Install PHP 8.2
        echo "Updating package index..."
        $SUDO apt-get update -y --allow-releaseinfo-change

        phppkgs=(
            "php8.2"
            "php8.2-fpm"
            "php8.2-common"
            "php8.2-curl"
            "php8.2-gd"
            "php8.2-mbstring"
            "php8.2-mysql"
            "php8.2-opcache"
            "php8.2-xml"
            "php8.2-zip"
	    "php8.2-ldap"
        )
        
        # Reset php-fpm failure state before installing to prevent rate-limit issues
        # The packages trigger service restarts during installation
        $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
        $SUDO systemctl stop php8.2-fpm 2>/dev/null || true

        # Build list of packages that need to be installed
        pkgs_to_install=()
        for phppkg in "${phppkgs[@]}"; do
            if deb_pkg_installed "$phppkg"; then
                echo "$phppkg is already installed."
            else
                echo "$phppkg needs to be installed."
                pkgs_to_install+=("$phppkg")
            fi
        done

        # Install all PHP packages in a single command to minimize service restart triggers
        if [ ${#pkgs_to_install[@]} -gt 0 ]; then
            echo "Installing PHP packages: ${pkgs_to_install[*]}"
            # Reset again right before install in case of prior failures
            $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
            if ! apt_install_noninteractive "${pkgs_to_install[@]}"; then
                echo "Failed to install PHP packages. Please check your configuration or network connection."
                exit 1
            fi
            echo "PHP packages installed successfully."
        fi

        # Reset failure state after package installation (dpkg triggers may have caused failures)
        $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true

        echo "Configuring PHP Settings"

        # #Configure PHP Settings
        $SUDO sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 10G/' $DEB_PHP_SETTINGS
        $SUDO sed -i 's/^post_max_size = .*/post_max_size = 10G/' $DEB_PHP_SETTINGS
        $SUDO sed -i 's/^max_execution_time = .*/max_execution_time = 360/' $DEB_PHP_SETTINGS

        #Verify PHP Settings
        echo "Verify PHP Settings"
        $SUDO grep -E 'upload_max_filesize|post_max_size|max_execution_time' $DEB_PHP_SETTINGS

        #Restart PHP-FPM (reset-failed handles systemd rate-limit issues)
        $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
        sleep 2  # Brief delay to avoid rate-limiting
        $SUDO systemctl restart php8.2-fpm
        if ! systemctl is-active --quiet php8.2-fpm; then
            echo "Warning: php8.2-fpm failed to start after PHP configuration"
            $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
            sleep 5
            $SUDO systemctl start php8.2-fpm
            if ! systemctl is-active --quiet php8.2-fpm; then
                echo "ERROR: php8.2-fpm failed to start. Check 'systemctl status php8.2-fpm' for details."
                exit 1
            fi
        fi
        
        #Install IonCube
        echo "Installing Ioncube"
        # Download and extract IonCube in /tmp (using subshell to preserve current directory)
        (
            cd /tmp || exit 1
            $SUDO wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
            $SUDO tar -zxvf ioncube_loaders_lin_x86-64.tar.gz
        )

        #Copy IonCube Loader
        $SUDO cp /tmp/ioncube/ioncube_loader_lin_8.2.so "$(php -r "echo ini_get('extension_dir').PHP_EOL;")"

        install_caddyserver

        #Update Config Files - add IonCube if not already present
        IONCUBE_EXT="zend_extension=$(php -r "echo ini_get('extension_dir').PHP_EOL;")/ioncube_loader_lin_8.2.so"
        if ! grep -q "ioncube_loader" /etc/php/8.2/fpm/php.ini 2>/dev/null; then
            echo "$IONCUBE_EXT" | $SUDO tee -a /etc/php/8.2/fpm/php.ini
        else
            echo "IonCube already configured in fpm/php.ini"
        fi

        if ! grep -q "ioncube_loader" /etc/php/8.2/cli/php.ini 2>/dev/null; then
            echo "$IONCUBE_EXT" | $SUDO tee -a /etc/php/8.2/cli/php.ini
        else
            echo "IonCube already configured in cli/php.ini"
        fi

        #restart PHP-FPM (reset-failed handles systemd rate-limit issues)
        $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
        sleep 2  # Brief delay to avoid rate-limiting
        $SUDO systemctl restart php8.2-fpm
        $SUDO systemctl enable php8.2-fpm

        # Verify PHP-FPM started successfully
        if ! systemctl is-active --quiet php8.2-fpm; then
            echo "Warning: php8.2-fpm failed to start after IonCube configuration"
            $SUDO systemctl reset-failed php8.2-fpm 2>/dev/null || true
            sleep 5
            $SUDO systemctl start php8.2-fpm
            if ! systemctl is-active --quiet php8.2-fpm; then
                echo "ERROR: php8.2-fpm failed to start. Check 'systemctl status php8.2-fpm' for details."
                exit 1
            fi
        fi
        echo "PHP-FPM is running successfully."

        $SUDO rm -r /tmp/ioncube
        $SUDO rm /tmp/ioncube_loaders_lin_x86-64.tar.gz

        #Install Node.js
        if [ -n "$SUDO" ]; then
            curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO -E bash -
        else
            curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
        fi
        apt_install_noninteractive nodejs
        
        #Install and Configure Composer
        $SUDO php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
        $SUDO php composer-setup.php --quiet
        $SUDO mv composer.phar /usr/local/bin/composer

    elif [[ "$PKGMGR" == "yum" || "$PKGMGR" == "dnf" ]]; then

        PHP_FPM_CONF="/etc/php-fpm.d/www.conf"
        RPM_PHP_SETTINGS="/etc/php.ini"
        
    
        $SUDO dnf update -y

        $SUDO dnf install -y \
            epel-release \
            dnf-utils \
            curl \
            libzip \
            unzip \
            wget \
            policycoreutils-python-utils \
            mod_fcgid \
            tar \
            git

        $SUDO curl -LsSO https://r.mariadb.com/downloads/mariadb_repo_setup
        $SUDO chmod +x ./mariadb_repo_setup
        $SUDO ./mariadb_repo_setup --mariadb-server-version="mariadb-11.8" --skip-maxscale
        $SUDO dnf install MariaDB-server MariaDB-client MariaDB-backup galera-4 -y

        $SUDO systemctl start mariadb && $SUDO systemctl enable mariadb

        $SUDO dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
        $SUDO dnf module reset php -y
        $SUDO dnf module enable php:remi-8.2 -y

        $SUDO dnf install -y \
            php \
            php-fpm \
            php-common \
            php-process \
            php-curl \
            php-gd \
            php-mbstring \
            php-mysqlnd \
            php-opcache \
            php-xml \
            php-xmlrpc \
            php-pecl-imagick \
            php-zip

        

        #Configure PHP Settings
$SUDO sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 10G/' $RPM_PHP_SETTINGS
$SUDO sed -i 's/^post_max_size = .*/post_max_size = 10G/' $RPM_PHP_SETTINGS
$SUDO sed -i 's/^max_execution_time = .*/max_execution_time = 360/' $RPM_PHP_SETTINGS

        #Verify PHP Settings
        #$SUDO grep -E 'upload_max_filesize|post_max_size|max_execution_time' $RPM_PHP_SETTINGS

        $SUDO sed -i 's/^user = apache/user = caddy/' $PHP_FPM_CONF
        $SUDO sed -i 's/^group = apache/group = caddy/' $PHP_FPM_CONF
        $SUDO sed -i 's/^listen.acl_users = .*/listen.acl_users = apache,nginx,caddy/' $PHP_FPM_CONF

        #$SUDO grep -E 'user = .*|group = .*|listen.acl_u' $PHP_FPM_CONF

        RPM_PHP_EXTDIR=$(php -r "echo ini_get('extension_dir').PHP_EOL;")

        # Download and extract IonCube in /tmp (using subshell to preserve current directory)
        (
            cd /tmp || exit 1
            $SUDO wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
            $SUDO tar -zxvf ioncube_loaders_lin_x86-64.tar.gz
        )

        $SUDO cp /tmp/ioncube/ioncube_loader_lin_8.2.so "$RPM_PHP_EXTDIR"

            #Update Config Files - add IonCube if not already present
            if ! grep -q "ioncube_loader" "$RPM_PHP_SETTINGS" 2>/dev/null; then
                echo "zend_extension=$RPM_PHP_EXTDIR/ioncube_loader_lin_8.2.so" | $SUDO tee -a $RPM_PHP_SETTINGS
            else
                echo "IonCube already configured in $RPM_PHP_SETTINGS"
            fi

            install_caddyserver

            # Reset-failed handles systemd rate-limit issues
            $SUDO systemctl reset-failed php-fpm 2>/dev/null || true
            sleep 2  # Brief delay to avoid rate-limiting
            $SUDO systemctl restart php-fpm
            $SUDO systemctl enable php-fpm

            # Verify PHP-FPM started successfully
            if ! systemctl is-active --quiet php-fpm; then
                echo "Warning: php-fpm failed to start after configuration"
                $SUDO systemctl reset-failed php-fpm 2>/dev/null || true
                sleep 5
                $SUDO systemctl start php-fpm
                if ! systemctl is-active --quiet php-fpm; then
                    echo "ERROR: php-fpm failed to start. Check 'systemctl status php-fpm' for details."
                    exit 1
                fi
            fi
            echo "PHP-FPM is running successfully."

            php -v

            $SUDO curl -fsSL https://rpm.nodesource.com/setup_22.x | $SUDO bash -
            $SUDO dnf install -y nodejs

            $SUDO php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
            $SUDO php composer-setup.php --quiet
            $SUDO mv composer.phar /bin/composer

            print_info "Required packages installed successfully."
    else
        echo "Unsupported Package Manager: $PKGMGR"
        exit 1
    fi

    if [[ $? -eq 0 ]]; then
        echo "Dependencies installed successfully."
    else
        echo "Failed to install dependencies."
        exit 1
    fi
}


install_caddyserver(){
    if [[ "$PKGMGR" == "apt" ]]; then
        echo -n "Installing CaddyServer... "
        $SUDO wget -q "https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_${CADDY_ARCH}.deb"
        apt_install_noninteractive "./caddy_${CADDY_VERSION}_linux_${CADDY_ARCH}.deb"

        if [ $? -eq 0 ]; then
            echo "CaddyServer installed."
        else
            echo "Failed to install CaddyServer."
            exit 1
        fi
        $SUDO rm -f "./caddy_${CADDY_VERSION}_linux_${CADDY_ARCH}.deb"
    elif [[ "$PKGMGR" == "yum" || "$PKGMGR" == "dnf" ]]; then
        $SUDO dnf install -y 'dnf-command(copr)'
        $SUDO dnf -y copr enable @caddy/caddy
        $SUDO dnf install -y caddy

        if [ $? -eq 0 ]; then
            echo "CaddyServer installed."
        else
            echo "Failed to install CaddyServer."
            exit 1
        fi
    else
        echo "Failed to install CaddyServer."
        exit 1
    fi
}


get_installer(){
    # Download the install repo file
    DEST_FILE="$INSTALL_DIR/installMultiportal"
    echo "Downloading the install repo file..."
    $SUDO curl -o "$DEST_FILE" "$REPO_URL"

    # Check if the file was downloaded successfully
    if [ $? -eq 0 ]; then
        # chmod +x "$DEST_FILE"
        echo "File downloaded successfully to $DEST_FILE."
    else
        echo "Failed to downloading the installer. Exiting..."
        exit 1
    fi

    # Make the downloaded file executable
    $SUDO chmod +x "$DEST_FILE"
    echo "The file has been made executable."
}

install_multiportal(){
    cd $INSTALL_DIR
       
    if [ -d "$WEB_DIR" ]; then
        echo "Directory '$WEB_DIR' exists. Skipping Installation"
    else
        echo "Directory '$WEB_DIR' does not exist. Proceeding with the installation"
        echo "Installing MultiPortal... on $WEB_DIR "
        $SUDO php "$INSTALL_DIR/installMultiportal" "$FQDN" ${MP_VERSION:+"$MP_VERSION"}
    fi
    
    if [ ! -d "$WEB_DIR" ]; then
        echo "Failed to install MultiPortal"
        exit 1
    fi

    $SUDO rm -f "$INSTALL_DIR/installMultiportal"
}

fix_permissions() {

# Determine the OS and set the default user
if grep -qi 'ubuntu' /etc/os-release || grep -qi 'debian' /etc/os-release; then
  default_user="www-data"
elif grep -qi "almalinux" /etc/os-release || grep -qi "rocky" /etc/os-release; then
  default_user="caddy"
else
  echo "Unsupported OS. Please specify the user manually."
  exit 1
fi

current_dir="/var/www/$(get_first_wwdir)"

# Check if the user is provided as an argument
current_user=$1

# Prompt for input if no user is provided
if [ -z "$current_user" ]; then
  read -p "Please enter the username of the current user (default: $default_user): " current_user
  if [ -z "$current_user" ]; then
    current_user="$default_user"
  fi
fi

# Check if the current user is in the default group if required
if [ "$current_user" != "$default_user" ] && ! groups "$current_user" | grep -qw "$default_user"; then
  echo "The current user is not in the $default_user group."
  echo "Please add the current user ($current_user) to the $default_user group and try again."
  exit 1
fi

# Set ownership recursively
echo "Setting ownership to $current_user:$default_user for all files and folders in $current_dir"
$SUDO chown -R "$current_user:$default_user" "$current_dir"

# Set permissions to 775 for directories and 664 for files
echo "Setting permissions to 775 for all directories"
$SUDO find "$current_dir" -type d -exec chmod 775 {} \;
echo "Setting permissions to 664 for all files"
$SUDO find "$current_dir" -type f -exec chmod 664 {} \;

# Set permissions to 775 for specific executable files
echo "Setting permissions to 775 for executable files"
$SUDO chmod +x "$current_dir/tools/permissionFix.sh"
$SUDO chmod +x "$current_dir/yii"
$SUDO chmod +x "$current_dir/init"

# Check if 'iso' directory exists, if not, create it
iso_dir="$current_dir/backend/web/iso"
if [ ! -d "$iso_dir" ]; then
  echo "Directory 'iso' does not exist. Creating it."
  $SUDO mkdir -p "$iso_dir"
fi

# Set ownership and permissions for specific backend directories
echo "Setting ownership to $current_user:$default_user for backend directories"
$SUDO chown -R "$current_user:$default_user" "$current_dir/backend/runtime/" "$current_dir/backend/web/assets/" "$iso_dir"
echo "Ensuring backend directories are writable"
$SUDO chmod -R 775 "$current_dir/backend/runtime/" "$current_dir/backend/web/assets/" "$iso_dir"

echo "Ownership and permissions updated successfully in $current_dir"
echo "If requested to do this before an update, you can now proceed with updating your MultiPortal Instance."
}

configure_permissions() {
    if [[ "$DISTRO" == "ubuntu" || "$DISTRO" == "debian" ]]; then
        fix_permissions www-data
    elif [[ "$DISTRO" == "almalinux" || "$DISTRO" == "rocky" ]]; then
        fix_permissions caddy
    else
        echo "Unsupported OS. Please specify the user manually."
        exit 1
    fi
}

setup_node() {
    echo "Setting up Node.js for $DISTRO..."
    # Use subshell to preserve current directory
    (
        cd "/var/www/$FQDN/websockets" || exit 1
        $SUDO npm cache clean --force
        $SUDO npm install
    )
}

setup_pm2(){
    echo "Installing PM2 globally..."
    PM2_LOG="/tmp/install_pm2.log"

    set +e  # Disable 'exit on error' temporarily

    $SUDO npm cache clean --force

    for attempt in {1..5}; do
        echo "Attempt $attempt: Installing PM2..." >> "$PM2_LOG"
        $SUDO timeout --kill-after=10s 2m bash -c "npm install pm2 -g" >> "$PM2_LOG" 2>&1
        install_pm2_status=$?

        if [ $install_pm2_status -eq 0 ]; then
            echo "PM2 installed successfully on attempt $attempt." >> "$PM2_LOG"
            break
        elif [ $install_pm2_status -eq 124 ]; then
            echo "Attempt $attempt: PM2 installation timed out after 2 minutes." >> "$PM2_LOG"
        else
            echo "Attempt $attempt: PM2 installation failed with exit code $install_pm2_status." >> "$PM2_LOG"
        fi
    done

    set -e  # Re-enable 'exit on error'
}



configure_sslcert() {
if [[ -n "${NOSSL:-}" ]]; then
    echo "NO cert provided as input, skipping this task and reverting to LetsEncrypt"
else
    echo "moving ssl cert to /var/lib/caddy folder..."
    $SUDO mv "$SSLCER" "$DSTCER"
    $SUDO mv "$SSLKEY" "$DSTKEY"
    $SUDO chown caddy:caddy "$DSTCER"
    $SUDO chown caddy:caddy "$DSTKEY"
fi
}

configure_caddy() {

    echo "Configuring CaddyServer..."

    if [[ "$PKGMGR" == "yum" || "$PKGMGR" == "dnf" ]]; then
        $SUDO mkdir -p /var/log/caddy
        $SUDO chown -R caddy:caddy /var/log/caddy
        $SUDO chmod 755 /var/log/caddy
    fi

    if [[ "$PKGMGR" == "apt" ]]; then
        cad_phpfastcgi="unix//var/run/php/php${PHP_VERSION_REQUIRED}-fpm.sock"
    elif [[ "$PKGMGR" == "yum" || "$PKGMGR" == "dnf" ]]; then
        cad_phpfastcgi="unix//run/php-fpm/www.sock"
    fi

if [[ -n "${NOSSL:-}" ]]; then
    if [[ -n "${HTTP_ONLY:-}" ]]; then
        CAD_FQDN="http://${FQDN}"
        echo "HTTP_ONLY mode: configuring Caddy for HTTP only (no TLS)"
    else
        CAD_FQDN="${FQDN}"
        echo "NO cert provided as input, configuring Caddy for LetsEncrypt"
    fi
    $SUDO cat > "$CADDYSERVER_CONF" << EOF
${CAD_FQDN} {
    @css path *.css
    header @css Content-Type text/css
    root * /var/www/${FQDN}/backend/web
    encode gzip
    file_server

    php_fastcgi ${cad_phpfastcgi} {
        index index.php
    }

    # Add headers
    header {
        Access-Control-Allow-Origin *
        Access-Control-Allow-Methods "GET, POST, OPTIONS"
        x-frame-options SAMEORIGIN
        Access-Control-Allow-Headers "Content-Type, Authorization"
    }

    # Error and access logs
    log {
        output file /var/log/caddy/${FQDN}.log
        level error
    }

    # PHP handling
    route / {
        try_files {path} {path}/ /index.php?{query}
        php_fastcgi ${cad_phpfastcgi} {
            env PHP_FCGI_MAX_REQUESTS 1000
        }
    }

    # Static files
    route / {
        file_server
    }

    # Reverse proxy to Node.js server
    reverse_proxy /wss* localhost:${CONSOLEPORT} {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }
}
EOF
else
    CAD_FQDN="*.${FQDN#*.}"
    echo "Cert provided as input, configuring Caddy to use SSL cert provided"
    $SUDO cat > "$CADDYSERVER_CONF" << EOF
${CAD_FQDN} {
    tls $DSTCER $DSTKEY
    @css path *.css
    header @css Content-Type text/css
    root * /var/www/${FQDN}/backend/web
    encode gzip
    file_server

    php_fastcgi ${cad_phpfastcgi} {
        index index.php
    }

    # Add headers
    header {
        Access-Control-Allow-Origin *
        Access-Control-Allow-Methods "GET, POST, OPTIONS"
        x-frame-options SAMEORIGIN
        Access-Control-Allow-Headers "Content-Type, Authorization"
    }

    # Error and access logs
    log {
        output file /var/log/caddy/${FQDN}.log
        level error
    }

    # PHP handling
    route / {
        try_files {path} {path}/ /index.php?{query}
        php_fastcgi ${cad_phpfastcgi} {
            env PHP_FCGI_MAX_REQUESTS 1000
        }
    }

    # Static files
    route / {
        file_server
    }

    # Reverse proxy to Node.js server
    reverse_proxy /wss* localhost:${CONSOLEPORT} {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }
}
EOF
fi

}

configure_mpservice(){
    echo "Configuring MultiPortal Service... " 
    # Create a new service file
    $SUDO tee "/etc/systemd/system/multiportal.service" > /dev/null << EOF
[Unit]
Description=MultiPortal Service
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=${WEB_DIR}
ExecStart=/usr/bin/php ${WEB_DIR}/yii queue/listen --verbose
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
    # Reload systemd
    $SUDO systemctl daemon-reload
    # Enable the service
    $SUDO systemctl enable multiportal.service
    # Start the service
    $SUDO systemctl start multiportal.service

    echo "MultiPortal Service configured successfully."
}

configure_firewall() {
    echo "checking if firewall-cmd exists,..."
    if command -v firewall-cmd &> /dev/null; then
        echo "firewall-cmd found, proceeding with firewall rules..."

        # Add HTTP and HTTPS services
        $SUDO firewall-cmd --permanent --add-service=https
        $SUDO firewall-cmd --permanent --add-service=http

        # Reload firewalld
        $SUDO firewall-cmd --reload

        echo "Firewall rules updated successfully."
    fi
}

configure_selinux() {

if command -v "getenforce" &>/dev/null; then
    echo "SELinux is Installed, starting configuration,..."
    selinux_status=$(getenforce)

    if [[ "$selinux_status" == "Enforcing" ]]; then

        # $SUDO setenforce 0
        echo "SELinux is enabled and set to 'Enforcing'."
        echo "Starting configuration,.."

        INSTALL_DIR="$(pwd)"
        POLICYCONF="$INSTALL_DIR/mp-policy.te"
        POLICYMOD="$INSTALL_DIR/mp-policy.mod"
        POLICYPP="$INSTALL_DIR/mp-policy.pp"

        $SUDO tee "$POLICYCONF" > /dev/null << EOF
module mp-policy 1.0;

require {
	type unconfined_service_t;
	type user_home_t;
	type httpd_sys_content_t;
	type init_t;
	type httpd_t;
	type mysqld_db_t;
	class file { append create open read setattr unlink write };
	class dir { add_name create remove_name setattr write };
	class unix_stream_socket connectto;
}

#============= httpd_t ==============

#!!!! This avc can be allowed using the boolean 'httpd_unified'
allow httpd_t httpd_sys_content_t:dir { add_name create remove_name setattr write };

#!!!! This avc can be allowed using the boolean 'httpd_unified'
allow httpd_t httpd_sys_content_t:file { append create setattr unlink write };
allow httpd_t unconfined_service_t:unix_stream_socket connectto;

#!!!! This avc can be allowed using the boolean 'httpd_read_user_content'
allow httpd_t user_home_t:file { open read };

#============= init_t ==============
allow init_t mysqld_db_t:file read;
EOF

    $SUDO checkmodule -M -m -o "$POLICYMOD" "$POLICYCONF"
    $SUDO semodule_package -o "$POLICYPP" -m "$POLICYMOD"
    $SUDO semodule -i "$POLICYPP"

    else
        echo "SELinux is not in 'Enforcing' mode. Current mode: $selinux_status"
        echo "Skipping SELinux configuration,.."
    fi

    return 0
fi


    
}



get_first_wwdir() {

    TARGET_DIR="/var/www/"
    for dir in "$TARGET_DIR"/*.*; do
        if [ -d "$dir" ]; then
            echo "${dir##"$TARGET_DIR"/}"
            break
        fi
    done

}

install_mp(){

if [[ -n "${CER:-}" || -n "${KEY:-}" ]]; then
    SSLCER="$(cd "$(dirname "$CER")" && pwd)/${CER##*/}"
    SSLKEY="$(cd "$(dirname "$KEY")" && pwd)/${KEY##*/}"
fi


accept_eula

WEB_DIR="/var/www/$FQDN"
DISTRO=$(get_linux_distribution)
PKGMGR=$(get_package_manager)

print_info "Installing Required Packages"
install_dependencies >> "$LOG_FILE" 2>&1 &
DEP_PID=$!
show_loading_with_timer $DEP_PID
wait $DEP_PID
DEP_STATUS=$?
if [ $DEP_STATUS -ne 0 ]; then
    print_error "Failed to install required packages (exit code: $DEP_STATUS)"
    print_error "Check log file for details: $LOG_FILE"
    print_error "Last 20 lines of log:"
    tail -20 "$LOG_FILE" >&2
    exit 1
fi

print_info "Disabling Apache2 if Installed"
disable_apache2 >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Downloading Multiportal Installer"
get_installer >> "$LOG_FILE" 2>&1 &
GET_PID=$!
show_loading_with_timer $GET_PID
wait $GET_PID
if [ $? -ne 0 ]; then
    print_error "Failed to download Multiportal installer"
    print_error "Check log file for details: $LOG_FILE"
    tail -20 "$LOG_FILE" >&2
    exit 1
fi

print_info "Installing Multiportal"
install_multiportal >> "$LOG_FILE" 2>&1 &
INST_PID=$!
show_loading_with_timer $INST_PID
wait $INST_PID
if [ $? -ne 0 ]; then
    print_error "Failed to install Multiportal"
    print_error "Check log file for details: $LOG_FILE"
    tail -20 "$LOG_FILE" >&2
    exit 1
fi

print_info "Configuring Permissions"
configure_permissions >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Setting up and configuring node"
setup_node >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Configuring PM2"
setup_pm2 >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

if which pm2 > /dev/null 2>&1; then
    print_info "PM2 Has successfully been installed."
else
    print_error "PM2 was not installed."
fi

$SUDO systemctl stop caddy

print_info "Configuring SSLCERT"
configure_sslcert >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Configuring Caddy"
configure_caddy >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Configuring Firewall-CMD if Installed"
configure_firewall >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Configuring SELinux if Installed"
configure_selinux >> "$LOG_FILE" 2>&1 &
show_loading_with_timer $!
wait $!

print_info "Disabling Apache2 if Installed"
disable_apache2 >> $LOG_FILE 2>&1 &
show_loading_with_timer $!
wait $!

sudo systemctl start caddy

#print_info "Configuring Multiportal Service"
#configure_mpservice >> "$LOG_FILE" 2>&1 &
#show_loading_with_timer $!
#wait $!

systemctl restart caddy

wait $!
if [ -f "$INSTALL_DIR/installMultiportal" ]; then
    rm -f "$INSTALL_DIR/installMultiportal"
fi

print_info "---------------------------------------"
print_info  "Installation has completed"
print_info  "---------------------------------------"
print_info  "To view your instance, visit http://$FQDN"
print_info  "The installed directory of your instance is $WEB_DIR"
print_info  "Caddyfile is located at $CADDYSERVER_CONF"
print_info  "---------------------------------------"

print_info "$APP_NAME installation finished successfully!"

END_TIME=$(date +%s)
ELAPSED_TIME=$((END_TIME - START_TIME))
ELAPSED_SECONDS=$(printf "%.0f" "$ELAPSED_TIME")
MINUTES=$((ELAPSED_SECONDS / 60))
SECONDS=$((ELAPSED_SECONDS % 60))
print_info "Elapsed time: $MINUTES minutes and $SECONDS seconds ($ELAPSED_TIME seconds total)"
print_info "For more details, see log file: $LOG_FILE"

}


#############################################################
# MAIN Execution                                            #
#############################################################

# Initialize sudo handling first - must be before any sudo commands
setup_sudo

if [ -e "$LOG_FILE" ]; then
    echo "The file '$LOG_FILE' exists."
    $SUDO chown "$USER:$USER" "$LOG_FILE"
fi


START_TIME=$(date +%s)
print_info "$(date '+%Y-%m-%d %H:%M:%S')"
initialize_log

PARAM1=false
PARAM4=false
PARAM5=false


export COMPOSER_ALLOW_SUPERUSER=1;

# Preprocess arguments to convert long options to short ones
for arg in "$@"; do
    shift
    case "$arg" in
        --fqdn) set -- "$@" "-f" ;;
        --sslcert) set -- "$@" "-s" ;;
        --sslkey) set -- "$@" "-k" ;;
        --http-only) set -- "$@" "-H" ;;
        --help) set -- "$@" "-h" ;;
        *) set -- "$@" "$arg" ;;
    esac
done

# Pre-process long options (getopts only handles short flags)
_args=()
while [[ $# -gt 0 ]]; do
    case "$1" in
        --version)
            if [[ -n "${2:-}" && "$2" != -* ]]; then
                MP_VERSION="$2"
                shift 2
            else
                echo "Error: --version requires a value (e.g. --version 1.1.0)"
                exit 1
            fi
            ;;
        *) _args+=("$1"); shift ;;
    esac
done
set -- "${_args[@]+"${_args[@]}"}"

# Parse arguments using getopts
while getopts "f:s:k:hH" opt; do
    case "$opt" in
        f)  
            PARAM1=true
            FQDN="$OPTARG"
            print_info "FQDN set from parameter, continuing..."
            ;;
        s)  
            PARAM4=true
            CER="$OPTARG"
            ;;
        k)  
            PARAM5=true
            KEY="$OPTARG"
            ;;
        H)
            HTTP_ONLY=true
            print_info "HTTP_ONLY mode enabled, Caddy will serve HTTP only"
            ;;
        h) 
            print_usage
            exit 0
            ;;
        *) 
            print_usage
            exit 1
            ;;
    esac
done

if [[ $PARAM1 == true && $PARAM4 == false && $PARAM5 == false ]]; then
    if is_root_user; then
        echo "Proceeding with root-only (sudo) operations..."
    else
        echo "Use FQDN parameter with sudo."
        exit 1
    fi
    export DEBIAN_FRONTEND=noninteractive
    NOSSL=true
    install_mp
    exit 0
fi

if [[ $PARAM1 == true && $PARAM4 == true && $PARAM5 == true ]]; then
    if is_root_user; then
        echo "Proceeding with root-only (sudo) operations..."
    else
        echo "Use FQDN parameter with sudo."
        exit 1
    fi
    export DEBIAN_FRONTEND=noninteractive
    install_mp
    exit 0
fi



