import pytest

from pkgcraft.config import Config
from pkgcraft.dep import Cpv, Dep, Version
from pkgcraft.error import InvalidRestrict

from ..misc import OperatorMap


class BaseRepoTests:
    def test_attrs_base(self, make_repo):
        r = make_repo(id="fake")

        # default
        assert r.id == "fake"
        assert str(r) == "fake"

    def test_pkg_methods_base(self, repo):
        # empty repo
        assert not repo.categories
        assert not repo.packages("cat")
        assert not repo.versions("cat", "pkg")

        # create pkg
        repo.create_pkg("cat1/pkga-1")
        assert repo.categories == ["cat1"]
        assert repo.packages("cat1") == ["pkga"]
        assert repo.versions("cat1", "pkga") == [Version("1")]

        # create new pkg version
        repo.create_pkg("cat1/pkga-2")
        assert repo.categories == ["cat1"]
        assert repo.packages("cat1") == ["pkga"]
        assert repo.versions("cat1", "pkga") == [Version("1"), Version("2")]

        # create new pkg
        repo.create_pkg("cat1/pkgb-1")
        assert repo.categories == ["cat1"]
        assert repo.packages("cat1") == ["pkga", "pkgb"]

        # create new pkg in new category
        repo.create_pkg("cat2/pkga-1")
        assert repo.categories == ["cat1", "cat2"]
        assert repo.packages("cat2") == ["pkga"]

    def test_cmp_base(self, make_repo):
        for r1_args, op, r2_args in (
            (["a"], "<", ["b"]),
            (["a", 2], "<=", ["b", 1]),
            (["a"], "!=", ["b"]),
            (["b", 1], ">=", ["a", 2]),
            (["b"], ">", ["a"]),
        ):
            config = Config()
            op_func = OperatorMap[op]
            r1 = make_repo(None, *r1_args, config=config)
            r2 = make_repo(None, *r2_args, config=config)
            assert op_func(r1, r2), f"failed {r1_args} {op} {r2_args}"

        # verify incompatible type comparisons
        obj = make_repo()
        for op, op_func in OperatorMap.items():
            if op == "==":
                assert not op_func(obj, None)
            elif op == "!=":
                assert op_func(obj, None)
            else:
                with pytest.raises(TypeError):
                    op_func(obj, None)

    def test_hash_base(self, make_repo):
        r1 = make_repo()
        r2 = make_repo()
        assert len({r1, r2}) == 2

    def test_contains_base(self, make_repo):
        r1 = make_repo()
        r2 = make_repo()
        pkg1 = r1.create_pkg("cat/pkg-1")
        pkg2 = r2.create_pkg("cat/pkg-1")

        # Cpv strings
        assert "cat/pkg-1" in r1
        assert "cat/pkg-2" not in r1
        # Cpv objects
        assert Cpv("cat/pkg-1") in r1
        assert Cpv("cat/pkg-2") not in r1
        # dep strings
        assert "cat/pkg" in r1
        assert "cat/pkg2" not in r1
        assert "=cat/pkg-1" in r1
        assert "=cat/pkg-2" not in r1
        # Dep objects
        assert Dep("=cat/pkg-1") in r1
        assert Dep("=cat/pkg-2") not in r1
        # Pkg objects
        assert pkg1 in r1
        assert pkg2 not in r1
        assert pkg1 not in r2
        assert pkg2 in r2
        # Cpv objects
        assert pkg1.cpv in r1
        assert pkg2.cpv in r1

        for obj in (object(), None):
            with pytest.raises(TypeError):
                assert obj in r1

    def test_getitem_base(self, repo):
        pkg1 = repo.create_pkg("cat/pkg-1")
        pkg2 = repo.create_pkg("cat/pkg-2")
        assert repo["cat/pkg-1"] == pkg1
        assert repo[Cpv("cat/pkg-1")] == pkg1
        assert repo[Dep("=cat/pkg-1")] == pkg1
        assert repo[Dep(">=cat/pkg-2")] == pkg2
        assert repo["cat/pkg"] == [pkg1, pkg2]
        assert repo["pkg"] == [pkg1, pkg2]
        assert repo["*"] == [pkg1, pkg2]

        # nonexistent matches
        for obj in ("cat/pkg-3", Cpv("cat/pkg-3"), Dep("<cat/pkg-1")):
            with pytest.raises(KeyError):
                _ = repo[obj]

        # invalid key values
        for obj in ("<cat/pkg", "maintainer is"):
            with pytest.raises(ValueError):
                repo[obj]

        # invalid key types
        for obj in (object(), None):
            with pytest.raises(TypeError):
                repo[obj]

    def test_bool_and_len_base(self, repo):
        # empty repo
        assert not repo
        assert len(repo) == 0

        # create pkg
        repo.create_pkg("cat/pkg-1")
        assert repo
        assert len(repo) == 1

        # recreate pkg
        repo.create_pkg("cat/pkg-1")
        assert repo
        assert len(repo) == 1

        # create new pkg version
        repo.create_pkg("cat/pkg-2")
        assert repo
        assert len(repo) == 2

    def test_iter_cpv_base(self, repo):
        # empty repo
        assert not list(repo.iter_cpv())

        # single pkg
        repo.create_pkg("cat/pkg-1")
        assert list(repo.iter_cpv()) == [Cpv("cat/pkg-1")]

        # multiple pkgs
        repo.create_pkg("cat/pkg-2")
        assert list(repo.iter_cpv()) == [Cpv("cat/pkg-1"), Cpv("cat/pkg-2")]

    def test_iter_base(self, repo):
        # calling next() directly on a repo object fails
        with pytest.raises(TypeError):
            next(repo)

        # empty repo
        assert not list(repo)

        # single pkg
        repo.create_pkg("cat/pkg-1")
        assert list(map(str, repo)) == ["cat/pkg-1::fake"]

        # multiple pkgs
        repo.create_pkg("cat/pkg-2")
        assert list(map(str, repo)) == ["cat/pkg-1::fake", "cat/pkg-2::fake"]

        # nested calls return equivalent objects
        assert list(iter(repo)) == list(iter(iter(repo)))

        # repo.iter() with no args is equivalent to iter(repo)
        assert list(iter(repo)) == list(repo.iter())

    def test_iter_restrict_base(self, repo):
        # unsupported object type
        with pytest.raises(TypeError):
            list(repo.iter(object()))

        cpv = Cpv("cat/pkg-1")
        dep = Dep(">=cat/pkg-1")

        # empty repo -- no matches
        assert not list(repo.iter(cpv))
        assert not list(repo.iter(dep))

        pkg1 = repo.create_pkg("cat/pkg-1")
        pkg2 = repo.create_pkg("cat/pkg-2")

        # non-empty repo -- no matches
        nonexistent = Cpv("nonexistent/pkg-1")
        assert not list(repo.iter(nonexistent))

        # single match via Cpv
        assert list(repo.iter(cpv)) == [pkg1]

        # single match via package
        assert list(repo.iter(pkg1)) == [pkg1]

        # multiple matches via restriction glob
        assert list(repo.iter("cat/*")) == [pkg1, pkg2]

        # multiple matches via Dep
        assert list(repo.iter(dep)) == [pkg1, pkg2]

        # invalid restriction string
        with pytest.raises(InvalidRestrict):
            list(repo.iter("-"))
