#!/bin/bash
export COMPOSER_ALLOW_SUPERUSER=1;
# Ensure the script is run as root
if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root" 1>&2
   exit 1
fi
# Function to generate a random string
generate_random_string() {
    echo "$(openssl rand -base64 4 | tr -dc 'a-zA-Z0-9' | head -c 5)"
}

# Loader function
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"
}

# Welcome to Multiportal

echo "---------------------------------------------"
echo "Welcome to Multiportal Alpha V2 Release!"
echo "Alpha-Version 0.5.0"
echo "---------------------------------------------"
echo "This is a pre-release version. Due to the experimental nature of the Alpha V2 release"
echo "You may experience bugs, instability, or incomplete features. Use with caution and report any issues you encounter."
echo ""
echo "This Script will install all the required packages and the Multiportal application."
echo "This application will use the CaddyServer application to run Multiportal."
echo ""
# Check if the directory exists
directory=$(pwd)'/multiportal'
installationDirectory=$(pwd)
CADDY_VERSION="2.7.6"
CADDY_ARCH="amd64"
if [ -d "$directory" ]; then
    echo "Directory exists. Proceeding..."
    # Add your commands here to proceed
else
    echo "Directory does not exist. Exiting..."
    exit 1
fi

# Main Script Execution
read -p "What is the hostname you're going to use?: " HOST_NAME
#read -p "Do you want to harden the MySQL instance? (yes/no): " hardenMysql
hardenMysql="no"

# Common Variables
ENV_NAME=".env"
WEB_DIR="/var/www/$HOST_NAME"
CADDYSERVER_CONF="/etc/caddy/Caddyfile"

DB_HOST=localhost
DB_NAME=multiportal
DB_USER=multiportal
RANDOM_STRING=$(generate_random_string)  # Changed from RANDOM to RANDOM_STRING
DB_PASS=$(openssl rand -base64 12) # Generates a secure password. Adjust length
ROOT_PASS=$(openssl rand -base64 12) # Generate a secure root password
PHPVERSION=8.2
CONSOLEPORT=8081
COOKIE_VALIDATION_KEY=$(openssl rand -base64 12)

# Function to install dependencies and verify installation
install_dependencies() {
    local success=true
    echo "Installing dependencies... "
    add-apt-repository -y ppa:ondrej/php >/dev/null 2>&1 &
    show_loader $!
    wait $!
    apt update >/dev/null 2>&1 &
    show_loader $!
    wait $!
    apt install -y debian-keyring debian-archive-keyring apt-transport-https curl >/dev/null 2>&1 &
    show_loader $!
    wait $!
    echo -n "Installing libapache2-mod-fcgid... "
    apt install -y libapache2-mod-fcgid >/dev/null 2>&1 &
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "libapache2-mod-fcgid installed."
    else
        echo "Failed to install libapache2-mod-fcgid."
        success=false
    fi
    echo -n "Installing software-properties-common... "
    apt install -y software-properties-common >/dev/null 2>&1 &
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "software-properties-common installed."
    else
        echo "Failed to install software-properties-common."
        success=false
    fi
    echo -n "Installing mariadb-server... "
    apt install -y mariadb-server >/dev/null 2>&1 &
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "mariadb-server installed."
    else
        echo "Failed to install mariadb-server."
        success=false
    fi
    echo -n "Installing PHP... "
    apt install -y 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-xmlrpc php8.2-imagick php8.2-zip >/dev/null 2>&1 &
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "PHP Installed."
    else
        echo "Failed to install PHP."
        success=false
    fi
    echo -n "Installing Node... "
    (curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - & apt install -y nodejs npm) >/dev/null 2>&1 &
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "Node has been installed."
    else
        echo "Failed to install Node."
        success=false
    fi

    if [ "$success" = false ]; then
        echo "One or more dependencies failed to install. Exiting..."
        exit 1
    fi
    echo "Dependencies have been installed!"
    if [ "$success" = false ]; then
        echo "One or more dependencies failed to install. Exiting..."
        exit 1
    fi
}

install_caddyserver(){
    echo -n "Installing CaddyServer... "
    wget -q "https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_${CADDY_ARCH}.deb"
    apt install "./caddy_${CADDY_VERSION}_linux_${CADDY_ARCH}.deb"
    show_loader $!
    wait $!
    if [ $? -eq 0 ]; then
        echo "CaddyServer installed."
    else
        echo "Failed to install CaddyServer."
        success=false
    fi
}

