"""Tests for resolver.py — runtime dependency path selector."""

import pytest

from strawhub.errors import DependencyError
from strawhub.resolver import resolve


class TestResolveBasic:
    def test_resolve_skill_no_deps(self, strawpot_dir, make_skill):
        make_skill("git-workflow", "1.0.0")
        result = resolve(
            "git-workflow", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "git-workflow"
        assert result["kind"] == "skill"
        assert result["version"] == "1.0.0"
        assert result["dependencies"] == []

    def test_resolve_skill_with_deps(self, strawpot_dir, make_skill):
        make_skill("security-baseline", "1.0.0")
        make_skill("code-review", "1.0.0", deps=["security-baseline"])
        result = resolve(
            "code-review", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "code-review"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"security-baseline"}

    def test_resolve_role_with_skill_deps(self, strawpot_dir, make_skill, make_role):
        make_skill("git-workflow", "1.0.0")
        make_skill("code-review", "1.0.0")
        make_role("implementer", "1.0.0", skill_deps=["git-workflow", "code-review"])

        result = resolve(
            "implementer", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "implementer"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"git-workflow", "code-review"}

    def test_resolve_transitive(self, strawpot_dir, make_skill, make_role):
        """Role → skill → skill (transitive)."""
        make_skill("security-baseline", "1.0.0")
        make_skill("code-review", "1.0.0", deps=["security-baseline"])
        make_role("reviewer", "1.0.0", skill_deps=["code-review"])

        result = resolve(
            "reviewer", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"code-review", "security-baseline"}


class TestResolveSingleVersion:
    def test_returns_installed_version(self, strawpot_dir, make_skill):
        make_skill("git-workflow", "2.0.0")

        result = resolve(
            "git-workflow", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["version"] == "2.0.0"

    def test_dep_version_spec_ignored(self, strawpot_dir, make_skill, make_role):
        """Version specs in deps are stripped; resolver uses installed version."""
        make_skill("git-workflow", "2.0.0")
        make_role("implementer", "1.0.0", skill_deps=["git-workflow==1.0.0"])

        result = resolve(
            "implementer", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        gw = next(d for d in result["dependencies"] if d["slug"] == "git-workflow")
        assert gw["version"] == "2.0.0"

    def test_bare_slug_dep_resolves(self, strawpot_dir, make_skill, make_role):
        """Bare slug deps resolve the installed version."""
        make_skill("git-workflow", "2.0.0")
        make_role("reviewer", "1.0.0", skill_deps=["git-workflow"])

        result = resolve(
            "reviewer", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        gw = next(d for d in result["dependencies"] if d["slug"] == "git-workflow")
        assert gw["version"] == "2.0.0"

    def test_resolve_specific_version(self, strawpot_dir, make_skill):
        make_skill("git-workflow", "2.0.0")

        result = resolve(
            "git-workflow", kind="skill", version="2.0.0",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["version"] == "2.0.0"


class TestResolveCrossScope:
    def test_local_and_global(self, tmp_path):
        local = tmp_path / "local"
        glob = tmp_path / "global"

        # v1.0.0 in global
        d = glob / "skills" / "git-workflow"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            '---\nname: git-workflow\ndescription: "v1"\n---\n# GW\n'
        )
        (d / ".version").write_text("1.0.0\n")

        # v1.2.0 in local
        d = local / "skills" / "git-workflow"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            '---\nname: git-workflow\ndescription: "v1.2"\n---\n# GW\n'
        )
        (d / ".version").write_text("1.2.0\n")

        result = resolve(
            "git-workflow", kind="skill",
            local_root=local, global_root=glob,
        )
        # Should pick 1.2.0 (local, higher)
        assert result["version"] == "1.2.0"
        assert result["source"] == "local"

    def test_global_higher_than_local(self, tmp_path):
        local = tmp_path / "local"
        glob = tmp_path / "global"

        d = local / "skills" / "git-workflow"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            '---\nname: git-workflow\ndescription: "v1"\n---\n# GW\n'
        )
        (d / ".version").write_text("1.0.0\n")

        d = glob / "skills" / "git-workflow"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            '---\nname: git-workflow\ndescription: "v2"\n---\n# GW\n'
        )
        (d / ".version").write_text("2.0.0\n")

        result = resolve(
            "git-workflow", kind="skill",
            local_root=local, global_root=glob,
        )
        assert result["version"] == "2.0.0"
        assert result["source"] == "global"


class TestResolveErrors:
    def test_not_installed(self, strawpot_dir):
        with pytest.raises(DependencyError, match="not installed"):
            resolve(
                "nonexistent", kind="skill",
                local_root=strawpot_dir, global_root=strawpot_dir,
            )

    def test_version_not_installed(self, strawpot_dir, make_skill):
        make_skill("git-workflow", "1.0.0")
        with pytest.raises(DependencyError, match="not installed"):
            resolve(
                "git-workflow", kind="skill", version="9.9.9",
                local_root=strawpot_dir, global_root=strawpot_dir,
            )

    def test_circular_dependency_resolves(self, strawpot_dir):
        """Two skills that depend on each other should resolve successfully."""
        d = strawpot_dir / "skills" / "a"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            "---\nname: a\ndescription: \"a\"\nmetadata:\n  strawpot:\n    dependencies:\n      - b\n---\n# A\n"
        )
        (d / ".version").write_text("1.0.0\n")

        d = strawpot_dir / "skills" / "b"
        d.mkdir(parents=True)
        (d / "SKILL.md").write_text(
            "---\nname: b\ndescription: \"b\"\nmetadata:\n  strawpot:\n    dependencies:\n      - a\n---\n# B\n"
        )
        (d / ".version").write_text("1.0.0\n")

        result = resolve(
            "a", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "a"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"b"}

    def test_kind_required(self, strawpot_dir, make_skill):
        """kind is a required argument."""
        make_skill("git-workflow", "1.0.0")
        with pytest.raises(TypeError):
            resolve(
                "git-workflow",
                local_root=strawpot_dir, global_root=strawpot_dir,
            )


