#!/bin/bash

set -euo pipefail

ensure_root() {
    if [[ $(id -u) -ne 0 ]]; then
        echo "Error: This script must be run as root!" >&2
        exit 1
    fi
    export HOME=/root LANG=C
}

check_required_packages() {
    local REQUIRED_PACKAGES=(
        git unp kmod cpio unzip bzip2 make bc build-essential libncurses5-dev fakeroot bison flex libelf-dev openssl libssl-dev curl wget jq rsync lsb-release dwarves python3 whiptail equivs
    )
    local MISSING_PACKAGES=()
    for PACKAGE in "${REQUIRED_PACKAGES[@]}"; do
        if ! dpkg -l | grep -qw "$PACKAGE"; then
            MISSING_PACKAGES+=("$PACKAGE")
        fi
    done
    if [[ ${#MISSING_PACKAGES[@]} -ne 0 ]]; then
        echo "Installing missing packages: ${MISSING_PACKAGES[*]}"
        apt-get update -qq
        apt-get install -y "${MISSING_PACKAGES[@]}"
    fi
}

get_available_kernels() {
    apt-get update -qq
    apt-cache search linux-image | grep -Po 'linux-image-\K([0-9]+\.[0-9]+)' | grep -v -- '-dbg\|unsigned' | sort -uV
}

select_kernel_version() {
    local KERNELS=($(get_available_kernels))
    local OPTIONS=()
    for KERNEL in "${KERNELS[@]}"; do
        OPTIONS+=("$KERNEL" "")
    done
    whiptail --title "Select Kernel Version" --menu "Choose a major kernel version:" 20 78 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3
}

set_kernel_type_suffix() {
    case $1 in
    none) echo "" ;;
    rt) echo "-rt" ;;
    cloud) echo "-cloud" ;;
    *)
        echo "Invalid kernel type: $1" >&2
        exit 1
        ;;
    esac
}

get_latest_kernel_version() {
    local MAJOR_VERSION=$1
    local KERNEL_TYPE_SUFFIX_ESCAPED=$(echo "$KERNEL_TYPE_SUFFIX" | sed 's/-/\\-/g')
    local PACKAGES=$(apt-cache search linux-image | grep -E "linux-image-$MAJOR_VERSION" | grep "$KERNEL_ARCH" | grep -E "$KERNEL_TYPE_SUFFIX_ESCAPED" | grep -v -- '-dbg\|unsigned')
    local VERSIONS=($(echo "$PACKAGES" | grep -oP "${MAJOR_VERSION}\.\d+[-\+][a-zA-Z0-9\.]*"))
    IFS=$'\n' SORTED_VERSIONS=($(sort -Vr <<<"${VERSIONS[*]}"))
    echo "${SORTED_VERSIONS[0]}"
}

print_help() {
    echo "Usage: $0 [options]"
    echo
    echo "Options:"
    echo "  -h, --help              Show this help message and exit"
    echo "  -k, --kernel            Specify kernel major version (e.g., 5.10)"
    echo "  -a, --arch              Specify kernel architecture (e.g., amd64, 686, 686-pae)"
    echo "  -f, --flavour           Specify kernel flavour (e.g., none, rt, cloud)"
    echo "  -d, --distribution      Specify the distribution codename (e.g., buster, bullseye)"
    echo "  --skip-kernel-build     Skip the kernel build operations"
    echo "  --skip-metapackages     Skip the metapackage creation operations"
    echo
    echo "If any option is not provided, the script will prompt you to select it using whiptail."
}

create_log_file() {
    if [ -z "${LOG_FILE-}" ]; then

        if [ -z "${BUILD_DIR-}" ]; then
            BUILD_DIR="${PWD}"
        fi
        mkdir -p "${BUILD_DIR}"

        export LOG_FILE="${BUILD_DIR}/build-$(date +%Y%m%d-%H%M%S).log"

        ARGS=""
        for VAR in "$@"; do
            ARGS="${ARGS}\"${VAR}\" "
        done
        script -q -e -c "${0} ${ARGS}" "${LOG_FILE}"

        if [ -f "${LOG_FILE}" ]; then
            sed -i 's/\x1B\[[0-9;]*[JKmsu]//g' "${LOG_FILE}"
        fi

        exit $?
    fi
}

ensure_root
create_log_file "$@"

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT

KERNEL_MAJOR_VERSION=""
KERNEL_ARCH=""
KERNEL_FLAVOUR=""
DISTRIBUTION=""
SKIP_KERNEL_BUILD=false
SKIP_METAPACKAGES=false

OPTS=$(getopt -o hk:a:f:d: --long help,kernel:,arch:,flavour:,distribution:,skip-kernel-build,skip-metapackages -n "$0" -- "$@")
if [[ $? != 0 ]]; then
    print_help
    exit 1
fi

eval set -- "$OPTS"

