#!/bin/bash

#############################################################
##################### b i n o m . o r g #####################
#############################################################

# binom V2 installation script

#############################################################

set -u
set -eE
set -o pipefail


VERSION=1.401
DOCKER_VERSION=
DOCKER_COMPOSE_VERSION=
BINOM_EMAIL=""
ram_total=""

# Error handler
trap 'echo "An error occurred during the installation. Please contact Binom support or check the log at /var/log/binom/binom_1click.log." >&3' ERR
exec 3>&2

log_file="/var/log/binom/binom_1click.log"
CUSTOM_DOMAIN_CONFIG_DIR="/var/lib/docker/volumes/binom_proxy_config/_data/domains"

log () {
    echo "---------------------------------------------------------------" >> "${log_file}"
    echo "$(date '+%d-%m-%Y %H:%M:%S') $1" >> "${log_file}"
    if [ "$2" == "+" ]; then
        echo "$1"
    fi
}

check_architecture () {
    cpu_type=$(uname -i)
    if [ "$cpu_type" == "x86_64" ]; then
        log "Check architecture - pass" ""
    else 
        echo "Tracker should be installed only on x86/64 CPU architecture. Exiting..."
        exit 1
    fi
}

check_cpu () {
    cpu_threads=$(grep -c ^processor /proc/cpuinfo)
    if [ "$cpu_threads" -lt 2 ] ; then
        echo "Tracker requires 2 or more CPU threads to operate. Exiting..."
        exit 1
    else 
        log "Check CPU - pass" ""
    fi
}

check_ram () {
    ram_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    if [ "$ram_total" -lt 3900000 ] ; then
        echo "Tracker requires 4GB of RAM on the server to operate. Exiting..."
        exit 1
    else 
        log "Check RAM - pass" ""
    fi
}

check_ram_and_update_env () {
    local env_file="${BINOM_FOLDER}/.env.init"
    
    if [ "$ram_total" -lt 8000000 ]; then
        log "RAM total is less than 8000000. Updating ${env_file}..." ""
        {
            echo "MAX_THREADS=1"
            echo "MAX_SERVER_MEMORY_USAGE_TO_RAM_RATIO=2.0"
            echo "CACHE_SIZE_TO_RAM_MAX_RATIO=0.1"
        } >> "$env_file"
    else
        log "RAM is more than 8000000. No update needed." ""
    fi
}

check_storage () {
    free_storage=$(df --output=avail "/var/lib" | tail -n 1)
    if  [[ "$free_storage" -lt 38000000 ]]; then
        echo "At least 40GB of free disk storage is required. Exiting..."
        exit 1
    else 
        log "Free storage check - pass" ""
    fi
}

