import os

from e3.anod.error import SandBoxError
from e3.anod.loader import AnodSpecRepository
from e3.fs import sync_tree
from e3.os.process import Run

import pytest


class TestLoader:

    spec_dir = os.path.join(os.path.dirname(__file__), "data")
    spec2_dir = os.path.join(os.path.dirname(__file__), "data2")

    def test_spec_does_not_exist(self):
        with pytest.raises(SandBoxError) as err:
            AnodSpecRepository("/foo/bar")

        assert str(err.value).startswith("spec directory /foo/bar does not exist")

    def test_spec_loader1(self):
        spec_repo = AnodSpecRepository(self.spec_dir)
        s = spec_repo.load("loader1")
        assert s.name == "loader1"

    def test_spec_loader2(self):
        spec_repo = AnodSpecRepository(self.spec_dir)

        with pytest.raises(SandBoxError) as err:
            spec_repo.load("loader2")
        assert str(err.value).startswith("load: cannot find Anod subclass in")

    def test_invalid_spec(self):
        """Ensure that loading an invalid spec result in a SandboxError."""
        spec_repo = AnodSpecRepository(self.spec_dir)
        with pytest.raises(SandBoxError) as err:
            spec_repo.load("invalid_spec")

        assert "invalid spec code" in str(err.value)

    def test_spec_loader_prolog(self):
        spec_repo = AnodSpecRepository(self.spec_dir, spec_config=True)
        anod_class = spec_repo.load("prolog_test")

        # We should be able to load a spec twice
        anod_class = spec_repo.load("prolog_test")

        anod_instance = anod_class("prolog_test", "", "build")
        assert anod_instance.prolog_test, "prolog not executed properly"

    def test_spec_inheritance(self):
        """Load a spec that inherit from another spec."""
        spec_repo = AnodSpecRepository(self.spec_dir)
        anod_class = spec_repo.load("child")
        anod_instance = anod_class("load", "", "build")
        assert anod_instance.parent_info == "from_parent"

    def test_multiple_spec_repository(self):
        """Ensure that spec function is context dependent."""
        spec_repo = AnodSpecRepository(self.spec_dir)
        spec2_repo = AnodSpecRepository(self.spec2_dir)
        anod_class = spec_repo.load("child")
        anod_instance = anod_class("load", "", "build")
        assert anod_instance.parent_info == "from_parent"
        anod_class2 = spec2_repo.load("child")
        anod_instance2 = anod_class2("load", "", "build")
        assert anod_instance2.parent_info == "from_parent2"

    def test_load_all(self):
        spec_repo = AnodSpecRepository(self.spec_dir)
        with pytest.raises(SandBoxError):
            spec_repo.load_all()

        spec_repo = AnodSpecRepository(self.spec2_dir)
        spec_repo.load_all()
        assert "parent" in spec_repo
        assert "child" in spec_repo
        assert "unknown" not in spec_repo

    def test_load_config(self):
        spec_repo = AnodSpecRepository(self.spec_dir)
        spec_repo.api_version = "1.4"
        anod_class = spec_repo.load("withconfig")
        anod_instance = anod_class("", "build")

        # See comments in tests/tests_e3/anod/data/withconfig.anod

        assert anod_instance.test1() == 9
        with pytest.raises(KeyError) as err:
            anod_instance.test2()
        assert "foo" in str(err.value)

        assert list(anod_instance.test3()) == ["case_foo"]

    def test_load_config_api_1_5(self):
        sync_tree(self.spec_dir, "new_spec_dir")
        Run(["e3-sandbox", "migrate", "1.5", "new_spec_dir"], output=None)
        spec_repo = AnodSpecRepository("new_spec_dir")
        spec_repo.api_version = "1.5"
        anod_class = spec_repo.load("withconfig")
        anod_instance = anod_class("", "build")
        assert anod_instance.test1() == 9
        assert anod_instance.test_suffix() == 42

    def test_sandbox_migrate_unknown_api(self):
        p = Run(["e3-sandbox", "migrate", "0.2", "foo"])
        assert "Only 1.5 is supported" in p.out

    def test_getitem_without_buildspace(self):
        """Without a build space PKG_DIR returns 'unknown'."""
        spec_repo = AnodSpecRepository(self.spec_dir)
        anod_class = spec_repo.load("parent")
        anod_instance = anod_class("", "build")
        assert anod_instance["PKG_DIR"] == "unknown"