while true; do
    case "$1" in
    -h | --help)
        print_help
        exit 0
        ;;
    -k | --kernel)
        KERNEL_MAJOR_VERSION="$2"
        shift 2
        ;;
    -a | --arch)
        KERNEL_ARCH="$2"
        shift 2
        ;;
    -f | --flavour)
        KERNEL_FLAVOUR="$2"
        shift 2
        ;;
    -d | --distribution)
        DISTRIBUTION="$2"
        shift 2
        ;;
    --skip-kernel-build)
        SKIP_KERNEL_BUILD=true
        shift
        ;;
    --skip-metapackages)
        SKIP_METAPACKAGES=true
        shift
        ;;
    --)
        shift
        break
        ;;
    *)
        echo "Invalid option: $1" >&2
        print_help
        exit 1
        ;;
    esac
done

check_required_packages

if [[ -z "$KERNEL_MAJOR_VERSION" ]]; then
    KERNEL_MAJOR_VERSION=$(select_kernel_version)
fi

if [[ -z "$KERNEL_ARCH" ]]; then
    KERNEL_ARCH=$(whiptail --title "Select Kernel Architecture" --menu "Choose a kernel architecture:" 20 78 10 \
        "amd64" "" \
        "686" "" \
        "686-pae" "" 3>&1 1>&2 2>&3 || echo "amd64")
fi

case $KERNEL_ARCH in
686 | 686-pae)
    ARCH="i386"
    if uname -a | grep -q x86_64; then
        dpkg --add-architecture i386
        apt-get update -qq
    fi
    ;;
amd64)
    ARCH="amd64"
    ;;
*)
    echo "Invalid arch: $KERNEL_ARCH" >&2
    exit 1
    ;;
esac

if [[ -z "$KERNEL_FLAVOUR" ]]; then
    KERNEL_FLAVOUR=$(whiptail --title "Select Kernel Flavour" --menu "Choose a kernel flavour:" 20 78 10 \
        "none" "" \
        "rt" "" \
        "cloud" "" 3>&1 1>&2 2>&3 || echo "none")
fi

KERNEL_TYPE_SUFFIX=$(set_kernel_type_suffix "$KERNEL_FLAVOUR")

if [[ -z "$DISTRIBUTION" ]]; then
    DISTRIBUTION=$(source /etc/os-release && echo $VERSION_CODENAME)
fi

if [ "$SKIP_KERNEL_BUILD" = false ]; then
    AUFS_REPO=${AUFS_REPO:-"https://github.com/sfjro/aufs-standalone"}
    AUFS="aufs-standalone"
    CHECKOUT="origin/aufs$KERNEL_MAJOR_VERSION"
    CPU=$(nproc)

    apt-get download "linux-config-$KERNEL_MAJOR_VERSION:$ARCH"

    mkdir -p "$TEMP_DIR/linux-config-$KERNEL_MAJOR_VERSION"
    dpkg-deb -xv "$(ls linux-config-$KERNEL_MAJOR_VERSION*$ARCH.deb | sort -Vr | head -n 1)" "$TEMP_DIR/linux-config-$KERNEL_MAJOR_VERSION"

    CONFIG_PATH="$TEMP_DIR/linux-config-$KERNEL_MAJOR_VERSION/usr/src/linux-config-$KERNEL_MAJOR_VERSION"

    CONFIG_FILE_NAME="config.${ARCH}_${KERNEL_FLAVOUR}_${KERNEL_ARCH}.xz"

    CONFIG="${CONFIG_PATH}/config.${ARCH}_${KERNEL_FLAVOUR}_${KERNEL_ARCH}"

    if [[ ! -f $CONFIG ]]; then
        unxz -k "$CONFIG_PATH/$CONFIG_FILE_NAME"
    fi

    if [[ ! -f /etc/apt/sources.list.bak ]]; then
        cp /etc/apt/sources.list /etc/apt/sources.list.bak
    fi

    if [ "$DISTRIBUTION" = "sid" ]; then
        cat <<EOF | tee /etc/apt/sources.list
deb http://deb.debian.org/debian/ $DISTRIBUTION main
deb-src http://deb.debian.org/debian/ $DISTRIBUTION main
EOF
    else
        cat <<EOF | tee /etc/apt/sources.list
