#!/bin/bash

set -euo pipefail
set -x

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"

# If the user is not root, use sudo
SUDOCMD=""
[ "$EUID" -eq 0 ] || SUDOCMD="sudo"

# If the user is not root, use the home directory of the user who ran the script
USER_HOME="$HOME"
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
    USER_HOME=$(eval echo "~${SUDO_USER}")
fi

# test sudo is working or we're root (return non-zero if not root!)
if ! ${SUDOCMD:-} true; then
    echo "Error: This script must be run as root or with sudo privileges." >&2
    exit 1
fi

_die() {
    echo -e "Error: $1" >&2
    exit 1
}

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Function to print status messages
print_status() {
    echo -e "${GREEN}[*] $1${NC}"
}

print_error() {
    echo -e "${RED}[!] $1${NC}"
}

print_warning() {
    echo -e "${YELLOW}[!] $1${NC}"
}



#----------------------------------------------------------------------------------------------------------
# Headers on Host (Native)
#----------------------------------------------------------------------------------------------------------


# Function to check if a package is installed
is_package_installed() {
    if [ "$OS" = "Alpine Linux" ]; then
        apk info | grep -q "^$1$"
    else
        dpkg -l "$1" 2>/dev/null
    fi
}
function install_packages() {
    local PACKAGES
    local HAVE_UPDATED=0

    # Detect distribution
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS=$NAME
        VER=$VERSION_ID
    else
        print_error "Could not detect distribution"
        exit 1
    fi

    print_status "Detected OS: $OS $VER"

    # Define packages based on distribution
    case $OS in
        "Ubuntu"|"Debian GNU/Linux")
            # Common packages for both Ubuntu and Debian
            PACKAGES="build-essential cmake git wget tar curl ninja-build mold nodejs npm perl"
            INSTALLCMD="apt-get install -y"
            UPDATECMD="apt-get update"
            ;;
        "Alpine Linux")
            PACKAGES="build-base cmake git wget tar curl ninja mold nodejs npm linux-headers perl"
            INSTALLCMD="apk add --no-cache"
            UPDATECMD="apk update"
            ;;
        *)
            print_error "Unsupported distribution: $OS"
            exit 1
            ;;
    esac

    # Install missing packages
    print_status "Checking and installing required packages..."
    for pkg in $PACKAGES; do
        if ! is_package_installed "$pkg"; then
            print_status "Installing $pkg..."

            # Update package lists
            if [ "$HAVE_UPDATED" -eq 0 ]; then
                print_status "Updating package lists..."
                ${SUDOCMD:-} bash -c "${UPDATECMD}"
                HAVE_UPDATED=1
            fi

            if ! bash -c "${SUDOCMD:-} ${INSTALLCMD} $pkg"; then
                print_error "Failed to install $pkg"
                exit 1
            fi
        else
            print_status "$pkg is already installed"
        fi
    done

}

function install_headers() {
    # put libassert headers on the host.
    if [ ! -f /usr/local/lib/libassert.a ]; then
        git clone https://github.com/jeremy-rifkin/libassert.git
        #git checkout v2.1.5
        mkdir libassert/build
        cd "libassert/build" || exit 1
        cmake .. -DCMAKE_BUILD_TYPE=Release
        make -j
        ${SUDOCMD:-} make install
        cd ../..
        rm -rf libassert
    fi
}


#----------------------------------------------------------------------------------------------------------
# OpenSSL for musl cross toolchains
#----------------------------------------------------------------------------------------------------------