check_firewall_and_ports () {
    log "Checking firewall and port availability..." ""
    local ports=(80 443 8088)
    
    # Check UFW and add rules if active
    if command -v ufw >/dev/null 2>&1; then
        if ufw status 2>/dev/null | head -n 1 | grep -q "Status: active"; then
            log "UFW is active, adding rules for ports: ${ports[*]}" "+"
            for port in "${ports[@]}"; do 
                ufw allow "$port" >/dev/null 2>&1 || true
            done
        fi
    fi
    
    # Check iptables for blocked ports (only if iptables is available)
    if command -v iptables >/dev/null 2>&1; then
        local blocked_ports=()
        for port in "${ports[@]}"; do
            if iptables -L INPUT -n 2>/dev/null | grep -q "DROP.*dpt:$port\|REJECT.*dpt:$port"; then
                blocked_ports+=("$port")
            fi
        done
        if [ ${#blocked_ports[@]} -gt 0 ]; then
            echo "Warning: Ports ${blocked_ports[*]} are blocked by iptables rules."
        fi
    fi
    
    # Check if ports are in use (use netstat as fallback if ss is not available)
    local used_ports=()
    local port_check_cmd=""
    
    if command -v ss >/dev/null 2>&1; then
        port_check_cmd="ss"
    elif command -v netstat >/dev/null 2>&1; then
        port_check_cmd="netstat"
    else
        log "Warning: Neither 'ss' nor 'netstat' found. Skipping port availability check." "+"
        log "Firewall and port check - pass" ""
        return 0
    fi
    
    for port in "${ports[@]}"; do
        local port_in_use=false
        local service_name=""
        
        if [ "$port_check_cmd" = "ss" ]; then
            if ss -tuln 2>/dev/null | grep -q ":$port "; then
                port_in_use=true
                service_name=$(ss -tulnp 2>/dev/null | grep ":$port " | head -n 1 | sed -n 's/.*users:((\([^,)]*\).*/\1/p' 2>/dev/null || echo "unknown")
            fi
        elif [ "$port_check_cmd" = "netstat" ]; then
            if netstat -tuln 2>/dev/null | grep -q ":$port "; then
                port_in_use=true
                service_name=$(netstat -tulnp 2>/dev/null | grep ":$port " | head -n 1 | awk '{print $7}' | cut -d'/' -f2 2>/dev/null || echo "unknown")
            fi
        fi
        
        if [ "$port_in_use" = true ]; then
            # Check if service is allowed for Binom tracker
            local allowed_service=false
            if { [ "$port" = "80" ] || [ "$port" = "443" ]; } && { [ "$service_name" = "caddy" ] || [ "$service_name" = "\"caddy\"" ]; }; then
                allowed_service=true
            elif [ "$port" = "8088" ] && { [ "$service_name" = "binom-observer" ] || [ "$service_name" = "\"binom-observer\"" ]; }; then
                allowed_service=true
            fi
            
            if [ "$allowed_service" = false ]; then
                used_ports+=("$port")
            fi
        fi
    done
    
    if [ ${#used_ports[@]} -gt 0 ]; then
        echo ""
        echo "ERROR: The following ports are already in use:"
        for port in "${used_ports[@]}"; do
            local service_name=""
            if [ "$port_check_cmd" = "ss" ]; then
                service_name=$(ss -tulnp 2>/dev/null | grep ":$port " | head -n 1 | sed -n 's/.*users:((\([^,)]*\).*/\1/p' 2>/dev/null || echo "unknown")
            elif [ "$port_check_cmd" = "netstat" ]; then
                service_name=$(netstat -tulnp 2>/dev/null | grep ":$port " | head -n 1 | awk '{print $7}' | cut -d'/' -f2 2>/dev/null || echo "unknown")
            fi
            echo "$port - $service_name"
        done
        echo ""
        echo "Please stop the services using these ports before installing Binom tracker."
        echo "Exiting installation..."
        log "Ports ${used_ports[*]} are already in use. Installation aborted." ""
        exit 1
    fi
    
    log "Firewall and port check - pass" ""
}

check_docker_version () {
    local current_version=""
    if command -v docker >/dev/null 2>&1; then
        current_version=$(dpkg-query -W -f='${Version}' docker-ce 2>/dev/null) || true
        if [[ -n "$current_version" ]]; then
            log "Current Docker version: $current_version" ""
            if [[ "$current_version" != "$DOCKER_VERSION" ]]; then
                log "Docker version mismatch. Required: $DOCKER_VERSION, Found: $current_version" ""
                return 1
            else
                log "Docker version is correct: $DOCKER_VERSION" ""
                return 0
            fi
        fi
    fi
    return 1
}

check_running_containers () {
    if command -v docker >/dev/null 2>&1; then
        local running_containers
        running_containers=$(docker ps -q 2>/dev/null | wc -l)
        if [ "$running_containers" -gt 0 ]; then
            log "Found $running_containers running container(s)" "+"
            cat << EOF

Warning! Active container/s found. This script will replace current docker version with the required one. It could break your current setup. Proceed?

EOF
            PS3=$'\nPlease choose an option: '
            options=("Yes" "No")
            select opt in "${options[@]}"; do
                case $opt in
                    "Yes")
                        return 0
                        ;;
                    "No")
                        echo "Installation cancelled."
                        exit 0
                        ;;
                    *)
                        echo "Please choose a valid option (1 or 2)"
                        ;;
                esac
            done
        fi
    fi
    return 0
}

remove_docker_packages () {
    log "Removing Docker packages to reinstall correct version..." "+"
    {
        log "Stopping Docker and containerd..." ""
        systemctl stop docker docker.socket containerd 2>/dev/null || true
        pkill -9 dockerd 2>/dev/null || true
        pkill -9 containerd 2>/dev/null || true

        log "Unholding Docker packages before removal..." ""
        for pkg in docker-ce docker-ce-cli docker-compose-plugin containerd.io docker-buildx-plugin docker-ce-rootless-extras; do
            apt-mark unhold "$pkg" 2>/dev/null || true
        done

        log "Removing Docker packages..." ""
        apt-get remove -y docker-ce docker-ce-cli docker-compose-plugin containerd.io docker-buildx-plugin docker-ce-rootless-extras 2>/dev/null || true
        apt-get purge -y docker-ce docker-ce-cli docker-compose-plugin containerd.io docker-buildx-plugin docker-ce-rootless-extras 2>/dev/null || true

        log "Reloading systemd..." ""
        systemctl daemon-reload
    } >> "${log_file}" 2>&1
    log "Docker packages removed successfully" ""
}

install_docker () {
    log "Installing docker..." "+"
    if ! [[ -f /etc/apt/sources.list.d/docker.list ]]; then
        {
            mkdir -p /etc/apt/keyrings
            rm -f /etc/apt/keyrings/docker.gpg
            curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
            chmod a+r /etc/apt/keyrings/docker.gpg
        } >> "${log_file}" 2>&1
        echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
    fi
    # Check if Docker is installed and if version matches
    if check_docker_version; then
        log "Docker is already installed with correct version: $DOCKER_VERSION" ""
        {
            systemctl enable docker containerd
            systemctl start docker containerd
        } >> "${log_file}" 2>&1 || true
    else
        if command -v docker >/dev/null 2>&1; then
            check_running_containers
            remove_docker_packages
        fi
        
        {
            apt-get -y update
            apt-get -y --allow-downgrades --allow-change-held-packages install \
                docker-ce="${DOCKER_VERSION}" \
                docker-ce-cli="${DOCKER_VERSION}" \
                containerd.io \
                docker-compose-plugin="${DOCKER_COMPOSE_VERSION}"
            systemctl enable docker containerd
            systemctl start docker containerd
        } >> "${log_file}" 2>&1
        log "Docker installed successfully" ""
    fi
    if ! [[ -f /usr/local/bin/compose-switch ]]; then
        {
            curl -fL https://github.com/docker/compose-switch/releases/latest/download/docker-compose-linux-amd64 -o /usr/local/bin/compose-switch
            chmod +x /usr/local/bin/compose-switch
            update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
        } >> "${log_file}" 2>&1
    fi
    # Create /etc/docker/daemon.json if not exists and set log driver
    if [ ! -f /etc/docker/daemon.json ] || [ ! -s /etc/docker/daemon.json ]; then
        echo '{ "log-driver": "journald" }' > /etc/docker/daemon.json
    elif ! grep -qF '"log-driver"' /etc/docker/daemon.json; then
        local tmp_daemon
        tmp_daemon=$(cat /etc/docker/daemon.json)
        echo "${tmp_daemon%\}}" > /etc/docker/daemon.json
        echo '    ,"log-driver": "journald"' >> /etc/docker/daemon.json
        echo '}' >> /etc/docker/daemon.json
    fi
    # Journald max file restriction
    if ! grep -q '^SystemMaxUse=' /etc/systemd/journald.conf 2>/dev/null; then
        echo "SystemMaxUse=2G" >> /etc/systemd/journald.conf
    fi
    systemctl restart systemd-journald
    # Wait for journald to be active
    jstatus=$(systemctl is-active systemd-journald)
    while [ "$jstatus" != "active" ]; do
        echo "$jstatus"
        sleep 2
        jstatus=$(systemctl is-active systemd-journald)
    done

    # Put Docker-related packages on hold to prevent accidental upgrades
    log "Putting Docker-related packages on hold to prevent upgrades..." ""
    for pkg in docker-ce docker-ce-cli docker-compose-plugin containerd.io; do
        if dpkg -s "$pkg" >/dev/null 2>&1; then
            apt-mark hold "$pkg" >> "${log_file}" 2>&1 || true
        fi
    done

    # Pin DOCKER_API_VERSION so that a newer compose plugin works with our pinned Docker CE daemon
    local api_ver
    api_ver=$(docker version --format '{{.Server.APIVersion}}' 2>/dev/null || echo "")
    if [ -n "$api_ver" ]; then
        export DOCKER_API_VERSION="$api_ver"

        # Interactive login shells
        echo "export DOCKER_API_VERSION=\"${api_ver}\"" > /etc/profile.d/binom_docker.sh

        # Non-interactive sessions (SSH commands, cron, scripts)
        if grep -q '^DOCKER_API_VERSION=' /etc/environment 2>/dev/null; then
            sed -i "s/^DOCKER_API_VERSION=.*/DOCKER_API_VERSION=${api_ver}/" /etc/environment
        else
            echo "DOCKER_API_VERSION=${api_ver}" >> /etc/environment
        fi

        log "DOCKER_API_VERSION set to ${api_ver} (via /etc/profile.d/ and /etc/environment)" ""
    else
        log "Warning: unable to detect Docker Server API version" ""
    fi
}

install_observer () {
    log "Installing observer..." "+"
    if ! [[ -f "${BINOM_FOLDER}/binom-observer" ]]; then
        {
            curl -L https://data.binom.org/binom-observer.tar.gz -o "${BINOM_FOLDER}/binom-observer.tar.gz"
            tar -xzf "${BINOM_FOLDER}/binom-observer.tar.gz" -C "${BINOM_FOLDER}"
            rm -rf "${BINOM_FOLDER}/binom-observer.tar.gz"
        } >> "${log_file}" 2>&1
    fi
    if ! [[ -f /etc/systemd/system/binom.service ]]; then
        {
            sed -i "s|REPLACE_WORKING_DIRECTORY|${BINOM_FOLDER}|g" "${BINOM_FOLDER}/binom.service"
            sed -i "s|REPLACE_EXEC_START|${BINOM_FOLDER}|g" "${BINOM_FOLDER}/binom.service"
            mv "${BINOM_FOLDER}"/binom.service /etc/systemd/system/
            systemctl daemon-reload
            systemctl enable binom.service
            systemctl start binom.service
        } >> "${log_file}" 2>&1
    fi
}

set_default_tracker_settings() {
    log "Installing the default settings..." "+"
    {
        cd "${BINOM_FOLDER}"/
        make -C "${BINOM_FOLDER}" -f "${BINOM_FOLDER}"/Makefile toggle-assign-offer-on-base-click
    } >> "${log_file}" 2>&1
}

check_availability () {
    # First starting of TD takes ~60sec
    sleep 60
    td_answer=$(curl -s localhost:80/status)
    while ! [[ "${td_answer}" == "available"  ]]; do
        echo "Waiting for TD service..."
        sleep 60
        td_answer=$(curl -s localhost:80/status)
    done
    # After available status we have to wait a bit longer
    sleep 20
}

check_v1_components () {
    local has_nginx=false
    local has_php=false
    local has_mysql=false
    
    # Check for nginx
    if command -v nginx >/dev/null 2>&1 || systemctl list-units --full -all | grep -q "nginx.service" || pgrep -x nginx >/dev/null 2>&1; then
        has_nginx=true
    fi
    
    # Check for php
    if command -v php >/dev/null 2>&1 || systemctl list-units --full -all | grep -q "php.*fpm" || pgrep php >/dev/null 2>&1; then
        has_php=true
    fi
    
    # Check for mysql/mariadb
    if command -v mysql >/dev/null 2>&1 || command -v mariadb >/dev/null 2>&1 || systemctl list-units --full -all | grep -qE "(mysql|mariadb).service" || pgrep -E "(mysql|mariadb)" >/dev/null 2>&1; then
        has_mysql=true
    fi
    
    # If all three components are found, show error and exit
    if [ "$has_nginx" = true ] && [ "$has_php" = true ] && [ "$has_mysql" = true ]; then
        cat<<EOF

Are you running v2 script on v1 tracker? Contact support.

Detected components:
EOF
        echo "- nginx"
        echo "- php"
        echo "- mysql/mariadb"
        echo ""
        exit 1
    fi
}

pass_generator () {
    symbols="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    lenght="$1"
    password=""
    while [ "${symbol:=1}" -le "$lenght" ]
    do
        password="${password}${symbols:$((RANDOM%${#symbols})):1}"
        ((symbol+=1))
    done
    symbol=0
    echo "${password}"
}

get_public_ipv4 () {
    local ip=""
    local -a ipv4_sources=(
        "https://api.ipify.org"
        "https://ipv4.icanhazip.com"
        "https://ifconfig.co/ip"
    )

    for src in "${ipv4_sources[@]}"; do
        ip="$(curl -4 -fsS --connect-timeout 2 --max-time 6 "$src" 2>/dev/null | tr -d '[:space:]' || true)"
        # Strict IPv4 validation: each octet must be 0–255
        if [[ "$ip" =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$ ]]; then
            echo "$ip"
            return 0
        fi
    done

    return 1
}

get_tracker_url () {
    local ip
    ip="$(get_public_ipv4 || true)"

    # If external services are blocked, fall back to local routing info
    if [[ -z "$ip" ]]; then
        # Extract source address from route output (token after "src"), not destination.
        ip="$(ip route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i == "src" && (i+1)<=NF) {print $(i+1); exit}}')"
    fi

    # Extra fallback via hostname -I (non-loopback)
    if [[ -z "$ip" ]]; then
        ip="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i !~ /^127\./) {print $i; exit}}')"
    fi

    if [[ -z "$ip" ]]; then
        log "Failed to determine IP address. Please use real IP" ""
        echo "http://REPLACE_WITH_REAL_IP/index"
        return 0
    fi

    echo "http://$ip/index"
}

check_google_ban () {
    IP_ADDRESS="$(get_public_ipv4 || true)"
    if [[ -z "${IP_ADDRESS}" ]]; then
        log "Failed to detect public IPv4 via external services. Skipping ban check." ""
        return 0
    fi

    CHECKER_URL="https://checker.binom.lgbt/check?url="
    FULL_URL="${CHECKER_URL}${IP_ADDRESS}"
    CHECKER_PING="https://checker.binom.lgbt/ping"
    CHECKER_RESPONSE="$(curl -fsS --connect-timeout 2 --max-time 6 "$CHECKER_PING" 2>/dev/null || true)"
    if [ "$CHECKER_RESPONSE" = "pong" ]; then
        log "Checker host is up" ""
        BAN_CHECK_RESPONSE="$(curl -fsS --connect-timeout 2 --max-time 8 "$FULL_URL" 2>/dev/null || true)"
        if [ "$BAN_CHECK_RESPONSE" = "true" ]; then
            cat << EOF
!!!
It appears that the server's IP address is on Google's blacklist.
This can negatively affect the tracker's performance.
It is strongly recommended to change the IP address or use a different server.
For more details, please contact your hosting provider or technical support.

Continue installation?

EOF
            PS3=$'\nPlease choose an option: '
            options2=("Yes" "No")
            select opt2 in "${options2[@]}"; do
                case $opt2 in
                    "Yes")
                        log "Continuing installation" "+"
                        break
                        ;;
                    "No")
                        log "Installation aborted" "+"
                        exit 1
                        ;;
                esac
            done
        else
            log "IP address is clear" ""
        fi
    else
        log "No answer from the checker host. Skipping ban check." ""
    fi
}

