"""Tests for the WorldInterface contract and type system."""

import pytest

from infrastructure.world.interface import WorldInterface
from infrastructure.world.types import (
    ActionResult,
    ActionStatus,
    CommandInput,
    PerceptionOutput,
)

# ---------------------------------------------------------------------------
# Type construction
# ---------------------------------------------------------------------------


class TestPerceptionOutput:
    def test_defaults(self):
        p = PerceptionOutput()
        assert p.location == ""
        assert p.entities == []
        assert p.events == []
        assert p.raw == {}
        assert p.timestamp is not None

    def test_custom_values(self):
        p = PerceptionOutput(
            location="Balmora",
            entities=["Guard", "Merchant"],
            events=["door_opened"],
        )
        assert p.location == "Balmora"
        assert len(p.entities) == 2
        assert "door_opened" in p.events


class TestCommandInput:
    def test_minimal(self):
        c = CommandInput(action="move")
        assert c.action == "move"
        assert c.target is None
        assert c.parameters == {}

    def test_with_target_and_params(self):
        c = CommandInput(action="attack", target="Rat", parameters={"weapon": "sword"})
        assert c.target == "Rat"
        assert c.parameters["weapon"] == "sword"


class TestActionResult:
    def test_defaults(self):
        r = ActionResult()
        assert r.status == ActionStatus.SUCCESS
        assert r.message == ""

    def test_failure(self):
        r = ActionResult(status=ActionStatus.FAILURE, message="blocked")
        assert r.status == ActionStatus.FAILURE


class TestActionStatus:
    def test_values(self):
        assert ActionStatus.SUCCESS.value == "success"
        assert ActionStatus.FAILURE.value == "failure"
        assert ActionStatus.PENDING.value == "pending"
        assert ActionStatus.NOOP.value == "noop"


# ---------------------------------------------------------------------------
# Abstract contract
# ---------------------------------------------------------------------------


class TestWorldInterfaceContract:
    """Verify the ABC cannot be instantiated directly."""

    def test_cannot_instantiate(self):
        with pytest.raises(TypeError):
            WorldInterface()

    def test_subclass_must_implement_observe(self):
        class Incomplete(WorldInterface):
            def act(self, command):
                pass

            def speak(self, message, target=None):
                pass

        with pytest.raises(TypeError):
            Incomplete()

    def test_subclass_must_implement_act(self):
        class Incomplete(WorldInterface):
            def observe(self):
                return PerceptionOutput()

            def speak(self, message, target=None):
                pass

        with pytest.raises(TypeError):
            Incomplete()

    def test_subclass_must_implement_speak(self):
        class Incomplete(WorldInterface):
            def observe(self):
                return PerceptionOutput()

            def act(self, command):
                return ActionResult()

        with pytest.raises(TypeError):
            Incomplete()

    def test_complete_subclass_instantiates(self):
        class Complete(WorldInterface):
            def observe(self):
                return PerceptionOutput()

            def act(self, command):
                return ActionResult()

            def speak(self, message, target=None):
                pass

        adapter = Complete()
        assert adapter.is_connected is True  # default
        assert isinstance(adapter.observe(), PerceptionOutput)
        assert isinstance(adapter.act(CommandInput(action="test")), ActionResult)
