"""Mock world adapter — returns canned perception and logs commands.

Useful for testing the heartbeat loop and WorldInterface contract
without a running game server.
"""

from __future__ import annotations

import logging
from dataclasses import dataclass
from datetime import UTC, datetime

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

logger = logging.getLogger(__name__)


@dataclass
class _ActionLog:
    """Record of an action dispatched to the mock world."""

    command: CommandInput
    timestamp: datetime


class MockWorldAdapter(WorldInterface):
    """In-memory mock adapter for testing.

    * ``observe()`` returns configurable canned perception.
    * ``act()`` logs the command and returns success.
    * ``speak()`` logs the message.

    Inspect ``action_log`` and ``speech_log`` to verify behaviour in tests.
    """

    def __init__(
        self,
        *,
        location: str = "Test Chamber",
        entities: list[str] | None = None,
        events: list[str] | None = None,
    ) -> None:
        self._location = location
        self._entities = entities or ["TestNPC"]
        self._events = events or []
        self._connected = False
        self.action_log: list[_ActionLog] = []
        self.speech_log: list[dict] = []

    # -- lifecycle ---------------------------------------------------------

    def connect(self) -> None:
        self._connected = True
        logger.info("MockWorldAdapter connected")

    def disconnect(self) -> None:
        self._connected = False
        logger.info("MockWorldAdapter disconnected")

    @property
    def is_connected(self) -> bool:
        return self._connected

    # -- core contract -----------------------------------------------------

    def observe(self) -> PerceptionOutput:
        logger.debug("MockWorldAdapter.observe()")
        return PerceptionOutput(
            timestamp=datetime.now(UTC),
            location=self._location,
            entities=list(self._entities),
            events=list(self._events),
            raw={"adapter": "mock"},
        )

    def act(self, command: CommandInput) -> ActionResult:
        logger.debug("MockWorldAdapter.act(%s)", command.action)
        self.action_log.append(_ActionLog(command=command, timestamp=datetime.now(UTC)))
        return ActionResult(
            status=ActionStatus.SUCCESS,
            message=f"Mock executed: {command.action}",
            data={"adapter": "mock"},
        )

    def speak(self, message: str, target: str | None = None) -> None:
        logger.debug("MockWorldAdapter.speak(%r, target=%r)", message, target)
        self.speech_log.append(
            {
                "message": message,
                "target": target,
                "timestamp": datetime.now(UTC).isoformat(),
            }
        )