install_magicchecker () {
    log "Installing MagicChecker..." "+"
    {
        mkdir /usr/share/mcproxy
        wget --no-check-certificate https://clients.magicchecker.com/files/proxy/mcsproxy.zip -O /usr/share/mcproxy/mcsproxy.zip
    } >> "${log_file}" 2>&1
    if [[ -f /usr/share/mcproxy/mcsproxy.zip ]]; then
        {
            apt-get -y update
            apt-get -y install unzip net-tools
            cd /usr/share/mcproxy/ && unzip mcsproxy.zip && rm mcsproxy.zip
        } >> "${log_file}" 2>&1
        interface=$(ip addr | awk '{print $2}' | grep "^.*:$" | grep -v "lo" | sed 's/://g' | head -n 1)
        {
            sed -i "s/eth0/${interface}/g" /usr/share/mcproxy/app.json
            bash /usr/share/mcproxy/start.sh
        } >> "${log_file}" 2>&1
    else
        log "Download error. Please check ${log_file} for detailed information or contact Binom support." "+"
        exit 1
    fi

    {
        mkdir /usr/share/binom-magic-proxy
        wget https://data.binom.org/magic-proxy -O /usr/share/binom-magic-proxy/magic-proxy
        chmod +x /usr/share/binom-magic-proxy/magic-proxy

        echo "[Unit]
Description=Magic Proxy
After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service

StartLimitIntervalSec=500
StartLimitBurst=5

[Service]
Restart=on-failure
RestartSec=5s

ExecStart=/usr/share/binom-magic-proxy/magic-proxy -b 172.17.0.1 -l 5000 -h 127.0.0.1 -p 8082 -f

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/magic-proxy.service

        systemctl daemon-reload
        systemctl enable magic-proxy.service
        systemctl start magic-proxy.service

    } >> "${log_file}" 2>&1
}

