#!/bin/bash

set -euo pipefail

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"
    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
            # cmake make g++ build-essential upx musl-tools wget tar ccache ninja-build mold
            PACKAGES="build-essential cmake git wget tar curl ninja-build mold nodejs npm perl jq ccache"
            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 jq ccache"
            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.
    echo "Checking for libassert headers"
    if [ ! -f "/usr/local/lib/libassert.a" ]; then
        echo "libassert not found, installing..."
        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
    else
        echo "libassert headers already installed"
    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"

        DEST_PATH="$SYSROOT/usr/lib/libssl.a"
        if [ -f "$DEST_PATH" ]; then
            echo "OpenSSL $ARCH is already installed"
            continue
        fi

        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
        else
            echo "No libssl.a found, will rebuild."
        fi

        if [ "$SKIP_BUILD" -eq 1 ]; then
            echo "Skipping build for $ARCH."
            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}"

        echo "----------------------------------------------"    
        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"
    else
        echo "$TOOLCHAIN musl cross toolchain already installed"
    fi
}

function install_musl() {
    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"
}

function output_version() {
    local VERSIONDATE
    local CONFIG_DIR="$USER_HOME/.config/dropshell-build/" 
    mkdir -p "$CONFIG_DIR"
    VERSIONDATE="$(date +%Y.%m%d.%H%M)"
    echo "$VERSIONDATE" > "$CONFIG_DIR/version" || echo "Error: Failed to write version file to $CONFIG_DIR/version"

}


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

function main() {

    install_packages

    install_headers

    install_musl

    install_openssl_musl

    output_version

    echo "Done"
}

main "$@"