# Function to configure MariaDB user and database
configure_mariadb() {
    echo "Configuring MariaDB user and database... " &
    show_loader $!
    mysql -e "CREATE DATABASE IF NOT EXISTS $DB_NAME;"
    mysql -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
    mysql -e "SET PASSWORD FOR '$DB_USER'@'localhost' = PASSWORD('$DB_PASS');"
    mysql -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';"
    mysql -e "FLUSH PRIVILEGES;"
    echo "setting up environment variables"
    echo "DB_HOST=$DB_HOST" > /root/mariadb_user_credentials_$RANDOM_STRING
    echo "DB_NAME=$DB_NAME" >> /root/mariadb_user_credentials_$RANDOM_STRING
    echo "DB_USER=$DB_USER" >> /root/mariadb_user_credentials_$RANDOM_STRING
    echo "DB_PASS=$DB_PASS" >> /root/mariadb_user_credentials_$RANDOM_STRING
    chmod 600 /root/mariadb_user_credentials_$RANDOM_STRING # Restrict access to root
    wait $!
    echo "MariaDB user and database configured successfully."
}

# Function to harden MySQL instance
harden_mysql() {
    echo "Hardening MySQL instance... " & 
    show_loader $!
    ROOT_PASS=$(openssl rand -base64 12)
    echo "MariaDB Root Password: $ROOT_PASS" >> /root/mariadb_credentials_$RANDOM_STRING
    chmod 600 /root/mariadb_credentials_$RANDOM_STRING # Restrict access to root

    # Remove anonymous users
    mysql -e "DELETE FROM mysql.user WHERE User='';"

    # Disallow root login remotely
    mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost','127.0.0.1', '::1');"

    # Remove test database and access to it
    mysql -e "DROP DATABASE IF EXISTS test;"
    mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';"

    # Change root password
    mysql -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('$ROOT_PASS');"
    mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$ROOT_PASS';"
    wait $!
    echo " Hardening of MySQL has completed successfully."
}

# Function to configure Ioncube
download_ioncube(){
    echo "Configuraing Ioncube... " &
    show_loader $!
    extension_dir=$(php -r "echo ini_get('extension_dir');") 
    cd /tmp
    wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz >/dev/null 2>&1
    tar -zxvf ioncube_loaders_lin_x86* >/dev/null 2>&1
    cd ioncube/
    sudo cp /tmp/ioncube/ioncube_loader_lin_8.2.so "$extension_dir/ioncube_loader_lin_8.2.so" >/dev/null 2>&1
    # Clean up downloaded files
    rm -f /tmp/ioncube_loaders_lin_x86-64.tar.gz >/dev/null 2>&1
    rm -rf /tmp/ioncube/ >/dev/null 2>&1
    wait $!
    echo "Ioncube configured successfully."
}

# Configure Ioncube
configure_ioncube() {
    extension_dir=$(php -r "echo ini_get('extension_dir');")
    
    # Check if the file contains any existing zend_extension directives
    if ! sudo grep -q '^zend_extension = ' "$1"; then
        # If not, append the ionCube loader configuration at the end of the file
        echo "zend_extension = $extension_dir/ioncube_loader_lin_8.2.so" | sudo tee -a "$1" >/dev/null
    else
        # If the file already contains zend_extension directives, replace the commented out line
        sudo sed -i "s|^;ioncube loader configuration|zend_extension = $extension_dir/ioncube_loader_lin_8.2.so|" "$1"
    fi
}

install_composer (){
    # Print message indicating the start of Composer installation
    echo "Installing Composer... "

    # Download Composer installer
    php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Failed to download Composer installer. Check your internet connection or try again later."
        exit 1
    fi

    # Verify the integrity of the downloaded installer
    EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
    ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"

    if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
    then
        >&2 echo 'ERROR: Invalid installer checksum'
        rm composer-setup.php
        exit 1
    fi


    if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then
        echo "Composer installer integrity check failed. Try running the script again."
        exit 1
    fi

    # Run Composer installer
    php composer-setup.php --quiet >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Composer setup failed. Check the composer-setup.php file for details."
        exit 1
    fi

    # Remove the installer script
    php -r "unlink('composer-setup.php');" >/dev/null 2>&1

    # Move Composer executable to /usr/local/bin
    sudo mv composer.phar /usr/local/bin/composer >/dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo "Composer installed successfully"
    else
        echo "Failed to move Composer executable to /usr/local/bin. Check your permissions."
        exit 1
    fi
}

install_multiportal(){
    cd $installationDirectory
    echo "Copy multiportal directory... " 
    if [ ! -d /var/www ]; then
      mkdir /var/www
    fi
    
    mv $directory $WEB_DIR && echo "Directory moved successfully" || echo "Directory move failed"
    if [ -d "$WEB_DIR" ]; then
        echo "Copy was successful. Proceeding..."
        # Add your commands here to proceed
    else
        echo "Copy failed. Exiting..."
        exit 1
    fi
    echo "setting up environment variables"
    echo "DB_HOST=$DB_HOST" > $WEB_DIR/$ENV_NAME
    echo "DB_NAME=$DB_NAME" >> $WEB_DIR/$ENV_NAME
    echo "DB_USER=$DB_USER" >> $WEB_DIR/$ENV_NAME
    echo "DB_PASS=$DB_PASS" >> $WEB_DIR/$ENV_NAME
    echo "Private_Key=''" >> $WEB_DIR/$ENV_NAME
    echo "Full_Chain=''" >> $WEB_DIR/$ENV_NAME
    echo "HOST_NAME=$HOST_NAME" >> $WEB_DIR/$ENV_NAME
    echo "CONSOLE_PORT=$CONSOLEPORT" >> $WEB_DIR/$ENV_NAME
    echo "COOKIE_VALIDATION_KEY=$COOKIE_VALIDATION_KEY" >> $WEB_DIR/$ENV_NAME
    
    echo "Multiportal and environment variables copied Successfully"
}

