from types import SimpleNamespace

from src.gameplay.core.tasks.collectDeadCorpse import (
    ATTEMPT_LIMIT, CollectDeadCorpseTask,
)


def creature(x=32005, y=32005, z=7):
    return {'name': 'Rat', 'coordinate': (x, y, z)}


def ctx(coordinate=(32000, 32000, 7), corpses=None):
    return {
        'ng_radar': {'coordinate': coordinate},
        'gameWindow': {'coordinate': (100, 100, 960, 704)},
        'loot': {'corpsesToLoot': corpses or []},
    }


def test_far_corpse_does_not_click_or_remove(mocker):
    task = CollectDeadCorpseTask(creature(x=32020, y=32000))
    context = ctx(corpses=[task.creature])
    keyboard = mocker.patch(
        'src.gameplay.core.tasks.collectDeadCorpse.utilsKeyboard')
    slots = mocker.patch(
        'src.gameplay.core.tasks.collectDeadCorpse.gameWindowSlot')
    task.do(context)
    assert slots.rightClickSlot.call_count == 0
    assert keyboard.keyDown.call_count == 0
    task.onComplete(context)
    # Unerreichbar: bleibt in der Liste, zaehlt aber als Versuch.
    assert context['loot']['corpsesToLoot'] == [task.creature]
    assert task.creature['lootAttempts'] == 1


def test_reachable_corpse_clicks_own_slot_and_neighbors(mocker):
    task = CollectDeadCorpseTask(creature(x=32001, y=32001))
    context = ctx(corpses=[task.creature, creature(x=32010, y=32010)])
    mocker.patch('src.gameplay.core.tasks.collectDeadCorpse.utilsKeyboard')
    slots = mocker.patch(
        'src.gameplay.core.tasks.collectDeadCorpse.gameWindowSlot')
    task.do(context)
    clicked = [tuple(call.args[0]) for call in slots.rightClickSlot.call_args_list]
    # Leichen-Slot (8,6) plus bis zu 8 Nachbarn, nie das feste 3x3-Raster.
    assert (8, 6) in clicked
    assert len(clicked) == 9
    assert all(7 <= x <= 9 and 5 <= y <= 7 for x, y in clicked)
    task.onComplete(context)
    assert context['loot']['corpsesToLoot'] == [
        creature(x=32010, y=32010),
    ]


def test_attempt_limit_drops_unreachable_corpse(mocker):
    task = CollectDeadCorpseTask(creature(x=32020, y=32000))
    task.creature['lootAttempts'] = ATTEMPT_LIMIT - 1
    context = ctx(corpses=[task.creature])
    mocker.patch('src.gameplay.core.tasks.collectDeadCorpse.utilsKeyboard')
    mocker.patch('src.gameplay.core.tasks.collectDeadCorpse.gameWindowSlot')
    task.do(context)
    task.onComplete(context)
    assert context['loot']['corpsesToLoot'] == []


def test_window_edge_slots_are_clipped(mocker):
    task = CollectDeadCorpseTask(creature(x=31993, y=31995))
    context = ctx(corpses=[task.creature])
    mocker.patch('src.gameplay.core.tasks.collectDeadCorpse.utilsKeyboard')
    slots = mocker.patch(
        'src.gameplay.core.tasks.collectDeadCorpse.gameWindowSlot')
    task.do(context)
    clicked = [tuple(call.args[0]) for call in slots.rightClickSlot.call_args_list]
    assert all(0 <= x <= 14 and 0 <= y <= 10 for x, y in clicked)
    assert (0, 0) in clicked
