import shutil
from unittest import mock

import pytest
from snakeoil.process.spawn import bash_version

from pkgcore.const import EBD_PATH
from pkgcore.ebuild import eapi
from pkgcore.ebuild.eapi import EAPI, eapi6, get_eapi, get_latest_PMS_eapi


def test_get_eapi():
    # unknown EAPI
    unknown_eapi = get_eapi("unknown")
    assert unknown_eapi in EAPI.unknown_eapis.values()
    # check that unknown EAPI is now registered as an unknown
    assert unknown_eapi == get_eapi("unknown")

    # known EAPI
    eapi = get_eapi("6")
    assert eapi6 == eapi


class TestEAPI:
    def test_register(self, tmp_path, caplog):
        # re-register known EAPI
        with pytest.raises(ValueError):
            EAPI.register(magic="0")

        mock_ebd_temp = str(shutil.copytree(EBD_PATH, tmp_path / "ebd"))
        with (
            mock.patch("pkgcore.ebuild.eapi.bash_version") as bash_version,
            mock.patch.dict(eapi.EAPI.known_eapis),
            mock.patch.dict(eapi.EAPI.unknown_eapis),
            mock.patch("pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp),
        ):
            # inadequate bash version
            bash_version.return_value = "3.1"
            disabled_eapi = EAPI.register(magic="new", optionals={"bash_compat": "3.2"})
            assert disabled_eapi.magic == "new"
            assert not disabled_eapi.supported
            assert "new" not in EAPI.known_eapis
            assert get_eapi("new") is disabled_eapi
            assert (
                "EAPI 'new' requires >=bash-3.2, system version: 3.1; disabling EAPI"
                in caplog.text
            )

            # adequate system bash versions
            bash_version.return_value = "3.2"
            test_eapi = EAPI.register(magic="test", optionals={"bash_compat": "3.2"})
            assert test_eapi.magic == "test"
            assert "test" in EAPI.known_eapis
            bash_version.return_value = "4.2"
            test_eapi = EAPI.register(magic="test1", optionals={"bash_compat": "4.1"})
            assert test_eapi.magic == "test1"
            assert "test1" in EAPI.known_eapis

    def test_system_bash_supports_bundled_eapis(self):
        system_bash = bash_version(force=True)
        for eapi_obj in get_latest_PMS_eapi().inherits:
            bash_compat = getattr(eapi_obj.options, "bash_compat", None)
            assert bash_compat, f"EAPI '{eapi_obj}' is missing bash_compat"
            assert system_bash >= bash_compat, (
                f"EAPI '{eapi_obj}' requires >=bash-{bash_compat}, "
                f"system version: {system_bash}"
            )
            assert eapi_obj.supported

    def test_supported(self, tmp_path, caplog):
        assert eapi6.supported

        mock_ebd_temp = str(shutil.copytree(EBD_PATH, tmp_path / "ebd"))
        with (
            mock.patch.dict(eapi.EAPI.known_eapis),
            mock.patch("pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp),
        ):
            # partially supported EAPI is flagged as such
            test_eapi = EAPI.register("test", optionals={"supported": False})
            assert test_eapi.supported
            assert caplog.text.endswith("EAPI 'test' isn't fully supported\n")

            # unsupported/unknown EAPI is flagged as such
            unknown_eapi = get_eapi("blah")
            assert not unknown_eapi.supported

    def test_inherits(self):
        for eapi_str, eapi_obj in EAPI.known_eapis.items():
            objs = (get_eapi(str(x)) for x in range(int(eapi_str), -1, -1))
            assert list(map(str, eapi_obj.inherits)) == list(map(str, objs))

    def test_ebd_env(self):
        for eapi_str, eapi_obj in EAPI.known_eapis.items():
            assert eapi_obj.ebd_env["EAPI"] == eapi_str
