
function retry {
    local retries=$1
    shift
    local msg="$1"
    shift

    local count=0
    until "$@"; do
        exit=$?
        wait=$((2 ** $count))
        count=$(($count + 1))
        if [ $count -lt $retries ]; then
            echo "Retry $count/$retries exited $exit, retrying in $wait seconds..."
            sleep $wait
        else
            echo "Retry $count/$retries exited $exit, no more retries left."
            echo "$msg"
            return $exit
        fi
    done
    return 0
}

function install_required_packages {
    function install_retry {
        apt update && \
        export DEBIAN_FRONTEND=noninteractive; apt install -y apt-transport-https ca-certificates curl software-properties-common jq unzip atop iptables-persistent
    }
    retry 10 "Failed to install required packages" install_retry
}


function install_mariadb {
    function install_retry {
        apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 && \
        add-apt-repository 'deb [arch=amd64] http://mirrors.digitalocean.com/mariadb/repo/10.6/ubuntu bionic main' && \
        apt update && \
        apt install -y mariadb-server mariadb-client rsync
    }
    retry 10 "Failed to install MariaDB" install_retry
    systemctl enable mariadb.service
    mysql_secure_installation <<EOF
y
${DATABASE_ADMIN_PASSWORD}
${DATABASE_ADMIN_PASSWORD}
y
y
y
y
EOF

}

function configure_mariadb {
    systemctl stop mariadb.service
    local node_ip_var="NODE_${NODE_NUMBER}_IP"
    cat <<EOF > /etc/mysql/mariadb.conf.d/60-galera.cnf
[mysqld]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
ignore-db-dirs=lost+found

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Galera Cluster Configuration
wsrep_cluster_name="test_cluster"
wsrep_cluster_address="gcomm://${NODE_01_IP},${NODE_02_IP},${NODE_03_IP}"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="${!node_ip_var}"
wsrep_node_name="$(hostname)"
EOF
}

function start_mariadb {
    if [[ "$NODE_NUMBER" -eq "01" ]]; then
        galera_new_cluster
    else
        systemctl start mariadb.service
    fi
}

function wait_for_mariadb {
    function wait_for {
        test $(mysql -u root -p${DATABASE_ADMIN_PASSWORD} -NBe "show status like 'wsrep_ready';" | cut -f2) = 'ON'
    }
    retry 10 "Failed to wait for MariaDB to be ready" wait_for
}

function grant_remote_access {
    mysql -u root -p${DATABASE_ADMIN_PASSWORD} -e "GRANT ALL ON *.* to root@'%' IDENTIFIED BY \"${DATABASE_ADMIN_PASSWORD}\";"
}

function init_refapp_database {
    local db=$1
    local db_user=$2
    local db_password=$3

    mysql -u root -p${DATABASE_ADMIN_PASSWORD} -e "CREATE DATABASE IF NOT EXISTS $db CHARACTER SET utf8;"
    mysql -u root -p${DATABASE_ADMIN_PASSWORD} -e "CREATE USER IF NOT EXISTS '${db_user}'@'%' identified by '${db_password}';"
    mysql -u root -p${DATABASE_ADMIN_PASSWORD} -e "GRANT ALL PRIVILEGES ON ${db}.* TO '${db_user}'@'%';"
}

function mount_drives {
    local database_disk=${DATABASE_DISK}
    local disk_name
    local disk_size
    local disk_type
    local disk_label
    local disk_mountpoint

    if [[ ${DATABASE_DISK} == "auto" ]]; then
        while read -r line; do
            disk_name=$(echo $line |awk '{print $1}')
            disk_size=$(echo $line |awk '{print $2}')
            disk_type=$(echo $line |awk '{print $3}')
            disk_label=$(echo $line |awk '{print $4}')
            disk_mountpoint=$(echo $line |awk '{print $5}')
            if [[ $disk_type != "disk" || $disk_mountpoint != "" || $disk_label == "config-2" ]]; then
                continue
            fi
            if [[ $(lsblk -o name |grep $disk_name | wc -l) -gt 1 ]]; then
                continue
            fi
            database_disk="/dev/${disk_name}"
            break
        done <<< "$(lsblk -o name,size,type,label,mountpoint |grep -vw NAME |grep "^[a-z]")"
    fi
    if [[ ${database_disk} == "auto" ]]; then
        echo "Failed to detect database disk."
        exit 1
    fi

    if ! lsblk -f ${database_disk} |grep -q ext4; then
        mkfs.ext4 ${database_disk}
    fi
    if [ ! -d /var/lib/mysql ]; then
        mkdir /var/lib/mysql
    fi
    if ! grep -q "${database_disk}" /etc/fstab; then
        mount ${database_disk} /var/lib/mysql
        echo "${database_disk}    /var/lib/mysql   ext4    defaults    0 0" >> /etc/fstab
    fi
}

function install_database {
    install_required_packages
    install_mariadb
    configure_mariadb
    start_mariadb
    wait_for_mariadb
    grant_remote_access
    init_refapp_database $APP_DATABASE_NAME $APP_DATABASE_USER $APP_DATABASE_PASSWORD
}

function install_docker {
    mkdir -p /etc/docker
    cat <<EOF > /etc/docker/daemon.json
{
  "default-address-pools": [
    { "base": "192.168.0.0/24", "size": 24 }
  ]
}
EOF
    apt update
    apt install -y docker.io
}

function install_app {
    install_docker
    docker run --restart=always -dit -p ${APP_PORT}:8000 --hostname $(hostname) -e OS_REFAPP_DB_URL="mysql+pymysql://${APP_DATABASE_USER}:${APP_DATABASE_PASSWORD}@${DATABASE_VIP}:3306/refapp" $APP_DOCKER_IMAGE
}

function disable_apt_daily {
    echo 'APT::Periodic::Unattended-Upgrade "0";' > /etc/apt/apt.conf.d/20auto-upgrades
    echo 'APT::Periodic::Update-Package-Lists "0";' >> /etc/apt/apt.conf.d/20auto-upgrades
    apt remove -y unattended-upgrades
    echo "" > /etc/crontab
    for TIMER in apt-daily-upgrade.timer apt-daily.timer systemd-tmpfiles-clean.timer motd-news.timer fstrim.timer; do
        systemctl stop ${TIMER}
        systemctl disable ${TIMER}
    done
}

function common_configuration {
    disable_apt_daily
}