put_stable_tag () {
    stable_tag=$1
    # Extract JSON part from the response (skip HTTP headers) and get the tag
    sed -n '/^{/,$p' "${BINOM_FOLDER}"/.tags_tmp | tr -d '\n' | sed -n "s/.*\"name\":$stable_tag,\"tag\":\"\([^\"]*\)\".*/\1/p"
}

check_email () {
    while true; do
        echo -e "\nPlease enter your email address or type 'exit' to quit:"
        read -r BINOM_EMAIL
        [[ "$BINOM_EMAIL" == "exit" ]] && exit 0
        if echo "${BINOM_EMAIL}" | grep -Eo '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z0-9.-]{2,20}\b' > /dev/null 2>&1; then
            break
        else
            cat <<EOF
The email is invalid.
You may only use these symbols: a-Z, 0-9, . - _ @
EOF
        fi
    done
}

find_tracker_directory () {
    local found_dir=""
    local search_dirs=("/root" "/home" "/opt")
    local required_files=("Makefile" "clickhouse" "binom.service")
    for base in "${search_dirs[@]}"; do
        while IFS= read -r -d '' dir; do
            local all_found=1
            for required_file in "${required_files[@]}"; do
                if [[ "$required_file" == "binom.service" ]]; then
                    if [[ ! -e "$dir/binom.service" && ! -e "$dir/binom-observer" ]]; then
                        all_found=0
                        break
                    fi
                else
                    if [[ ! -e "$dir/$required_file" ]]; then
                        all_found=0
                        break
                    fi
                fi
            done
            if (( all_found )); then
                found_dir="$dir"
                break 2
            fi
        done < <(find "$base" -maxdepth 2 -type d -print0 2>/dev/null)
    done

    if [[ -z "$found_dir" ]]; then
        while true; do
            read -rp "Tracker files not found in /root, /home, and /opt. Please enter the path to the folder manually: " user_path
            if [[ -d "$user_path" ]]; then
                local all_found=1
                for required_file in "${required_files[@]}"; do
                    if [[ "$required_file" == "binom.service" ]]; then
                        if [[ ! -e "$user_path/binom.service" && ! -e "$user_path/binom-observer" ]]; then
                            all_found=0
                            break
                        fi
                    else
                        if [[ ! -e "$user_path/$required_file" ]]; then
                            all_found=0
                            break
                        fi
                    fi
                done
                if (( all_found )); then
                    found_dir="$user_path"
                    break
                else
                    echo "Required files not found at $user_path. Try again."
                fi
            else
                echo "Directory $user_path does not exist. Try again."
            fi
        done
    fi
    echo "$found_dir"
}