run_composer(){
    echo "Running Composer... " 

    cd "$WEB_DIR"

    # Check if composer.lock and vendor directory exist
    if [ -f "composer.lock" ] || [ -d "vendor" ]; then
        # Remove existing composer.lock and vendor directory
        rm -rf composer.lock vendor
    fi

    # Run composer update after removing existing lock and vendor
    composer update --no-interaction > composer.log  >/dev/null 2>&1 &
    show_loader $!
    
    if [ $? -ne 0 ]; then
        echo "Composer update failed. Check composer.log for details."
        exit 1
    fi

    # Set permissions after composer update
    chown www-data:www-data -R "$WEB_DIR"
    chmod 775 -R "$WEB_DIR"
    wait $!
    echo "Composer completed successfully."
}


setup_yii(){
    echo "Setting Up Multiportal Framework... " 
    cd "$WEB_DIR"
    # Make sure the 'yii' file is executable
    chmod +x init  >/dev/null 2>&1 &
    show_loader $!
    wait $!
    ./init --env=Production --overwrite=y  >/dev/null 2>&1 &
    show_loader $!
    wait $!
    # Check if Yii framework's 'yii' file is executable
    if [ ! -f "./yii" ]; then
        echo "The Yii 'yii' file does not exist in the expected location."
        exit 1
    fi

    chmod +x ./yii

    # Run Yii migration
    ./yii migrate --interactive=0 >/dev/null 2>&1 &
    show_loader $!
    wait $!
    ./yii server/update-routes >/dev/null 2>&1 &
    show_loader $!
    wait $!
    ./yii generate-api-key >/dev/null 2>&1 &
    show_loader $!
    wait $!
    # Define your cron job command
    CRON_JOB="cd /var/www/${HOST_NAME} && ./yii cron"

    # Add the cron job to crontab
    # To run the job every day at 8:00 AM, for example
    echo "* * * * * $CRON_JOB" | crontab -
    echo "Multiportal Framework setup completed successfully."
}

configure_service(){
    echo "Configuring Multiportal Service... " 
    # Create a new service file
    cat > "/etc/systemd/system/multiportal.service" << 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
    systemctl daemon-reload
    # Enable the service
    systemctl enable multiportal.service
    # Start the service
    systemctl start multiportal.service

    echo "Multiportal Service configured successfully."
}

# Function to configure CaddyServer
configure_caddy() {
    echo "Configuring CaddyServer..."
    cat > "$CADDYSERVER_CONF" << EOF
${HOST_NAME} {
    @css path *.css
    header @css Content-Type text/css
    root * /var/www/${HOST_NAME}/backend/web
    encode gzip
    file_server

    php_fastcgi unix//var/run/php/php${PHPVERSION}-fpm.sock {
        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/${HOST_NAME}.log
        level error
    }

    # PHP handling
    route / {
        try_files {path} {path}/ /index.php?{query}
        php_fastcgi unix//var/run/php/php8.2-fpm.sock {
            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
}

setup_node() {
    echo "Setting up Node... " 
    cd "$WEB_DIR/websockets"
    sudo npm install >/dev/null 2>&1 &
    show_loader $!
    wait $!
    sudo npm install pm2 -g >/dev/null 2>&1 &
    show_loader $!
    wait $!
    sudo pm2 list >/dev/null 2>&1 &
    show_loader $!
    wait $!
    echo "Node setup completed successfully."
}

# Call functions based on user input
if [ "$hardenMysql" == "yes" ]; then
    install_dependencies
    install_caddyserver
    install_composer
    configure_mariadb
    harden_mysql
    # install ioncube and configure PHP CLI and FPM configuration file
    download_ioncube
    configure_ioncube "/etc/php/8.2/cli/php.ini"
    configure_ioncube "/etc/php/8.2/fpm/php.ini"
    systemctl restart php8.2-fpm

else
    install_dependencies
    install_composer
    configure_mariadb
    # Install ioncube and configure PHP CLI and FPM configuration file
    download_ioncube
    configure_ioncube "/etc/php/8.2/cli/php.ini"
    configure_ioncube "/etc/php/8.2/fpm/php.ini"
    systemctl restart php8.2-fpm
fi


install_multiportal

sleep 5
run_composer
# Check for vendor folder before running composer
if [ ! -d "$WEB_DIR/vendor" ]; then
  run_composer
fi

# Setup Yii
setup_yii
# Setup Node
setup_node
systemctl stop caddy
configure_caddy
systemctl start caddy
configure_service


wait $!

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

