"""Utilities for searching and importing GPG keys locally."""

from subprocess import run, STDOUT, PIPE
from re import compile


_DEBIAN_KEY_SERVER_HOSTNAME = "keyring.debian.org"
_DEBIAN_CD_SIGNING_KEY_ID = "DA87E80D6294BE9B"
_DEBIAN_CD_TESTING_SIGNING_KEY_ID = "42468F4009EA8AC3"


def import_debian_signing_key():
    """Imports the public debian CD signing key using gpg.

    The key is imported from keyring.debian.org into the invoking user's
    GPG public key store using a shell command.
    """

    # execute a gpg key import as a shell command, redirecting stderr to stdout
    process_result = run(
        [
            "gpg", "--keyserver", _DEBIAN_KEY_SERVER_HOSTNAME,
            "--recv-key", _DEBIAN_CD_SIGNING_KEY_ID
        ],
        stdout=PIPE,
        stderr=STDOUT,
        text=True,
    )

    # check shell return code
    if process_result.returncode != 0:
        if process_result.stdout:
            raise RuntimeError(
                f"Failed to import key using gpg:\n{process_result.stdout}"
            )
        else:
            raise RuntimeError("Failed to import key using gpg.")

    # check shell output:
    # verify that the key was imported successfully by checking for key patterns
    stdout_lower = process_result.stdout.lower()
    key_id_in_output = _DEBIAN_CD_SIGNING_KEY_ID.lower() in stdout_lower
    imported_or_unchanged = any(word in stdout_lower for word in ["imported", "unchanged", "importée", "importées"])
    
    if not (key_id_in_output and imported_or_unchanged):
        raise RuntimeError(
            f"Unexpected output while importing PGP public key:\n"
            f"{process_result.stdout}"
        )


def import_debian_testing_signing_key():
    """Imports the public debian testing CD signing key using gpg.

    The key is imported from keyring.debian.org into the invoking user's
    GPG public key store using a shell command.
    """

    # execute a gpg key import as a shell command, redirecting stderr to stdout
    process_result = run(
        [
            "gpg", "--keyserver", _DEBIAN_KEY_SERVER_HOSTNAME,
            "--recv-key", _DEBIAN_CD_TESTING_SIGNING_KEY_ID
        ],
        stdout=PIPE,
        stderr=STDOUT,
        text=True,
    )

    # check shell return code
    if process_result.returncode != 0:
        if process_result.stdout:
            raise RuntimeError(
                f"Failed to import key using gpg:\n{process_result.stdout}"
            )
        else:
            raise RuntimeError("Failed to import key using gpg.")

    # check shell output:
    # verify that the key was imported successfully by checking for key patterns
    stdout_lower = process_result.stdout.lower()
    key_id_in_output = _DEBIAN_CD_TESTING_SIGNING_KEY_ID.lower() in stdout_lower
    imported_or_unchanged = any(word in stdout_lower for word in ["imported", "unchanged", "importée", "importées"])
    
    if not (key_id_in_output and imported_or_unchanged):
        raise RuntimeError(
            f"Unexpected output while importing PGP public key:\n"
            f"{process_result.stdout}"
        )


def debian_signing_key_is_imported():
    """Checks whether the debian PGP signing key exists in the local key store.

    The invoking user's GPG key store is checked using a shell command.

    Returns
    -------
    True : bool
        If the public PGP debian cd signing key exists in the invoking user's
        GPG key store.
    False : bool
        If the public PGP debian cd signing key does not exist in the invoking
        user's GPG key store.
    """

    # execute a local gpg key lookup as a shell command, redirecting stderr to
    # stdout
    # NOTE: this command returns 0 even if the key is not present
    process_result = run(
        ["gpg", "--locate-keys", _DEBIAN_CD_SIGNING_KEY_ID],
        stdout=PIPE,
        stderr=STDOUT,
        text=True,
    )

    # check shell return code
    if process_result.returncode != 0:
        raise RuntimeError("Failed to search local keys using gpg.")

    # no shell output means that the key does not exist locally
    if not process_result.stdout:
        return False

    # verify existing key shell output using regex:
    # it should contain six lines in the following format
    expected_output_lines_regexes = [
        compile(r"^pub .*$"),
        compile(r"^ *[0-9A-F]{40}$"),
        compile(r"^uid .*$"),
        compile(r"^sub .*$"),
        compile(r"^$"),
        compile(r"^$"),
    ]

    actual_output_lines = process_result.stdout.split("\n")
    if not len(actual_output_lines) == len(expected_output_lines_regexes):
        raise RuntimeError(
            f"Unexpected line count in shell output while performing local"
            f"GPG key lookup:\n"
            f"{process_result.stdout}"
        )
    for i in range(4):
        if not expected_output_lines_regexes[i].match(actual_output_lines[i]):
            raise RuntimeError(
                f"Unexpected shell output format while performing local"
                f"GPG key lookup:\n"
                f"{process_result.stdout}"
            )

    return True


def debian_testing_signing_key_is_imported():
    """Checks whether the debian testing PGP signing key exists in the local key store.

    The invoking user's GPG key store is checked using a shell command.

    Returns True if the key exists, False otherwise.
    """

    # execute a gpg key lookup as a shell command, redirecting stderr to stdout
    process_result = run(
        ["gpg", "--locate-keys", _DEBIAN_CD_TESTING_SIGNING_KEY_ID],
        stdout=PIPE,
        stderr=STDOUT,
        text=True,
    )

    # check shell return code
    if process_result.returncode != 0:
        raise RuntimeError("Failed to search local keys using gpg.")

    # no shell output means that the key does not exist locally
    if not process_result.stdout:
        return False

    # verify existing key shell output using regex:
    # it should contain six lines in the following format
    expected_output_lines_regexes = [
        compile(r"^pub .*$"),
        compile(r"^ *[0-9A-F]{40}$"),
        compile(r"^uid .*$"),
        compile(r"^sub .*$"),
        compile(r"^$"),
        compile(r"^$"),
    ]

    actual_output_lines = process_result.stdout.split("\n")
    if len(actual_output_lines) < 4:
        raise RuntimeError(
            f"Unexpected shell output format while performing local "
            f"GPG key lookup:\n"
            f"{process_result.stdout}"
        )
    for i in range(4):
        if not expected_output_lines_regexes[i].match(actual_output_lines[i]):
            raise RuntimeError(
                f"Unexpected shell output format while performing local"
                f"GPG key lookup:\n"
                f"{process_result.stdout}"
            )

    return True