check_second_install () {
    if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1 && docker ps -a --filter "name=binom" --format '{{.Names}}' 2>/dev/null | grep -q .; then
        echo ""
        echo "Binom 2.0 is already installed. Do you want to delete the tracker files?"
        PS3=$'\nPlease choose an option: '
        options=("Yes" "No")
        select choice in "${options[@]}"; do
            case $choice in
                "Yes")
                    if [[ -z ${BINOM_EMAIL} ]]; then
                        check_email
                    fi
                    log "Deleting old tracker files..." "+"
                    # Stop observer if running
                    if systemctl list-units --full -all | grep -Fq "binom-observer"; then
                        if systemctl is-active --quiet "binom.service"; then
                            systemctl stop "binom.service"
                        fi
                    fi
                    # Search and remove old tracker folder using find_tracker_directory
                    FOUND_DIR=$(find_tracker_directory)
                    if [[ -n "$FOUND_DIR" ]]; then
                        rm -rf "$FOUND_DIR" && log "Old Binom folder at $FOUND_DIR has been deleted" ""
                    fi
                    # Remove docker containers and related objects
                    if docker ps -a --filter "name=binom" --format '{{.Names}}' | grep -q .; then
                        for old_container in $(docker ps -a --format '{{.Names}}' | grep "binom"); do
                            docker stop "$old_container" >> /dev/null && docker rm -v "$old_container" >> /dev/null
                            log "$old_container and its volume have been deleted" ""
                        done
                        docker volume ls -qf name="$(basename "$FOUND_DIR")" | xargs -r docker volume rm > /dev/null 2>&1
                        docker network ls | grep binom | awk '{print $1}' | xargs docker network rm > /dev/null 2>&1
                    else
                        log "No previously installed containers found." ""
                    fi
                    if [[ -f /usr/share/mcproxy/start.sh ]]; then
                        bash /usr/share/mcproxy/stop.sh >> "${log_file}" 2>&1
                        rm -rf /usr/share/mcproxy >> "${log_file}" 2>&1
                    else
                        log "stop.sh not found!" ""
                    fi
                    if [[ -f /usr/share/binom-magic-proxy/magic-proxy ]]; then
                        systemctl stop magic-proxy.service
                        systemctl disable magic-proxy.service > /dev/null 2>&1
                        rm -rf /usr/share/binom-magic-proxy
                        rm -rf /usr/share/mcproxy >> "${log_file}" 2>&1
                    fi
                    log "Magic Checker has been removed." ""
                    break
                    ;;
                "No")
                    echo "Exiting installer"
                    exit 0
                    ;;
                *)
                    echo "Please choose a valid option (1 or 2)"
                    ;;
            esac
        done
    else
        log "Tracker hasn't been installed yet. Skipping..." ""
    fi
}

