# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
# SPDX-License-Identifier: GPL-2.0-or-later
"""Build `debsigs` in a temporary directory, run the test."""

from __future__ import annotations

import enum
import os
import pathlib
import shlex
import subprocess  # noqa: S404
import sys
import tempfile
import typing

import click


if typing.TYPE_CHECKING:
    from typing import Final


class Impl(str, enum.Enum):
    """The supported `debsigs` implementations."""

    PERL = "perl"

    def __str__(self) -> str:
        """Use the value itself as a string representation."""
        return self.value


def copy_source(tempd: pathlib.Path, u8env: dict[str, str]) -> pathlib.Path:
    """Copy the `debsigs` source files over to the temporary directory."""
    srcdir: Final = tempd / "debsigs"
    srcdir.mkdir(mode=0o755)

    files: Final = sorted(
        set(
            subprocess.check_output(  # noqa: S603
                [  # noqa: S607
                    "git",
                    "ls-files",
                ],
                encoding="UTF-8",
                env=u8env,
            ).splitlines(),
        ),
    )

    srcball: Final = tempd / "debsigs.tar"
    subprocess.check_call(  # noqa: S603
        [  # noqa: S607
            "tar",
            "-cf",
            srcball,
            "--",
            *files,
        ],
        env=u8env,
    )
    subprocess.check_call(  # noqa: S603
        [  # noqa: S607
            "tar",
            "-xf",
            srcball,
        ],
        cwd=srcdir,
        env=u8env,
    )
    srcball.unlink()
    return srcdir


def build_debsigs_perl(
    bindir: pathlib.Path,
    srcdir: pathlib.Path,
    u8env: dict[str, str],
) -> pathlib.Path:
    """Build the Perl implementation of `debsigs`, create a wrapper script."""
    perldir: Final = srcdir / "perl"
    subprocess.check_call(  # noqa: S603
        [  # noqa: S607
            "perl",
            "Makefile.PL",
        ],
        cwd=perldir,
        env=u8env,
    )
    subprocess.check_call(  # noqa: S603
        [  # noqa: S607
            "make",
        ],
        cwd=perldir,
        env=u8env,
    )
    blibdir: Final = perldir / "blib/lib"
    if not blibdir.is_dir():
        raise RuntimeError(repr((perldir, blibdir)))

    def create_wrapper(name: str) -> pathlib.Path:
        """Create a single wrapper for one of the Perl command-line tools."""
        wrapper: Final = bindir / name
        wrapper.write_text(
            f"""#!/bin/sh

set -e

exec perl -I{shlex.quote(str(blibdir))} -- {shlex.quote(str(perldir / name))} "$@"
""",
            encoding="UTF-8",
        )
        wrapper.chmod(0o755)
        return wrapper

    wrapper: Final = create_wrapper("debsigs")
    # Make sure the wrapper script works.
    version: Final = subprocess.check_output(  # noqa: S603
        [wrapper, "--version"],
        encoding="UTF-8",
        env=u8env,
    )
    if not version.startswith("debsigs v0.2."):
        raise RuntimeError(repr(version))

    for other in ("debsigs-autosign", "debsigs-signchanges"):
        create_wrapper(other)

    return wrapper


def build_debsigs(
    impl: Impl,
    bindir: pathlib.Path,
    srcdir: pathlib.Path,
    u8env: dict[str, str],
) -> pathlib.Path:
    """Build the `debsigs` implementation, return the path to a wrapper script."""
    match impl:
        case Impl.PERL:
            return build_debsigs_perl(bindir, srcdir, u8env)

        case _:
            raise NotImplementedError(repr(impl))


@click.command(name="build-and-test")
@click.option(
    "--debsigs-impl",
    type=Impl,
    default=Impl.PERL,
    help="the debsigs implementation to test",
)
@click.option(
    "--debsig-verify-program",
    type=click.Path(exists=True, executable=True, path_type=pathlib.Path, resolve_path=True),
    default="/usr/bin/debsig-verify",
    help="the debsig-verify program to check the signed package files with",
)
@click.option(
    "--gnupg-program",
    type=click.Path(exists=True, executable=True, path_type=pathlib.Path, resolve_path=True),
    default="/usr/bin/gpg",
    help="the GnuPG program to test and sign with",
)
@click.option(
    "--sop-program",
    type=click.Path(exists=True, executable=True, path_type=pathlib.Path, resolve_path=True),
    default="/usr/bin/sqop",
    help="the stateless OpenPGP program to generate keys with",
)
def main(
    *,
    debsigs_impl: Impl,
    debsig_verify_program: pathlib.Path,
    gnupg_program: pathlib.Path,
    sop_program: pathlib.Path,
) -> None:
    """Parse command-line options, build `debsigs`, run the test."""
    with tempfile.TemporaryDirectory(prefix="debsigs-build.") as tempd_obj:
        tempd: Final = pathlib.Path(tempd_obj)
        print(f"build-and-test: using {tempd} as a temporary directory")

        u8env: Final = dict(os.environ)
        u8env["LC_ALL"] = "C.UTF-8"
        if "LANGUAGE" in u8env:
            del u8env["LANGUAGE"]

        srcdir: Final = copy_source(tempd, u8env)
        print(f"build-and-test: copied the debsigs source to {srcdir}")

        bindir: Final = tempd / "bin"
        bindir.mkdir(mode=0o755)
        wrapper: Final = build_debsigs(debsigs_impl, bindir, srcdir, u8env)
        print(f"build-and-test: built the debsigs source, created {wrapper}")

        print("build-and-test: invoking testsigs")
        subprocess.check_call(  # noqa: S603
            [
                sys.executable,
                "-B",
                "-u",
                "-m",
                "testsigs",
                "--debsigs-program",
                wrapper,
                "--debsig-verify-program",
                debsig_verify_program,
                "--gnupg-program",
                gnupg_program,
                "--sop-program",
                sop_program,
                "--test-datadir",
                srcdir / "test-data",
            ],
            cwd=tempd,
            env=u8env,
        )

        print("build-and-test: seems fine")


if __name__ == "__main__":
    main()
