"""Tests for thinking layer — Scout, Physicist, Architect."""

import pytest
from xyglue.thinking.models import (
    BenchmarkMap, BenchmarkElement, ClassifiedMap, Constraint,
    Path, ThinkingResult,
)
from xyglue.thinking.scout import Scout
from xyglue.thinking.physicist import Physicist
from xyglue.thinking.architect import Architect
from xyglue.goal.models import Goal


class TestBenchmarkElement:
    def test_basic_element(self):
        e = BenchmarkElement(name="REST API", tool="http", category="approach")
        assert e.name == "REST API"
        assert e.is_standard is True

    def test_to_dict_from_dict(self):
        e = BenchmarkElement(name="test", tool="http")
        d = e.to_dict()
        e2 = BenchmarkElement.from_dict(d)
        assert e2.name == "test"


class TestBenchmarkMap:
    def test_basic_map(self):
        bm = BenchmarkMap(domain="web")
        assert bm.domain == "web"
        assert bm.elements == []

    def test_to_dict(self):
        bm = BenchmarkMap(domain="data", standard_tools=["postgres"])
        d = bm.to_dict()
        assert d["domain"] == "data"

    def test_from_dict(self):
        bm = BenchmarkMap(domain="web", standard_tools=["http"])
        d = bm.to_dict()
        bm2 = BenchmarkMap.from_dict(d)
        assert bm2.domain == "web"


class TestConstraint:
    def test_physics_constraint(self):
        c = Constraint(element_name="rate_limit", constraint_type="physics", breakable=False)
        assert c.constraint_type == "physics"
        assert c.breakable is False

    def test_habit_constraint(self):
        c = Constraint(element_name="logging", constraint_type="habit", breakable=True)
        assert c.constraint_type == "habit"


class TestClassifiedMap:
    def test_default_classified(self):
        cm = ClassifiedMap()
        assert cm.physics_count == 0
        assert cm.habit_count == 0

    def test_with_constraints(self):
        cm = ClassifiedMap(constraints=[
            Constraint(element_name="rate_limit", constraint_type="physics"),
            Constraint(element_name="logging", constraint_type="habit"),
            Constraint(element_name="caching", constraint_type="habit"),
        ])
        assert cm.physics_count == 1
        assert cm.habit_count == 2

    def test_get_habits(self):
        cm = ClassifiedMap(constraints=[
            Constraint(constraint_type="physics"),
            Constraint(constraint_type="habit"),
        ])
        habits = cm.get_habits()
        assert len(habits) == 1

    def test_to_dict(self):
        cm = ClassifiedMap()
        d = cm.to_dict()
        assert "physics_count" in d
        assert "constraints" in d


class TestPath:
    def test_basic_path(self):
        p = Path(id="A", description="Max divergence", confidence=0.8)
        assert p.id == "A"
        assert p.confidence == 0.8

    def test_to_dict(self):
        p = Path(id="B", description="Hybrid", steps=[{"action": "s1"}])
        d = p.to_dict()
        assert d["id"] == "B"
        assert len(d["steps"]) == 1

    def test_from_dict(self):
        p = Path(id="C", label="Optimized", confidence=0.9)
        d = p.to_dict()
        p2 = Path.from_dict(d)
        assert p2.id == "C"
        assert p2.confidence == 0.9


class TestThinkingResult:
    def test_basic_result(self):
        result = ThinkingResult(
            paths=[Path(id="A"), Path(id="B"), Path(id="C")]
        )
        assert len(result.paths) == 3

    def test_get_path(self):
        result = ThinkingResult(paths=[
            Path(id="A", description="first"),
            Path(id="B", description="second"),
        ])
        assert result.get_path("A").description == "first"
        assert result.get_path("X") is None

    def test_to_dict(self):
        result = ThinkingResult(
            goal_text="test",
            benchmark_map=BenchmarkMap(domain="web"),
            paths=[Path(id="A")],
        )
        d = result.to_dict()
        assert "benchmark_map" in d
        assert "paths" in d

    def test_from_dict(self):
        result = ThinkingResult(goal_text="test", paths=[Path(id="A")])
        d = result.to_dict()
        r2 = ThinkingResult.from_dict(d)
        assert r2.goal_text == "test"


class TestScout:
    @pytest.fixture
    def scout(self):
        return Scout()

    async def test_scout_analyze(self, scout):
        goal = Goal(raw="Build a REST API", normalized="Build a REST API")
        result = await scout.analyze(goal, available_tools=["http"])
        assert isinstance(result, BenchmarkMap)
        assert result.domain


class TestPhysicist:
    @pytest.fixture
    def physicist(self):
        return Physicist()

    async def test_classify(self, physicist):
        bm = BenchmarkMap(
            domain="web",
            elements=[
                BenchmarkElement(name="api", tool="http"),
                BenchmarkElement(name="auth", tool="auth"),
            ],
            standard_tools=["http"],
        )
        result = await physicist.classify(bm, available_tools=["http"])
        assert isinstance(result, ClassifiedMap)
        assert isinstance(result.constraints, list)


class TestArchitect:
    @pytest.fixture
    def architect(self):
        return Architect()

    async def test_design(self, architect):
        cm = ClassifiedMap(
            benchmark=BenchmarkMap(domain="web"),
            constraints=[
                Constraint(element_name="rate_limit", constraint_type="physics"),
                Constraint(element_name="logging", constraint_type="habit"),
            ],
        )
        paths = await architect.design(cm, available_tools=["http"])
        assert len(paths) == 3
        ids = [p.id for p in paths]
        assert "A" in ids
        assert "B" in ids
        assert "C" in ids
