"""Tests for TOML verb support in thing templates."""

import textwrap
from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.player import Player
from mudlib.things import (
    Container,
    ThingTemplate,
    load_thing_template,
    spawn_thing,
)
from mudlib.zone import Zone


# Test handler functions
async def _test_verb_handler(obj, player, args):
    """Test verb handler that sends a message."""
    await player.send(f"verb on {obj.name} with args={args}\r\n")


async def _another_test_handler(obj, player, args):
    """Another test handler."""
    await player.send(f"another verb on {obj.name}\r\n")


@pytest.fixture
def mock_writer():
    writer = MagicMock()
    writer.write = MagicMock()
    writer.drain = AsyncMock()
    return writer


@pytest.fixture
def mock_reader():
    return MagicMock()


@pytest.fixture
def test_zone():
    terrain = [["." for _ in range(10)] for _ in range(10)]
    return Zone(name="testzone", width=10, height=10, terrain=terrain)


@pytest.fixture
def player(mock_reader, mock_writer, test_zone):
    return Player(
        name="TestPlayer",
        x=5,
        y=5,
        reader=mock_reader,
        writer=mock_writer,
        location=test_zone,
    )


def test_thing_template_default_empty_verbs():
    """ThingTemplate has empty verbs dict by default."""
    template = ThingTemplate(name="test", description="a test")
    assert template.verbs == {}


def test_load_template_with_verbs(tmp_path):
    """load_thing_template parses [verbs] section from TOML."""
    toml = tmp_path / "key.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "key"
        description = "a brass key"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
    """)
    )
    template = load_thing_template(toml)
    assert template.verbs == {"unlock": "tests.test_toml_verbs:_test_verb_handler"}


def test_load_template_without_verbs_section(tmp_path):
    """load_thing_template works without [verbs] section (backwards compatible)."""
    toml = tmp_path / "rock.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "rock"
        description = "a smooth stone"
        portable = true
    """)
    )
    template = load_thing_template(toml)
    assert template.verbs == {}


def test_load_template_with_multiple_verbs(tmp_path):
    """load_thing_template handles multiple verbs."""
    toml = tmp_path / "multi.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "door"
        description = "a wooden door"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
        open = "tests.test_toml_verbs:_another_test_handler"
    """)
    )
    template = load_thing_template(toml)
    assert len(template.verbs) == 2
    assert "unlock" in template.verbs
    assert "open" in template.verbs


def test_spawn_thing_registers_verbs(test_zone, tmp_path):
    """spawn_thing registers verbs on spawned instance."""
    toml = tmp_path / "key.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "key"
        description = "a brass key"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
    """)
    )
    template = load_thing_template(toml)
    thing = spawn_thing(template, test_zone, x=5, y=5)

    assert thing.has_verb("unlock")


def test_spawn_container_registers_verbs(test_zone, tmp_path):
    """spawn_thing registers verbs on Container instances too."""
    toml = tmp_path / "chest.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "chest"
        description = "a wooden chest"
        capacity = 10
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
    """)
    )
    template = load_thing_template(toml)
    container = spawn_thing(template, test_zone, x=5, y=5)

    assert isinstance(container, Container)
    assert container.has_verb("unlock")


@pytest.mark.asyncio
async def test_spawned_verb_handler_receives_correct_args(test_zone, player, tmp_path):
    """Spawned thing's verb handler can be called and receives (player, args)."""
    toml = tmp_path / "key.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "key"
        description = "a brass key"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
    """)
    )
    template = load_thing_template(toml)
    thing = spawn_thing(template, test_zone, x=5, y=5)

    # Call the verb handler
    handler = thing.get_verb("unlock")
    assert handler is not None
    await handler(player, "door")

    # Check that the message was sent
    player.writer.write.assert_called()
    call_args = player.writer.write.call_args[0][0]
    assert "verb on key with args=door" in call_args


@pytest.mark.asyncio
async def test_verb_handler_receives_correct_object_instance(
    test_zone, player, tmp_path
):
    """Handler receives the correct object instance as first arg (via partial)."""
    toml = tmp_path / "key.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "brass key"
        description = "a brass key"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
    """)
    )
    template = load_thing_template(toml)
    thing = spawn_thing(template, test_zone, x=5, y=5)

    # Call the verb handler
    handler = thing.get_verb("unlock")
    assert handler is not None
    await handler(player, "chest")

    # Check that the object name appears in the message (verifying correct object)
    player.writer.write.assert_called()
    call_args = player.writer.write.call_args[0][0]
    assert "verb on brass key with args=chest" in call_args


@pytest.mark.asyncio
async def test_multiple_verbs_all_registered(test_zone, player, tmp_path):
    """Multiple verbs in TOML all get registered."""
    toml = tmp_path / "door.toml"
    toml.write_text(
        textwrap.dedent("""\
        name = "door"
        description = "a wooden door"
        [verbs]
        unlock = "tests.test_toml_verbs:_test_verb_handler"
        open = "tests.test_toml_verbs:_another_test_handler"
    """)
    )
    template = load_thing_template(toml)
    thing = spawn_thing(template, test_zone, x=5, y=5)

    # Both verbs should be registered
    assert thing.has_verb("unlock")
    assert thing.has_verb("open")

    # Both should work
    unlock_handler = thing.get_verb("unlock")
    assert unlock_handler is not None
    await unlock_handler(player, "test1")
    player.writer.write.assert_called()

    open_handler = thing.get_verb("open")
    assert open_handler is not None
    await open_handler(player, "test2")
    call_args = player.writer.write.call_args[0][0]
    assert "another verb on door" in call_args


def test_bad_handler_ref_skipped(test_zone):
    """A TOML verb with bad handler ref is skipped, not a crash."""
    template = ThingTemplate(
        name="bad",
        description="oops",
        portable=True,
        verbs={"zap": "nonexistent.module:fake_handler"},
    )
    thing = spawn_thing(template, test_zone, x=5, y=5)
    assert not thing.has_verb("zap")
