# 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 import defs

from . import base


if typing.TYPE_CHECKING:
    import pathlib
    from typing import Final


class SOPCli(base.PGPCli):
    """Invoke a stateless OpenPGP command-line tool."""

    def generate_keys(self, uid: str) -> defs.KeyFiles:
        """Generate an OpenPGP key to sign with."""
        keydir: Final = self.cfg.path.home / "keys"
        keydir.mkdir(mode=0o755)
        keys: Final = defs.KeyFiles(
            secret=keydir / "secret.asc",
            secret_bin=keydir / "secret.gpg",
            public=keydir / "public.asc",
            public_bin=keydir / "public.gpg",
        )

        secret_text: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "generate-key", "--profile=rfc4880", uid],
            cwd=self.cfg.path.work,
            encoding="UTF-8",
            env=self.cfg.env,
        )
        if not secret_text.startswith("-----BEGIN PGP PRIVATE KEY BLOCK-----"):
            raise RuntimeError(repr((self.cfg.prog.sop, secret_text)))
        keys.secret.write_text(secret_text, encoding="UTF-8")

        # This is not UTF-8 text; it may contain arbitrary byte values.
        secret_bin: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "dearmor"],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
            input=secret_text.encode("UTF-8"),
        )
        keys.secret_bin.write_bytes(secret_bin)

        public_text: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "extract-cert"],
            cwd=self.cfg.path.work,
            encoding="UTF-8",
            env=self.cfg.env,
            input=secret_text,
        )
        if not public_text.startswith("-----BEGIN PGP PUBLIC KEY BLOCK-----"):
            raise RuntimeError(repr((self.cfg.prog.sop, public_text)))
        keys.public.write_text(public_text, encoding="UTF-8")

        # This is not UTF-8 text; it may contain arbitrary byte values.
        public_bin: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "dearmor"],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
            input=public_text.encode("UTF-8"),
        )
        keys.public_bin.write_bytes(public_bin)

        return keys

    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."""
        # We don't need that yet, and we cannot parse an OpenPGP key's structure by ourselves.
        raise NotImplementedError

    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(f"{datafile.name}.asc")
        sig_contents: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "sign", keys.secret],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
            input=datafile.read_bytes(),
        )
        signature.write_bytes(sig_contents)
        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."""
        sig_report: Final = subprocess.check_output(  # noqa: S603
            [self.cfg.prog.sop, "verify", signature, keys.public],
            cwd=self.cfg.path.work,
            env=self.cfg.env,
            input=datafile.read_bytes(),
        ).decode()
        if not sig_report.endswith(f"{pub_key.key_id} mode:binary\n"):
            raise RuntimeError(repr((pub_key, sig_report)))