function install_openssl_musl() {
    OPENSSL_VERSION=3.5.0

    SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    BUILD_DIR="${SCRIPT_DIR}/build"
    MUSL_CROSS_DIR="$HOME/.musl-cross"
    mkdir -p "$BUILD_DIR"

    # Helper: compare versions (returns 0 if $1 >= $2)
    version_ge() {
        [ "$1" = "$2" ] && return 0
        [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
    }

    # Find all installed musl cross toolchains
    TOOLCHAINS=()
    for tc in "$MUSL_CROSS_DIR"/*-linux-musl-cross; do
        [ -d "$tc" ] || continue
        arch=$(basename "$tc" | sed 's/-linux-musl-cross//')
        TOOLCHAINS+=("$arch")
    done

    if [ ${#TOOLCHAINS[@]} -eq 0 ]; then
        echo "No musl cross toolchains found in $MUSL_CROSS_DIR. Exiting."
        exit 1
    fi

    echo "Found musl cross toolchains: ${TOOLCHAINS[*]}"

    for ARCH in "${TOOLCHAINS[@]}"; do
        echo "==============================="
        echo "Checking OpenSSL for $ARCH..."
        echo "==============================="
        if [ "$ARCH" = "x86_64" ]; then
            MUSL_PREFIX="$MUSL_CROSS_DIR/x86_64-linux-musl-cross"
            OPENSSL_TARGET=linux-x86_64
        elif [ "$ARCH" = "aarch64" ]; then
            MUSL_PREFIX="$MUSL_CROSS_DIR/aarch64-linux-musl-cross"
            OPENSSL_TARGET=linux-aarch64
        else
            # Try to guess OpenSSL target for other musl toolchains
            OPENSSL_TARGET="linux-$ARCH"
            MUSL_PREFIX="$MUSL_CROSS_DIR/${ARCH}-linux-musl-cross"
        fi
        SYSROOT="$MUSL_PREFIX/${ARCH}-linux-musl/sysroot"
        CC="$MUSL_PREFIX/bin/${ARCH}-linux-musl-gcc"
        AR="$MUSL_PREFIX/bin/${ARCH}-linux-musl-ar"
        RANLIB="$MUSL_PREFIX/bin/${ARCH}-linux-musl-ranlib"

        SKIP_BUILD=0
        if [ -f "$SYSROOT/usr/lib/libssl.a" ]; then
            # Try to extract version from opensslv.h
            OPENSSLV_H="$SYSROOT/usr/include/openssl/opensslv.h"
            if [ -f "$OPENSSLV_H" ]; then
                INSTALLED_VERSION=$(grep '# *define *OPENSSL_VERSION_TEXT' "$OPENSSLV_H" | sed -E 's/.*OpenSSL ([0-9.]+)[^ ]*.*/\1/')
                if [ -n "$INSTALLED_VERSION" ]; then
                    echo "Found installed OpenSSL version: $INSTALLED_VERSION"
                    if version_ge "$INSTALLED_VERSION" "$OPENSSL_VERSION"; then
                        echo "OpenSSL $INSTALLED_VERSION is up-to-date (>= $OPENSSL_VERSION), skipping build for $ARCH."
                        SKIP_BUILD=1
                    else
                        echo "OpenSSL $INSTALLED_VERSION is older than $OPENSSL_VERSION, will rebuild."
                    fi
                else
                    echo "Could not determine installed OpenSSL version, will rebuild."
                fi
            else
                echo "No opensslv.h found, will rebuild."
            fi
        fi

        if [ "$SKIP_BUILD" -eq 1 ]; then
            echo
            continue
        fi

        cd "$BUILD_DIR"
        if [ ! -d "openssl-${OPENSSL_VERSION}" ]; then
            if [ ! -f "openssl-${OPENSSL_VERSION}.tar.gz" ]; then
                wget "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz"
            fi
            tar xf "openssl-${OPENSSL_VERSION}.tar.gz"
        fi
        cd "openssl-${OPENSSL_VERSION}"

        # Clean previous build for this arch
        make clean || true

        echo "Configuring for $ARCH with sysroot $SYSROOT..."
        CC="$CC" AR="$AR" RANLIB="$RANLIB" ./Configure "$OPENSSL_TARGET" no-shared --prefix="$SYSROOT/usr"
        echo "Building..."
        make -j"$(nproc)"
        echo "Installing to $SYSROOT/usr ..."
        make install_sw
        cd "$BUILD_DIR"
        echo "Done for $ARCH."
        echo
        # Remove build dir for next arch to avoid cross contamination
        rm -rf "openssl-${OPENSSL_VERSION}"
        tar xf "openssl-${OPENSSL_VERSION}.tar.gz"
    done

    echo "OpenSSL built and installed for all detected musl toolchains."
}



# ----------------------------------------------------------------------------------------------------------
# MUSL CROSS COMPILERS
# ----------------------------------------------------------------------------------------------------------


function check_path() {
    local BASHRC="${USER_HOME}/.bashrc"
    local TOOLCHAIN="$1"
    local MUSL_PATH="$INSTALL_DIR/$TOOLCHAIN/bin"
    if ! echo "$PATH" | grep -q "$MUSL_PATH"; then
        echo "Adding $MUSL_PATH to PATH in $BASHRC"
        PATH_LINE="export PATH=\"$MUSL_PATH:\$PATH\""
        if ! grep -Fxq "$PATH_LINE" "$BASHRC"; then
            echo "" >> "$BASHRC"
            echo "# Add musl cross compilers to PATH for dropshell" >> "$BASHRC"
            echo "$PATH_LINE" >> "$BASHRC"

            echo "Added musl cross compilers to $BASHRC"
            echo "You should run 'source ~/.bashrc' to update your PATH"
        else
            echo "You should run 'source ~/.bashrc' to update your PATH"
        fi
    fi
}

function install_musl_cross() {
    local TOOLCHAIN="$1"
    local MUSL_CC_URL="https://getbin.xyz"
    if [ ! -d "$INSTALL_DIR/$TOOLCHAIN" ]; then
        echo "Downloading $TOOLCHAIN musl cross toolchain..."
        wget -nc -O "$TMPDIR/$TOOLCHAIN.tgz" "$MUSL_CC_URL/$TOOLCHAIN.tgz:latest"
        tar -C "$INSTALL_DIR" -xvf "$TMPDIR/$TOOLCHAIN.tgz"
    fi
}

function installmusl() {
    echo "Installing musl toolchain"

    # Set install directory
    INSTALL_DIR="$USER_HOME/.musl-cross"
    mkdir -p "$INSTALL_DIR"

    TMPDIR=$(mktemp -d)
    trap 'rm -rf "$TMPDIR"' EXIT

    TOOLCHAIN_LIST=(
        "aarch64-linux-musl-cross"
        "x86_64-linux-musl-cross"
    )

    for TOOLCHAIN in "${TOOLCHAIN_LIST[@]}"; do
        install_musl_cross "$TOOLCHAIN"
        check_path "$TOOLCHAIN"
    done

    # Clean up
    rm -rf "$TMPDIR"
}


#----------------------------------------------------------------------------------------------------------
# Main
#----------------------------------------------------------------------------------------------------------

function main() {

    install_packages

    install_headers

    installmusl

    install_openssl_musl

}

main