uninstall_tracker () {
    local BINOM_FOLDER_DEFAULT="/root/binom"
    local BINOM_FOLDER=""

    echo ""
    echo "WARNING: This will completely uninstall Binom 2.0 and remove all its files, services, docker containers, images, volumes, and related directories."
    PS3=$'\nPlease choose an option: '
    options=("Yes" "No")
    select choice in "${options[@]}"; do
        case $choice in
            "Yes")
                break
                ;;
            "No")
                echo "Uninstallation cancelled."
                exit 0
                ;;
            *)
                echo "Please choose a valid option."
                ;;
        esac
    done

    log "Starting uninstallation..." "+"

    echo "Stopping and disabling systemd services..."
    if systemctl is-active --quiet binom.service; then
        systemctl stop binom.service
    fi
    systemctl disable binom.service 2>/dev/null
    rm -f /etc/systemd/system/binom.service
    log "binom.service has been removed." ""

    if systemctl list-units --full -all | grep -Fq "magic-proxy.service"; then
        if systemctl is-active --quiet magic-proxy.service; then
            systemctl stop magic-proxy.service
        fi
        systemctl disable magic-proxy.service 2>/dev/null
        rm -f /etc/systemd/system/magic-proxy.service
        log "magic-proxy.service has been removed." ""
    fi
    systemctl daemon-reload

    echo "Removing tracker folder..."
    if [[ -z "$BINOM_FOLDER" || ! -d "$BINOM_FOLDER" ]]; then
        BINOM_FOLDER=$(find_tracker_directory)
    fi

    if [[ -d "$BINOM_FOLDER" ]]; then
        rm -rf "$BINOM_FOLDER" && log "Tracker folder at $BINOM_FOLDER has been removed." ""
    else
        echo "Tracker folder not found."
    fi

    if [[ -d /usr/share/mcproxy ]]; then
        [[ -f /usr/share/mcproxy/stop.sh ]] && bash /usr/share/mcproxy/stop.sh >> "${log_file}" 2>&1
        rm -rf /usr/share/mcproxy
        echo "MagicChecker (mcproxy) has been removed."
    fi

    if [[ -d /usr/share/binom-magic-proxy ]]; then
        systemctl stop magic-proxy.service 2>/dev/null
        systemctl disable magic-proxy.service 2>/dev/null
        rm -rf /usr/share/binom-magic-proxy
        echo "Binom Magic Proxy has been removed."
    fi

    for old_container in $(docker ps -a --filter "name=binom" --format '{{.Names}}'); do
            docker stop "$old_container" >> /dev/null 2>&1 && docker rm -v "$old_container" >> /dev/null 2>&1
            log "Docker container $old_container has been removed." ""
    done
    docker volume ls -qf name="$(basename "$BINOM_FOLDER")" | xargs -r docker volume rm > /dev/null 2>&1
    docker network ls | grep binom | awk '{print $1}' | xargs docker network rm > /dev/null 2>&1

    echo "Removing docker images..."
    mapfile -t images_to_remove < <(docker images --format "{{.Repository}} {{.ID}}" | grep -i "binom" | awk '{print $2}' | sort -u)
    if (( ${#images_to_remove[@]} > 0 )); then
        docker rmi -f "${images_to_remove[@]}" >> "${log_file}" 2>&1
        log "Docker images for binom have been removed." ""
    fi

    if command -v docker >/dev/null 2>&1; then
        echo ""
        echo "Do you want to remove docker and all its plugins?"
        PS3=$'\nPlease, choose an option: '
        options_docker=("Yes" "No")
        select docker_choice in "${options_docker[@]}"; do
            case "$docker_choice" in
                "Yes")
                    log "Removing Docker and all its plugins..." "+"

                    # Deep Docker cleanup - begin
                    {
                        log "Stopping Docker and containerd..." ""
                        systemctl stop docker docker.socket containerd 2>/dev/null || true
                        pkill -9 dockerd 2>/dev/null || true
                        pkill -9 containerd 2>/dev/null || true

                        log "Purge Docker packages..." ""
                        apt-get purge -y docker-engine docker docker.io docker-ce docker-ce-cli docker-compose-plugin containerd.io docker-buildx-plugin docker-ce-rootless-extras
                        apt-get autoremove -y --purge docker-engine docker docker.io docker-ce docker-ce-cli docker-compose-plugin containerd.io docker-buildx-plugin docker-ce-rootless-extras

                        log "Remove leftover directories and config files..." ""
                        rm -rf /var/lib/docker /etc/docker
                        rm -rf /var/lib/containerd
                        rm -f /etc/apt/keyrings/docker.gpg
                        rm -f /etc/apt/sources.list.d/docker.list
                        rm -rf /var/log/docker*
                        rm -f /usr/local/bin/docker-compose
                        rm -f /usr/local/bin/compose-switch
                        rm -f /etc/systemd/system/docker.service /etc/systemd/system/docker.socket
                        rm -f /usr/bin/docker /usr/bin/docker-containerd /usr/bin/docker-containerd-ctr \
                              /usr/bin/docker-containerd-shim /usr/bin/docker-runc /usr/bin/containerd /usr/bin/containerd-shim

                        log "Remove docker group and env config..." ""
                        groupdel docker 2>/dev/null || true
                        rm -f /etc/profile.d/binom_docker.sh
                        sed -i '/^DOCKER_API_VERSION=/d' /etc/environment 2>/dev/null || true

                        log "[*] Reload systemd..." ""
                        systemctl daemon-reload
                        
                        if ip link show docker0 &>/dev/null; then
                            echo "[*] Deleting docker0 interface..."
                            ip link set docker0 down 2>/dev/null || true
                            ip link delete docker0 2>/dev/null || true
                        fi

                        # echo "[*] Clearing iptables NAT rules..."
                        # iptables -t nat -F
                        # iptables -t filter -F DOCKER
                        # iptables -t filter -F DOCKER-USER
                        # iptables -t nat -X DOCKER
                        # iptables -t filter -X DOCKER
                        # iptables -t filter -X DOCKER-USER
                    } >> "${log_file}" 2>&1
                    # Deep Docker cleanup - end

                    echo "Docker and all its plugins have been removed."
                    break
                    ;;
                "No")
                    break
                    ;;
                *)
                    echo "Please choose a valid option."
                    ;;
            esac
        done
    fi

    if [[ -d /var/log/binom ]]; then
        rm -rf /var/log/binom
        echo "Log files have been removed."
    fi

    echo ""
    echo "Uninstallation complete."
    exit 0
}

get_tracker_id () {
    local log_line
    log_line=$(docker logs binom_traffic_distribution 2>&1 | grep "Tracker ID:" | tail -n 1)
    local tracker_id
    tracker_id=$(echo "$log_line" | grep -oP 'ID:\K[0-9a-fA-F\-]+')
    echo "$tracker_id"
}

caddy_check_n_reload () {
  # Format the Caddyfile
  docker exec binom_proxy caddy fmt --overwrite /etc/caddy/Caddyfile >> "${log_file}" 2>&1
  # Validate the configuration
  output=$(docker exec binom_proxy caddy validate --config /etc/caddy/Caddyfile 2>&1)
  echo "$output" >> "${log_file}"  # Log the output of the validation
  # Check the result of the validation
  if echo "$output" | grep -q "Valid configuration"; then
    echo "Configuration is valid, reloading Caddy..."
    docker exec binom_proxy caddy reload --config /etc/caddy/Caddyfile >> "${log_file}" 2>&1
    echo "Success"
  else
    echo "Serious errors in the configuration:"
    echo "$output"
    exit 1
  fi
}

create_domain_directory () {
    local DOMAIN=$1
    local DOMAIN_DIR="/var/lib/docker/volumes/binom_landers_data/_data/${DOMAIN}"

    if [[ -d "$DOMAIN_DIR" ]]; then
        echo "Directory for domain $DOMAIN already exists. Skipping."
    else
        mkdir -p "$DOMAIN_DIR"
        chown 82:82 "$DOMAIN_DIR"
        echo "Directory $DOMAIN_DIR created successfully"
    fi
}

add_domain_config () {
  local DOMAIN=$1
  local CONFIG_FILE="${CUSTOM_DOMAIN_CONFIG_DIR}/${DOMAIN}.caddy"
  
  if [[ -f "$CONFIG_FILE" ]]; then
    echo "Config for domain $DOMAIN already exists. Skipping."
  else
    cat <<EOL > "$CONFIG_FILE"
http://$DOMAIN,
https://$DOMAIN {
        reverse_proxy {env.PROXY_FRONTEND_URL}
        rewrite * /landers/$DOMAIN/{path}

        log {
                level ERROR
                output file /var/log/${DOMAIN}.error.log
        }
}
EOL
    echo "Config for domain $DOMAIN added."
    create_domain_directory "$DOMAIN"  # Create domain directory
    caddy_check_n_reload
  fi  
}