class TestResolveRoleDeps:
    def test_role_depends_on_role(self, strawpot_dir, make_skill, make_role):
        make_skill("code-review", "1.0.0")
        make_role("reviewer", "1.0.0", skill_deps=["code-review"])
        make_role("lead", "1.0.0", role_deps=["reviewer"])

        result = resolve(
            "lead", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"reviewer", "code-review"}

    def test_diamond_dependency(self, strawpot_dir, make_skill, make_role):
        """A → B, A → C, B → D, C → D. D should appear once."""
        make_skill("d", "1.0.0")
        make_skill("b", "1.0.0", deps=["d"])
        make_skill("c", "1.0.0", deps=["d"])
        make_role("a", "1.0.0", skill_deps=["b", "c"])

        result = resolve(
            "a", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = [d["slug"] for d in result["dependencies"]]
        assert dep_slugs.count("d") == 1
        assert set(dep_slugs) == {"b", "c", "d"}

    def test_wildcard_role_dep_ignored(self, strawpot_dir, make_skill, make_role):
        """'*' in role deps is skipped during resolution."""
        make_skill("code-review", "1.0.0")
        make_role("lead", "1.0.0", skill_deps=["code-review"], role_deps=["*"])

        result = resolve(
            "lead", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert "*" not in dep_slugs
        assert dep_slugs == {"code-review"}

    def test_wildcard_skill_dep_ignored(self, strawpot_dir, make_skill, make_role):
        """'*' in skill deps is skipped during resolution."""
        make_role("lead", "1.0.0", skill_deps=["*"])

        result = resolve(
            "lead", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert "*" not in dep_slugs
        assert dep_slugs == set()

    def test_wildcard_with_explicit_role_dep(self, strawpot_dir, make_skill, make_role):
        """'*' alongside explicit role deps: explicit deps resolved, '*' skipped."""
        make_skill("testing", "1.0.0")
        make_role("reviewer", "1.0.0", skill_deps=["testing"])
        make_role("lead", "1.0.0", role_deps=["reviewer", "*"])

        result = resolve(
            "lead", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert "*" not in dep_slugs
        assert "reviewer" in dep_slugs
        assert "testing" in dep_slugs


class TestCircularDependencies:
    """Circular dependencies should resolve gracefully, not error."""

    def test_two_roles_circular(self, strawpot_dir, make_role):
        """Role A ↔ Role B (the design-system-architect ↔ visual-qa-reviewer case)."""
        make_role("design-system-architect", "1.0.0", role_deps=["visual-qa-reviewer"])
        make_role("visual-qa-reviewer", "1.0.0", role_deps=["design-system-architect"])

        result = resolve(
            "design-system-architect", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "design-system-architect"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"visual-qa-reviewer"}

    def test_three_way_cycle(self, strawpot_dir, make_skill):
        """A → B → C → A."""
        make_skill("a", "1.0.0", deps=["b"])
        make_skill("b", "1.0.0", deps=["c"])
        make_skill("c", "1.0.0", deps=["a"])

        result = resolve(
            "a", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "a"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"b", "c"}

    def test_circular_with_shared_deps(self, strawpot_dir, make_skill, make_role):
        """Roles A ↔ B, both depending on skill S."""
        make_skill("shared-skill", "1.0.0")
        make_role("role-a", "1.0.0", skill_deps=["shared-skill"], role_deps=["role-b"])
        make_role("role-b", "1.0.0", skill_deps=["shared-skill"], role_deps=["role-a"])

        result = resolve(
            "role-a", kind="role",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "role-a"
        dep_slugs = {d["slug"] for d in result["dependencies"]}
        assert dep_slugs == {"role-b", "shared-skill"}

    def test_self_dependency(self, strawpot_dir, make_skill):
        """A skill that depends on itself."""
        make_skill("self-dep", "1.0.0", deps=["self-dep"])

        result = resolve(
            "self-dep", kind="skill",
            local_root=strawpot_dir, global_root=strawpot_dir,
        )
        assert result["slug"] == "self-dep"
        assert result["dependencies"] == []
