#!python
'''
    %(prog)s - A Software PAckage Manager interface utility for Linux.
    (C) 2024 Mike Miller, License: GPLv3+

    %(prog)s \x1b[3moptions\x1b[m sub-command \x1b[3mmoar-args\x1b[m

    # additional/customized sub-commands for
'''
import os
import signal
import sys
from os.path import exists
from argparse import ArgumentParser
from platform import freedesktop_os_release
from subprocess import call


__version__ = '0.53'
USE_SUDO = {
    'add-apt-repository',
    'autoremove',
    'check-update',
    'clean',
    'config-manager',
    'dist-upgrade',
    'install',
    'reinstall',
    'remove',
    'selfupdate',
    'system-upgrade',
    'update',
    'upgrade',
}
PLATFORM_MAP = dict(  # os-release.ID: (pkg-mgr, aliases)
    fedora=(
        'dnf',
        dict(
            add='config-manager --add-repo',
            clean='clean packages; autoremove',
            list_installed='list --installed',
            install='install',
            infile='install',  # doesn't need separate command
            contents='repoquery -l',
            provides='provides --disablerepo=\\*',
            purge='remove',
            show='info --cacheonly',
            update='check-update --refresh',
        ),
    ),
    debian=(
        'apt',
        dict(
            add='!add-apt-repository',
            clean='clean; autoremove',
            list_installed='list --installed',
            info='show',
            infile='!sudo dpkg --install',
            contents='!dpkg-query --listfiles',
            provides='!dpkg --search',
            upgrade='update; dist-upgrade',
        ),
    ),
    mac_ports=(
        'port',
        dict(
            clean='clean --all all; reclaim',
            info='info; variants',
            remove='uninstall --follow-dependents',
            search='search --line --name',
            update='sync',
            upgrade='-v selfupdate; upgrade outdated',
        ),
    ),
    openwrt=(
        'opkg',
        dict(
            list_installed='list-installed',
            info='show; status',
            contents='files',
            provides='search',
            purge='remove',
            search='find',
            upgrade='update;!auc -c',
        ),
    ),
    # add more here, should be tested first
)
PLATFORM_MAP['ubuntu'] = PLATFORM_MAP['debian']
PLATFORM_MAP['linuxmint'] = PLATFORM_MAP['debian']
PLATFORM_MAP['redhat'] = PLATFORM_MAP['fedora']

SCMD_ALIASES = dict(
    inf='infile',
    lsf='contents',  # old list package files alias
    ls='list',
    lsi='list_installed',
    po='policy',
    pr='provides',
    pu='purge',
    rm='remove',
    se='search',
    up='upgrade',
)
SCMD_ALIASES['in'] = 'install'


def main(argv):
    # find system - need to do first so we can print aliases if need be
    if sys.platform == 'darwin':
        platform_id = 'mac_ports' if exists('/opt/local/bin/port') else None
    else:
        release = freedesktop_os_release()
        platform_id = release.get('ID')

    pkgmgr, platform_aliases = PLATFORM_MAP.get(platform_id, (None, None))
    if not pkgmgr:
        print('Error:', f'package manager not found for {platform_id!r}.')
        return os.EX_UNAVAILABLE

    _usage = (
        f'{__doc__.rstrip()} \x1b[1m{pkgmgr}\x1b[m:\n    '
        f'{", ".join(platform_aliases.keys())}\n '
    )  # important, ends in nbsp to prevent another strip by argparse

    # check & prepare args
    parser = ArgumentParser(usage=_usage)
    parser.add_argument('-d', '--debug', action='store_true', help='print...')
    parser.add_argument('-f', '--skip-flatpak', action='store_true',
        help='on update, skip flatpak update.')
    parser.add_argument('-p', '--package-man',
        choices=tuple(sorted(PLATFORM_MAP.keys())),
        help='choose a platform/pkg manager immediately, don\'t do a check.')
    parser.add_argument(
        '--version', action='version', version='%(prog)s ' + __version__
    )
    parser.add_argument(
        'sub_command', metavar='sub-command', help='package manager command.'
    )
    try:
        args, moar_args = parser.parse_known_args(argv[1:])
    except SystemExit:
        exit(os.EX_USAGE)
    if args.debug:
        print('debug:', args)
        print(' moar:', moar_args)

    codes = []
    # unalias, if necessary
    SCMD_ALIASES.update(platform_aliases)
    if args.debug:
        print('aliases:', SCMD_ALIASES)
        print('subcmd0:', args.sub_command)
    sub_command = SCMD_ALIASES.get(args.sub_command, args.sub_command)

    if args.debug:
        print('subcmd1:', repr(sub_command))
    if sub_command in platform_aliases:  # expand again
        sub_command = platform_aliases[sub_command]
    if args.debug:
        print('subcmd2:', repr(sub_command))

    for sub_command in sub_command.split(';'):  # may be multiple

        if args.debug:
            print('subcmd3:', repr(sub_command))
        cmdlist = [pkgmgr]
        if sub_command.startswith('!'):  # runs stand-alone, rm prefix
            cmdlist = []
            sub_command = sub_command[1:]

        if args.debug:
            print('subcmd4:', repr(sub_command))
        sub_command = sub_command.split()  # may have spaces
        sub_command_without_switches = [
            s for s in sub_command if not s.startswith('-')
        ]
        if not platform_id == 'openwrt':
            if sub_command_without_switches[0] in USE_SUDO:
                cmdlist.insert(0, 'sudo')
        cmdlist.extend(sub_command)
        cmdlist.extend(moar_args)

        # cmdline should be a string when shell=True
        cmdline = ' '.join(cmdlist)
        if args.debug:
            print('cmdlist:', cmdlist)
        print('\x1b[2m⏵', cmdline, '\x1b[m', file=sys.stderr)
        if error_code := call(cmdline, shell=True):
            return error_code

        if (sub_command[0] == 'update'
            and sys.platform == 'linux'
            and exists('/usr/bin/flatpak')
        ):
            if error_code := call('flatpak update', shell=True):
                return error_code

        codes.append(error_code)
        print()

    return codes[-1]  # last success


# http://youtu.be/0hiUuL5uTKc?t=8s
try:
    sys.exit(main(sys.argv))
except KeyboardInterrupt:
    print('\nWarning: Ctrl+C entered, exiting.', file=sys.stderr)
    sys.exit(128 + signal.SIGINT)