remove_domain_config () {
  local DOMAIN=$1
  local CONFIG_FILE="${CUSTOM_DOMAIN_CONFIG_DIR}/${DOMAIN}.caddy"
  
  if [[ -f "$CONFIG_FILE" ]]; then
    rm "$CONFIG_FILE"
    echo "Config for domain $DOMAIN removed."
    caddy_check_n_reload
  else
    echo "Config for domain $DOMAIN does not exist. Skipping."
  fi
}

domain_add_menu () {
    echo "Select an action:"
    PS3='Enter the number of the action: '
    options=("Add config" "Remove config")
    select opt in "${options[@]}"
    do
        case $opt in
            "Add config")
                read -rp "Enter domains (e.g., domain1.com domain2.com): " -a DOMAINS
                for DOMAIN in "${DOMAINS[@]}"; do
                    add_domain_config "$DOMAIN"
                done
                break
                ;;
            "Remove config")
                read -rp "Enter domains (e.g., domain1.com domain2.com): " -a DOMAINS
                for DOMAIN in "${DOMAINS[@]}"; do
                    remove_domain_config "$DOMAIN"
                done
                break
                ;;
            *) echo "Invalid option $REPLY";;
        esac
    done
}

help () {
    cat<<EOF

Installer for Binom v2 (v.${VERSION})

Usage: bash binom_v2_install.sh [OPTION]...

To install Binom tracker run the script with the argument:

    i, install                             - To install Binom 2.0

    Options for install:

        -e="admin@foo.com",
            --email="admin@foo.com"        - To set an email for SSL directly.

        -d="/tracker/directory"
            --dir="/tracker/directory"     - To set a custom directory for the tracker. Default path: /root/binom/

        --magicchecker                     - Install with Magic Checker Super Filter.

        --help                             - Display this help and exit.

To install additional software run the script with one argument:

    m, magicchecker                        - To install/remove MagicChecker Super Filter

To add custom domain not directly related to the tracker run:

    l, lpdomain                            - Add or remove custom domain (lpdomain function)

To uninstall Binom tracker run the script with the argument:

    u, uninstall 
                              - To completely remove Binom 2.0 and all installed files, services, and directories.
EOF
}

mkdir -p /var/log/binom

