"""Test the rpmautospec plugin."""

from copy import deepcopy
from pathlib import Path
from unittest import mock

import pytest

from mockbuild.exception import ConfigError, PkgError
from mockbuild.plugins import rpmautospec
from mockbuild.util import nullcontext

UNSET = object()


@mock.patch("mockbuild.plugins.rpmautospec.RpmautospecPlugin")
def test_init(RpmautospecPlugin):  # pylint: disable=invalid-name
    """Test the function which registers the plugin."""
    plugins = object()
    conf = object()
    buildroot = object()

    rpmautospec.init(plugins, conf, buildroot)

    RpmautospecPlugin.assert_called_once_with(plugins, conf, buildroot)


class TestRpmautospecPlugin:
    """Test the RpmautospecPlugin class."""

    DEFAULT_OPTS = {
        "requires": ["rpmautospec"],
        "cmd_base": ["rpmautospec", "process-distgit"],
    }

    def create_plugin(self, plugins=UNSET, conf=UNSET, buildroot=UNSET):
        """Create a plugin object and prepare it for testing."""
        if plugins is UNSET:
            plugins = mock.Mock()
        if conf is UNSET:
            conf = deepcopy(self.DEFAULT_OPTS)
        if buildroot is UNSET:
            buildroot = mock.Mock()

        return rpmautospec.RpmautospecPlugin(plugins, conf, buildroot)

    @pytest.mark.parametrize(
        "with_cmd_base", (True, False), ids=("with-cmd_base", "without-cmd_base")
    )
    @mock.patch("mockbuild.plugins.rpmautospec.getLog")
    def test___init__(self, getLog, with_cmd_base):
        """Test the constructor."""
        plugins = mock.Mock()

        conf = deepcopy(self.DEFAULT_OPTS)
        if with_cmd_base:
            expectation = nullcontext()
        else:
            expectation = pytest.raises(ConfigError)
            del conf["cmd_base"]

        buildroot = mock.Mock()

        with expectation as exc_info:
            logger = getLog.return_value
            plugin = self.create_plugin(plugins=plugins, conf=conf, buildroot=buildroot)

        if with_cmd_base:
            assert plugin.buildroot is buildroot
            assert plugin.config is buildroot.config
            assert plugin.opts is conf
            assert plugin.log is logger
            plugins.add_hook.assert_called_once_with("pre_srpm_build", plugin.attempt_process_distgit)
            logger.info.assert_called_once_with("rpmautospec: initialized")
        else:
            assert "rpmautospec_opts.cmd_base" in str(exc_info.value)

    @pytest.mark.parametrize(
        "testcase",
        (
            "happy-path",
            "happy-path-no-requires",
            "without-sources",
            "sources-not-dir",
            "sources-not-repo",
            "sources-no-specfile",
            "spec-files-different",
            "specfile-no-rpmautospec",
            "broken-requires",
        ),
    )
    def test_attempt_process_distgit(
        self, testcase, tmp_path
    ):  # pylint: disable=too-many-branches disable=too-many-statements disable=too-many-locals
        """Test the attempt_process_distgit() method."""
        # Set the stage
        plugin = self.create_plugin()
        plugin.log = log = mock.Mock()
        plugin.buildroot.make_chroot_path.return_value = str(tmp_path)
        if "no-requires" in testcase:
            plugin.opts["requires"] = []

        spec_dir = tmp_path / "SPECS"
        spec_dir.mkdir()
        sources_dir = tmp_path / "SOURCES"
        sources_dir.mkdir()

        host_chroot_spec = spec_dir / "pkg.spec"
        host_chroot_sources = sources_dir / "pkg"
        host_chroot_sources.mkdir()
        host_chroot_sources_git = host_chroot_sources / ".git"
        host_chroot_sources_spec = host_chroot_sources / "pkg.spec"

        if "no-rpmautospec" not in testcase:
            spec_contents = (
                "Release: %autorelease",
                "%changelog",
                "%autochangelog",
            )
        else:
            spec_contents = (
                "Release: 1",
                "%changelog",
            )

        with host_chroot_spec.open("w") as fp:
            for line in spec_contents:
                print(line, file=fp)

        if "without-sources" in testcase:
            host_chroot_sources = None
        elif "sources-not-dir" not in testcase:
            if "sources-no-specfile" not in testcase:
                with host_chroot_sources_spec.open("w") as fp:
                    if "spec-files-different" in testcase:
                        print("# BOO", file=fp)
                    for line in spec_contents:
                        print(line, file=fp)
            if "sources-not-repo" not in testcase:
                host_chroot_sources_git.mkdir()
        else:
            host_chroot_sources = tmp_path / "pkg.tar"
            host_chroot_sources.touch()

        if "broken-requires" in testcase:
            plugin.buildroot.install_as_root.side_effect = RuntimeError("FAIL")
            expect_exception = pytest.raises(PkgError)
        else:
            expect_exception = nullcontext()

        with expect_exception as excinfo:
            plugin.attempt_process_distgit(host_chroot_spec, host_chroot_sources)

        if "happy-path" in testcase:
            chroot_spec = Path("/") / host_chroot_spec.relative_to(tmp_path)
            chroot_sources = Path("/") / host_chroot_sources.relative_to(tmp_path)
            chroot_sources_spec = Path("/") / host_chroot_sources_spec.relative_to(tmp_path)

            expected_command = plugin.opts["cmd_base"] + [chroot_sources_spec, chroot_spec]

            plugin.buildroot.doChrootPlugin.assert_called_once_with(
                expected_command,
                cwd=chroot_sources,
            )
        else:
            plugin.buildroot.doChrootPlugin.assert_not_called()
            if "broken-requires" not in testcase:
                if "spec-files-different" in testcase:
                    log_method = log.warning
                else:
                    log_method = log.debug
                log_method.assert_called_once()
                log_string = log_method.call_args[0][0]
                assert "skipping rpmautospec preprocessing" in log_string

                if "without-sources" in testcase:
                    assert "Sources not specified" in log_string
                elif "sources-not-dir" in testcase:
                    assert "Sources not a directory" in log_string
                elif "sources-not-repo" in testcase:
                    assert "Sources is not a git repository" in log_string
                elif "sources-no-specfile" in testcase:
                    assert "Sources doesn’t contain spec file" in log_string
                elif "spec-files-different" in testcase:
                    assert "Spec file inside and outside sources are different" in log_string
                elif "specfile-no-rpmautospec" in testcase:
                    assert "Spec file doesn’t use rpmautospec" in log_string
            else:
                assert str(excinfo.value) == (
                    "Can’t install rpmautospec dependencies into chroot: "
                    + ", ".join(self.DEFAULT_OPTS["requires"])
                )
