#!/usr/bin/python3
"""Upload a gist containing packages installed on the system.

Dependencies
============
* pacman
* gist
"""

import argparse
import configparser
import re
import subprocess
import sys

# ========== Constants ==========
# Paths
CONFIG_FILE = "/etc/packaging-scripts.conf"
DEFAULT_FILENAME = "pacman-packages.txt"

# Config file options
CONFIG_SECTION = "pug2"
CONFIG_OPTION_DESCRIPTION = "GIST_DESCRIPTION"
CONFIG_OPTION_ENABLE = "RUN_ON_PACMAN_HOOK"
CONFIG_OPTION_FILENAME = "GIST_FILENAME"
CONFIG_OPTION_ID = "GIST_ID"

DESCRIPTION = "Send a list of explicitly installed pacman packages to a gist"


# ========== Functions ==========
def extract_gist_id(url):
    """Extract the gist id from a gist URL.

    Normalizes URLs from
    * http(s)?://<subdomain>.<second level domain>.<top level domain>/<username>/<gist ID>
    * <username>/<gist ID>
    * <gist ID>

    to one of

    * <username>/<gist ID>
    * <gist ID>

    both of which are valid for use with gist

    :param url: a valid URL containing the gist id
    :type url: str
    :returns: the valid gist ID
    :rtype: str
    """
    return re.sub("^http(s)?://[\\w]*.[\\w]*.[\\w]/", "", url)


def retrieve_gist_info(gist_id):
    """Retrieve info from gist ID that is specified in the config file.

    :param gist_id: string id to read gist info
    :returns: data read from gist
    :rtype: bytes
    """
    return subprocess.run(
        ["gist", "--read", gist_id], capture_output=True, text=True
    ).stdout


def package_lists_match(gist_id, package_list):
    """Compare local package list to that of pacman and gist, return if they match.
    :param gist_id: ID of the gist to read from
    :param package_list: Newline separated list of packages installed on the system
    :type gist_id: str
    :type package_list: str
    """
    return retrieve_gist_info(gist_id) == package_list


# ========== Main Script ==========
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=DESCRIPTION)
    parser.add_argument(
        "-c",
        "--check-if-enabled",
        dest="check_enabled",
        help="check if enabled in config file",
        action="store_true",
    )
    parser.add_argument(
        "-f",
        "--force-update",
        help="force update regardless if package lists match",
        action="store_true",
    )
    args = parser.parse_args()
    config = configparser.ConfigParser()
    config.read(CONFIG_FILE)

    # Check if script is enabled to run; if not, exit silently
    if args.check_enabled and not config.getboolean(
        CONFIG_SECTION, CONFIG_OPTION_ENABLE
    ):
        sys.exit(0)

    gist_description = config.get(CONFIG_SECTION, CONFIG_OPTION_DESCRIPTION)
    gist_filename = config.get(CONFIG_SECTION, CONFIG_OPTION_FILENAME)
    gist_id = config.get(CONFIG_SECTION, CONFIG_OPTION_ID)
    gist_opts = ["--filename", gist_filename, "--description", gist_description]

    packages = subprocess.run(
        ["pacman", "-Qqen"], capture_output=True, text=True
    ).stdout

    if gist_id == "":
        try:
            print("No gist ID detected, creating new.")
            gist_process = subprocess.run(
                ["gist", *gist_opts],
                input=packages,
                capture_output=True,
                text=True,
                check=True,
            )
        except subprocess.CalledProcessError as e:
            print(e.stdout.strip(), file=sys.stderr)
            sys.exit(e.returncode)
        else:
            config[CONFIG_SECTION][CONFIG_OPTION_ID] = extract_gist_id(
                gist_process.stdout
            )
            print("Gist creation complete.")

    elif package_lists_match(gist_id, packages) and not args.force_update:
        print("Package lists match, no update necessary.")
        sys.exit(0)
    else:
        try:
            print("Updating package list.")
            subprocess.run(
                ["gist", "--update", gist_id, *gist_opts],
                input=packages,
                capture_output=True,
                text=True,
                check=True,
            )
        except subprocess.CalledProcessError as e:
            print(e.stdout.strip(), file=sys.stderr)
            sys.exit(e.returncode)
        else:
            print("Update complete.")