#!/bin/bash

KEEPPACKAGES=$1

echo
echo "________________________________________"
echo ' Start cleanup'
echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"

function obsolete_packages() {
    # Get not used kernel packages
    LATEST_KERNEL_VERSION=$(dpkg -l | awk '/^ii  linux-image-[0-9]/ {print $2}' | cut -d'-' -f 3 | sort -V | tail -n 1)
    KERNELS=($(dpkg -l | awk '/^ii  linux-(image|headers|kbuild)-[0-9]/ {print $2}' | grep -v "$LATEST_KERNEL_VERSION"))

    # Get autoremove candidates
    AUTORM=($(LC_ALL=C apt-get -s autoremove | grep -E -i "^Remv " | cut -d' ' -f2))

    # Get obsolete packages
    OBSOLETE=($(aptitude search --display-format='%p' '~o !~ahold' 2>/dev/null))

    # Combine and deduplicate
    COMBINED=($(printf "%s\n" "${KERNELS[@]}" "${AUTORM[@]}" "${OBSOLETE[@]}" | sort -u))

    # Output
    printf "%s\n" "${COMBINED[@]}"
}

# Make this script unattended
export DEBIAN_FRONTEND=noninteractive
APT="apt-get -y --autoremove -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold"

apt-get clean
eval $APT --purge autoremove

# Remove old kernel and headers
VERSION=$(readlink -s /vmlinuz | grep -o -P '(?<=vmlinuz-).*(?=-amd64)')
if [ -n "$VERSION" ]; then
    OLDKERNEL=$(dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' 'linux-image-[0-9]*' 'linux-headers-[0-9]*' | grep ^i | grep -v "$VERSION" | egrep -v "[a-z]-486|[a-z]-686|[a-z]-586" | awk '{print $2}')
    if [ -n "$OLDKERNEL" ]; then
        echo "________________________________________"
        echo " Remove old kernel packages: $OLDKERNEL"
        echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"
        eval $APT purge $OLDKERNEL
        KBCNT=$(dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' 'linux-kbuild*' | grep ^i | wc -l)
        if [ $KBCNT -gt 1 ]; then
            eval $APT purge $(dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' 'linux-kbuild*' | grep ^i | egrep -v ${VERSION%-*} | awk '{print $2}')
        fi
    fi
fi

echo "________________________________________"
echo ' Remove unavailable packages only when not manually held back'
echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"
for PCK in $(env LANG=C apt list --installed 2>/dev/null | grep installed,local | cut -d'/' -f 1); do
    # Check if the locally installed packages are not available in a repository
    if [ -z "$(apt-cache show $PCK | grep -i filename)" ]; then
        REMOVE=true
        for HELDPCK in $(env LANG=C dpkg --get-selections | grep hold$ | awk '{print $1}'); do
            if [ "$PCK" == "$HELDPCK" ]; then
                REMOVE=false
            fi
        done
        if $REMOVE; then
            if [[ "$KEEPPACKAGES" =~ "$PCK" ]] || [ "$KEEPPACKAGES" == '*' ]; then
                echo "Not available but keep installed: $PCK"
            else
                eval $APT purge $PCK
            fi
        fi
    fi
done

if [ "$KEEPPACKAGES" != '*' ] && [ -n "$(which aptitude)" ]; then
    echo "________________________________________"
    echo ' Removing obsolete packages, except the ones listed in KEEPPACKAGES'
    echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"
    Exclude=${KEEPPACKAGES//,/\/d;/}
    
    Obsolete=$(obsolete_packages | sed '/'$Exclude'/d')
    while [ "$Obsolete" ]; do
        eval $APT purge $Obsolete
        RC=$(dpkg-query -l | sed -n 's/^rc\s*\(\S*\).*/\1/p')
        [ "$RC" ] && eval $APT purge $RC
        Obsolete=$(obsolete_packages | sed '/'$Exclude'/d')
    done
fi

echo "________________________________________"
echo ' Cleanup files'
echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"

if [ -f /initrd.img ] && [ -f /initrd.img.old ]; then
    rm /initrd.img.old
fi
if [ -f /vmlinuz ] && [ -f /vmlinuz.old ]; then
    rm /vmlinuz.old
fi

if [ -e /usr/sbin/policy-rc.d ]; then
    rm -r /usr/sbin/policy-rc.d
fi

# Cleanup root history
rm /root/.nano_history 2>/dev/null
rm /root/.bash_history 2>/dev/null
rm /root/.wget-hsts 2>/dev/null
rm /root/.aptitude 2>/dev/null
rm /root/.nano 2>/dev/null
rm /root/.cache 2>/dev/null
rm -rf /media/*
rm -rf /tmp/*
rm -rf /tmp/.??*
rm -rf /var/backups/*
rm -rf /var/cache/fontconfig/*
rm -rf /var/cache/samba/*
rm -rf /var/mail/*
rm -rf /var/spool/exim4/input/*
rm -rf /var/spool/exim4/msglog/*
rm -rf /var/tmp/*
rm -rf /var/tmp/.??*
rm -rf /boot/grub/grub.cfg

if [ -e "/etc/resolv.conf" ] && [ ! -L "/etc/resolv.conf" ]; then
    rm -f /etc/resolv.conf
fi

# -xdev: Restricts find to the same filesystem as the starting point (/).
#        This automatically skips /proc, /sys, /dev, etc., which are mounted as separate filesystems.
# +: Groups all matches into a single rm -r call (more efficient than \;).
find / -xdev -not -path "/proc/*" -not -path "/sys/*" -not -path "/dev/*" -type d -name "__pycache__" -exec rm -r {} +
find / -xdev -not -path "/proc/*" -not -path "/sys/*" -not -path "/dev/*" \( -name "*.bak*" -o -name "*.dpkg*" -o -name "*.old" -o -name "*.tmp" -o -name "*.ucf-dist" \) -delete
find /var/log -type f -delete
find -L /etc/alternatives -type l -exec rm -v {} +
find / -maxdepth 1 -name "*.deb" -delete

# Removing redundant kernel module structure(s) from /lib/modules (if any)
VersionPlusArch=$(ls -l /vmlinuz | sed 's/.*\/vmlinuz-\(.*\)/\1/')
L=${#VersionPlusArch}
for I in /lib/modules/*; do
    if [ ${I: -$L} != $VersionPlusArch ] && [ ! -d $I/kernel ]; then
        rm -fr $I
    fi
done

echo "________________________________________"
echo " Cleanup finished"
echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"
exit 0
