# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
# SPDX-License-Identifier: GPL-2.0-or-later
"""Run a stateless OpenPGP command-line tool."""

from __future__ import annotations

import subprocess  # noqa: S404
import typing

from testsigs.parse import gnupg as p_gnupg

from . import base


if typing.TYPE_CHECKING:
    import pathlib
    from typing import Final

    from testsigs import defs


def _gnupg_list_keys(cfg: defs.Config) -> list[defs.PublicKey]:
    """Run `gpg --list-keys` and partially parse the output."""
    list_output: Final = subprocess.check_output(  # noqa: S603
        [cfg.prog.gnupg, "--batch", "--with-colons", "--list-keys"],
        cwd=cfg.path.work,
        encoding="UTF-8",
        env=cfg.env,
    )
    print(list_output)  # noqa: T201
    return p_gnupg.parse_list_keys(list_output)


class GnuPGCli(base.PGPCli):
    """Invoke GnuPG as far as we can in batch mode."""

    def generate_keys(self, uid: str) -> defs.KeyFiles:
        """Generate an OpenPGP key to sign with."""
        # GnuPG makes it... difficult to generate keys in an unattended manner.
        raise NotImplementedError

    def import_secret_key(self, keys: defs.KeyFiles, uid: str) -> defs.PublicKey:
        """Import a secret key, return the parts of the public key we care about."""
        no_keys: Final = _gnupg_list_keys(self.cfg)
        if no_keys:
            raise RuntimeError(repr((self.cfg.prog.gnupg, no_keys)))

        subprocess.check_call(  # noqa: S603
            [self.cfg.prog.gnupg, "--import", keys.secret_bin],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
        )

        single_key: Final = _gnupg_list_keys(self.cfg)
        if len(single_key) != 1 or single_key[0].uid != uid:
            raise RuntimeError(repr((self.cfg.prog.gnupg, single_key)))
        return single_key[0]

    def sign_detached(self, _keys: defs.KeyFiles, datafile: pathlib.Path) -> pathlib.Path:
        """Create an ASCII-armored detached signature, return the path to the new file."""
        signature: Final = datafile.with_name(datafile.name + ".asc")
        subprocess.check_call(  # noqa: S603
            [self.cfg.prog.gnupg, "-ab", datafile.name],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
        )
        if not signature.is_file():
            raise RuntimeError(repr((datafile, signature)))
        return signature

    def verify_detached(
        self,
        _keys: defs.KeyFiles,
        _pub_key: defs.PublicKey,
        datafile: pathlib.Path,
        signature: pathlib.Path,
    ) -> None:
        """Verify an OpenPGP detached signature in a file."""
        subprocess.check_call(  # noqa: S603
            [self.cfg.prog.gnupg, "--verify", signature, datafile],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
        )
