from unittest.mock import patch

import pytest
from snakeoil.cli import arghparse

from pkgcheck.checks import SkipCheck, perl

from .. import misc

REASON = ""


def perl_deps_missing():
    """Check if perl deps are missing."""
    global REASON
    try:
        perl.PerlCheck(arghparse.Namespace(verbosity=1))
    except SkipCheck as e:
        REASON = str(e)
        return True
    return False


@pytest.mark.skipif(perl_deps_missing(), reason=REASON)
class TestPerlCheck(misc.ReportTestCase):
    check_kls = perl.PerlCheck

    def mk_check(self, verbosity=0):
        return self.check_kls(arghparse.Namespace(verbosity=verbosity))

    def mk_pkg(self, PVR, dist_version="", eclasses=("perl-module",), **kwargs):
        lines = ["inherit perl-module\n"]
        if dist_version:
            lines.append(f"DIST_VERSION={dist_version}\n")
        kwargs.setdefault("EAPI", "7")
        kwargs.setdefault("_eclasses_", list(eclasses))
        return misc.FakePkg(f"app-foo/bar-{PVR}", lines=lines, data=kwargs)

    def test_matching(self):
        """Ebuilds with matching DIST_VERSION and package version."""
        for PVR in ("1.7.0-r0", "1.7.0", "1.7.0-r100"):
            self.assertNoReport(self.mk_check(), self.mk_pkg(PVR, "1.007"))

    def test_nonmatching(self):
        """Ebuilds without matching DIST_VERSION and package version."""
        for PVR in ("1.7.0-r0", "1.7.0", "1.7.0-r100"):
            r = self.assertReport(self.mk_check(), self.mk_pkg(PVR, "1.07"))
            assert isinstance(r, perl.MismatchedPerlVersion)
            assert r.dist_version == "1.07"
            assert r.normalized == "1.70.0"
            assert "DIST_VERSION=1.07 normalizes to 1.70.0" in str(r)
            r = self.assertReport(self.mk_check(), self.mk_pkg(PVR, "1.7"))
            assert isinstance(r, perl.MismatchedPerlVersion)
            assert r.dist_version == "1.7"
            assert r.normalized == "1.700.0"
            assert "DIST_VERSION=1.7 normalizes to 1.700.0" in str(r)

    def test_no_dist_version(self):
        """Ebuilds without DIST_VERSION defined are skipped."""
        self.assertNoReport(self.mk_check(), self.mk_pkg("1.7.0"))

    def test_no_perl(self):
        """Check initialization fails if perl isn't installed."""
        with patch("subprocess.Popen") as popen:
            popen.side_effect = FileNotFoundError("perl not available")
            with pytest.raises(SkipCheck, match="perl not installed"):
                self.mk_check()

    def test_no_perl_deps(self):
        """Check initialization fails if perl deps aren't installed."""
        with patch("pkgcheck.checks.perl.subprocess.Popen") as popen:
            popen.return_value.stdout.readline.return_value = "perl error"
            popen.return_value.poll.return_value = 2
            for verbosity in (0, 1):
                with pytest.raises(SkipCheck, match="failed to run perl script"):
                    self.mk_check(verbosity=verbosity)

    @pytest.mark.parametrize(
        "cpv", ("dev-lang/perl-0", "perl-core/pkgcheck-0", "virtual/perl-pkgcheck-0")
    )
    def test_missing_op_virtual_good_packages(self, cpv):
        pkg = misc.FakePkg(cpv, data={"RDEPEND": "virtual/perl-pkgcheck"})
        self.assertNoReport(self.mk_check(), pkg)

    @pytest.mark.parametrize(
        "rdepend",
        (
            ">=virtual/perl-pkgcheck-0",
            "<virtual/perl-pkgcheck-0",
            "virtual/pkgcheck",
            "dev-cpp/perl-pkgcheck-0",
        ),
    )
    def test_missing_op_virtual_good_deps(self, rdepend):
        pkg = misc.FakePkg("dev-cpp/perl-pkgcheck-0", data={"RDEPEND": rdepend})
        self.assertNoReport(self.mk_check(), pkg)

    def test_missing_op_virtual_bad(self):
        pkg = misc.FakePkg(
            "dev-cpp/perl-pkgcheck-0",
            data={
                "RDEPEND": "virtual/perl-pkgcore virtual/perl-pkgcheck",
                "DEPEND": "virtual/perl-pkgcheck",
                "BDEPEND": ">=virtual/perl-pkgcore-0",
            },
        )
        reports = self.assertReports(self.mk_check(), pkg)
        assert len(reports) == 2
        for r in reports:
            assert isinstance(r, perl.MissingVersionedVirtualPerlDependency)
        assert reports[0].atom == "virtual/perl-pkgcheck"
        assert reports[1].atom == "virtual/perl-pkgcore"