install_tracker() {
    BINOM_FOLDER_DEFAULT=/root/binom
    BINOM_FOLDER=""
    BUILD=""
    MAGICCHECKER=""
    NO_PASS_REMOVE=""

    check_architecture
    check_cpu
    check_ram
    check_storage
    check_firewall_and_ports
    check_google_ban

    for arg in "$@" ; do
        case "$arg" in
            --email=*|-e=*)
                BINOM_EMAIL="${arg#*=}"
                ;;
            --dir=*|-d=*)
                BINOM_FOLDER="${arg#*=}"
                ;;
            --magicchecker)
                MAGICCHECKER=1
                ;;
            --help)
                help
                exit
                ;;
            --no-pass-remove)
                NO_PASS_REMOVE=1
                ;;
            --dev)
                BUILD=dev/
                ;;
            *)
                ;;
        esac
    done

    if [[ -z ${BINOM_FOLDER} ]]; then
        BINOM_FOLDER="${BINOM_FOLDER_DEFAULT}"
    fi

    check_second_install

    if [[ -z ${BINOM_EMAIL} ]]; then
        check_email
    fi

    log "Installing dependencies..." "+"
    apt-get -y update >> "${log_file}" 2>&1
    apt-get -y install \
        make \
        ca-certificates \
        curl \
        gnupg \
        lsb-release \
        iproute2 >> "${log_file}" 2>&1

    install_docker

    log "Installing tracker..." "+"
    {
        curl -sSL "https://data.binom.org/${BUILD}binom_v2_install.tar.gz" -o /tmp/binom_v2_install.tar.gz
        mkdir -p "${BINOM_FOLDER}"
        tar xvzf /tmp/binom_v2_install.tar.gz -C "${BINOM_FOLDER}"
    } >> "${log_file}" 2>&1

    check_ram_and_update_env
    
    {
        tag_key=$(awk -F'api_auth_token=' '/api_auth_token=/{print $2}' "${BINOM_FOLDER}"/binom.service)
        touch "${BINOM_FOLDER}"/.tags_tmp
        curl -fsSL "https://tinker.binom.org/install/version" -H "Authorization: Bearer $tag_key" > "${BINOM_FOLDER}"/.tags_tmp
    }

    TRAFFIC_DISTRIBUTION_TOKEN=$(pass_generator 32)
    sed -i 's/REPLACE_TRAFFIC_DISTRIBUTION_TOKEN/'"${TRAFFIC_DISTRIBUTION_TOKEN}"'/' "${BINOM_FOLDER}"/.env*

    POSTGRES_PASSWORD=$(pass_generator 32)
    sed -i 's/REPLACE_POSTGRES/'"${POSTGRES_PASSWORD}"'/' "${BINOM_FOLDER}"/.env* "${BINOM_FOLDER}"/clickhouse/*

    CLICKHOUSE_PASSWORD=$(pass_generator 32)
    sed -i 's/REPLACE_CLICKHOUSE/'"${CLICKHOUSE_PASSWORD}"'/' "${BINOM_FOLDER}"/.env*

    BINOM_PASSWORD=$(pass_generator 32)
    sed -i 's/--password\ REPLACE_BINOM/--password\ '"${BINOM_PASSWORD}"'/' "${BINOM_FOLDER}"/Makefile

    sed -i 's/REPLACE_EMAIL/'"${BINOM_EMAIL}"'/' "${BINOM_FOLDER}"/.env*
    sed -i 's/REPLACE_EMAIL/'"${BINOM_EMAIL}"'/' "${BINOM_FOLDER}"/Makefile

    AUTH_SECRET=$(pass_generator 32)
    sed -i 's/REPLACE_AUTH_SECRET/'"${AUTH_SECRET}"'/' "${BINOM_FOLDER}"/.env*

    APP_SECRET=$(pass_generator 32)
    sed -i 's/REPLACE_APP_SECRET/'"${APP_SECRET}"'/' "${BINOM_FOLDER}"/.env*

    if [[ ${MAGICCHECKER} = 1 ]]; then
        install_magicchecker
    fi

    {
        mv "${BINOM_FOLDER}"/.env.init "${BINOM_FOLDER}"/.env
        cd "${BINOM_FOLDER}"/
        make -C "${BINOM_FOLDER}" -f "${BINOM_FOLDER}"/Makefile \
            REGISTRY=gcr.io/pr-binom \
            FRONTEND_TAG="$(put_stable_tag 1)" \
            BACKEND_TAG="$(put_stable_tag 2)" \
            CLICKHOUSE_TAG="$(put_stable_tag 3)" \
            TD_TAG="$(put_stable_tag 4)" \
            PROXY_TAG="$(put_stable_tag 5)" \
            POSTGRES_TAG="$(put_stable_tag 6)" \
            PROTECT_TAG="$(put_stable_tag 7)" \
            init
    } >> "${log_file}" 2>&1

    install_observer
    check_availability
    make create-super-admin >> "${log_file}" 2>&1

    {
        cat << EOF > "${BINOM_FOLDER}"/.binom 
$(get_tracker_url)
Login: admin
Password: ${BINOM_PASSWORD}
Tracker ID: $(get_tracker_id)
EOF
    }
    set_default_tracker_settings

    chmod 600 "${BINOM_FOLDER}"/.binom
    if [[ -z ${NO_PASS_REMOVE} ]]; then
        nohup bash -c "sleep 7200 && rm -f '${BINOM_FOLDER}/.binom'" >>/dev/null 2>&1 &
    fi

    echo ""
    echo "Done!"
    echo "Your tracker access:"
    echo ""
    cat "${BINOM_FOLDER}"/.binom
    echo ""
    exit 0
}

if [[ -z "$*" ]]; then
    help
    exit 0
fi

if (( "$(id -u)" != 0 )); then
    cat<<EOF

Current user is not root.

EOF
    exit 0
fi

if [[ -f /etc/os-release ]]; then
    # shellcheck source=/dev/null
    case "$(source /etc/os-release && echo "${VERSION_ID}")" in
        22.04)
            DOCKER_VERSION="5:23.0.4-1~ubuntu.22.04~jammy"
            DOCKER_COMPOSE_VERSION="2.17.3-1~ubuntu.22.04~jammy"
            ;;
        24.04)
            DOCKER_VERSION="5:27.3.1-1~ubuntu.24.04~noble"
            DOCKER_COMPOSE_VERSION="2.29.7-1~ubuntu.24.04~noble"
            ;;
        *)
            cat<<EOF

Invalid OS.
Only Ubuntu 22.04/24.04 are supported.

EOF
            exit 0
            ;;
    esac
else
    cat<<EOF

Invalid OS.
Only Ubuntu 22.04/24.04 are supported.

EOF
    exit 0
fi

case $1 in
    l|lpdomain)
        domain_add_menu
        ;;
    m|magicchecker)
        check_v1_components
        if ss -ntulap | grep -q ':8082' || [[ -d /usr/share/mcproxy ]]; then
            echo ""
            echo "Magic Checker is already installed."
            echo "Do you want to remove it?"
            PS3=$'\nPlease choose an option: '
            options=("Yes" "No")
            select opt in "${options[@]}"
            do
                case $opt in
                    "Yes")
                        echo ""
                        cat << EOF
Removing Magic Checker will require a restart of the tracker with an estimated downtime of 3-5 minutes.
Continue?
EOF
                        PS3=$'\nPlease choose an option: '
                        options2=("Yes" "No")
                        select opt2 in "${options2[@]}"
                        do
                            case $opt2 in
                                "Yes")
                                    if [[ -f /usr/share/mcproxy/stop.sh ]]; then
                                        bash /usr/share/mcproxy/stop.sh >> "${log_file}" 2>&1
                                        rm -rf /usr/share/mcproxy >> "${log_file}" 2>&1
                                    else
                                        echo "stop.sh not found!"
                                    fi
                                    if [[ -f /usr/share/binom-magic-proxy/magic-proxy ]]; then
                                        systemctl stop magic-proxy.service
                                        systemctl disable magic-proxy.service
                                        rm -rf /usr/share/binom-magic-proxy
                                    fi
                                    docker restart binom_traffic_distribution >> "${log_file}" 2>&1
                                    echo "Done!"
                                    exit 0
                                    ;;
                                "No")
                                    exit 0
                                    ;;
                            esac
                        done
                        ;;
                    "No")
                        exit 0
                        ;;
                esac
            done
        else
            echo ""
            cat << EOF
Installing Magic Checker will require a restart of the tracker with an estimated downtime of 3-5 minutes.
Continue with the installation?
EOF
            PS3=$'\nPlease choose an option: '
            options=("Yes" "No")
            select opt in "${options[@]}"
            do
                case $opt in
                    "Yes")
                        install_magicchecker
                        docker restart binom_traffic_distribution >> "${log_file}" 2>&1
                        echo "Done!"
                        exit 0
                        ;;
                    "No")
                        exit 0
                        ;;
                esac
            done
        fi
        ;;
    i|install)
        check_v1_components
        install_tracker "$@"
        ;;
    u|uninstall)
        check_v1_components
        uninstall_tracker 
        ;;
    h|help)
        help
        ;;
    *)
        echo ""
        echo "Invalid argument. Please refer to the help instructions."
        echo ""
        help
        ;;
esac