deb http://deb.debian.org/debian/ $DISTRIBUTION main
deb-src http://deb.debian.org/debian/ $DISTRIBUTION main
deb http://security.debian.org/debian-security $DISTRIBUTION-security main
deb-src http://security.debian.org/debian-security $DISTRIBUTION-security main
deb http://deb.debian.org/debian/ $DISTRIBUTION-updates main
deb-src http://deb.debian.org/debian/ $DISTRIBUTION-updates main
deb http://deb.debian.org/debian $DISTRIBUTION-backports main
deb-src http://deb.debian.org/debian $DISTRIBUTION-backports main
EOF
    fi

    apt-get update -qq

    KERNEL_VERSION=$(apt show "linux-source-$KERNEL_MAJOR_VERSION" | grep 'Version:' | awk '{print $2}')

    KERNEL="linux-${KERNEL_VERSION%%[-+]*}"

    [[ -d "$KERNEL" ]] && rm -rf "$KERNEL"

    apt-get build-dep linux="$KERNEL_VERSION"

    apt-get source linux="$KERNEL_VERSION"

    if [[ -d "$KERNEL" ]]; then
        cd "$KERNEL"
    else
        echo "Error: Kernel source directory not found." >&2
        exit 1
    fi

    rm -rf "$AUFS"
    git -c http.sslVerify=false clone "$AUFS_REPO"
    cd "$AUFS"
    git checkout "$CHECKOUT"
    cd ..

    cp -aR "$AUFS/fs" .
    cp -a "$AUFS/include/uapi/linux/aufs_type.h" include/uapi/linux

    if [ "$KERNEL_VERSION" = "6.1.124-1" ]; then
        cp "$SCRIPT_DIR/aufs6.1.124-mmap.patch" "$AUFS/aufs6-mmap.patch" 
    fi

    PATCHES=("aufs${KERNEL_VERSION:0:1}-kbuild" "aufs${KERNEL_VERSION:0:1}-base" "aufs${KERNEL_VERSION:0:1}-mmap" "aufs${KERNEL_VERSION:0:1}-standalone" "aufs${KERNEL_VERSION:0:1}-loopback" "vfs-ino" "tmpfs-idr")
    for PATCH in "${PATCHES[@]}"; do
        patch -p1 <"$AUFS/$PATCH.patch"
    done

    sed -r "s/CONFIG_SYSTEM_TRUSTED_KEYS/#CONFIG_SYSTEM_TRUSTED_KEYS/" "$CONFIG" >.config

    cat <<EOF >>.config
CONFIG_AUFS_FS=m
CONFIG_AUFS_BRANCH_MAX_32767=y
CONFIG_AUFS_SBILIST=y
CONFIG_AUFS_EXPORT=y
CONFIG_AUFS_INO_T_64=y
CONFIG_AUFS_XATTR=y
CONFIG_AUFS_DIRREN=y
CONFIG_AUFS_SHWH=y
CONFIG_AUFS_BR_RAMFS=y
CONFIG_AUFS_BR_FUSE=y
CONFIG_AUFS_POLL=y
CONFIG_AUFS_BR_HFSPLUS=y
CONFIG_AUFS_BDEV_LOOP=y
CONFIG_NTFS3_FS=m
CONFIG_NTFS3_LZX_XPRESS=y
EOF

    yes "" | make oldconfig || true

    sed -ri '/CONFIG_SYSTEM_TRUSTED_KEYS/s/=.+/=""/g' .config

    export DEBFULLNAME="MiniOS Kernel Team"
    export DEBEMAIL="team@minios.dev"
    make -j"$CPU" bindeb-pkg DEBUG_INFO=n EXTRAVERSION="-mos$KERNEL_TYPE_SUFFIX" LOCALVERSION="-$KERNEL_ARCH"
else
    apt-get update -qq
    KERNEL_VERSION=$(apt show "linux-source-$KERNEL_MAJOR_VERSION" | grep 'Version:' | awk '{print $2}')
fi

if [ "$SKIP_METAPACKAGES" = false ]; then
    equivs-build-metapackage() {
        local NAME=$1
        local VERSION=$2
        local ARCHITECTURE=$3
        local MAINTAINER=$4
        local DEPENDS=$5
        local PROVIDES=$6
        local DESCRIPTION=$7

        if [ "$ARCHITECTURE" = "686" ] || [ "$ARCHITECTURE" = "686-pae" ]; then
            ARCHITECTURE="i386"
        fi

        cat <<EOF >"$TEMP_DIR/$NAME"
Package: $NAME
Version: $VERSION
Architecture: $ARCHITECTURE
Maintainer: $MAINTAINER
Depends: $DEPENDS
Provides: $PROVIDES
Description: $DESCRIPTION
EOF

        equivs-build --full --arch $ARCHITECTURE "$TEMP_DIR/$NAME"
    }

    cd $SCRIPT_DIR

    equivs-build-metapackage \
        "linux-image-$KERNEL_MAJOR_VERSION-mos-$KERNEL_ARCH" "${KERNEL_VERSION%%[-+]*}" "$KERNEL_ARCH" \
        "MiniOS Kernel Team <team@minios.dev>" "linux-image-${KERNEL_VERSION%%[-+]*}-mos-$KERNEL_ARCH" \
        "linux-latest-modules-${KERNEL_VERSION%%[-+]*}-mos-$KERNEL_ARCH, linux-image-$KERNEL_MAJOR_VERSION-mos-generic" \
        "Linux for $KERNEL_ARCH PCs (meta-package)"

    equivs-build-metapackage \
        "linux-headers-$KERNEL_MAJOR_VERSION-mos-$KERNEL_ARCH" "${KERNEL_VERSION%%[-+]*}" "$KERNEL_ARCH" \
        "MiniOS Kernel Team <team@minios.dev>" "linux-headers-${KERNEL_VERSION%%[-+]*}-mos-$KERNEL_ARCH" \
        "linux-headers-$KERNEL_MAJOR_VERSION-mos-generic" \
        "Header files for Linux $KERNEL_ARCH configuration (meta-package)"
fi
