from src.gameplay.core.tasks.clickInClosestCreature import ClickInClosestCreatureTask


def test_should_test_default_params():
    task = ClickInClosestCreatureTask()
    assert task.name == 'clickInClosestCreature'
    assert task.delayOfTimeout == 5

def test_should_method_shouldIgnore_return_False_when_has_no_target_creature():
    context = {'ng_cave': {'targetCreature': None}}
    task = ClickInClosestCreatureTask()
    assert task.shouldIgnore(context) == False

def test_should_method_shouldIgnore_return_True_when_has_target_creature():
    context = {'ng_cave': {'targetCreature': ('Dragon', 'monster', True, (0, 0), (0, 0, 0), (0, 0), (0, 0), False)}}
    task = ClickInClosestCreatureTask()
    assert task.shouldIgnore(context) == True


def test_should_not_ignore_route_blocker_when_target_is_already_selected():
    context = {'ng_cave': {'targetCreature': {'coordinate': [3, 0, 7]}}}
    task = ClickInClosestCreatureTask()
    trappedWalk = type('WalkTask', (), {'isTrapped': True})()
    attackBox = type('AttackBox', (), {
        'name': 'attackMonstersBox',
        'parentTask': trappedWalk,
    })()
    task.parentTask = type('AttackClosest', (), {'parentTask': attackBox})()

    assert task.shouldIgnore(context) is False

def test_should_method_did_return_False_when_is_not_attacking_some_creature():
    context = {'ng_cave': {'isAttackingSomeCreature': False}}
    task = ClickInClosestCreatureTask()
    assert task.did(context) == False

def test_should_method_did_return_True_when_is_attacking_some_creature():
    context = {'ng_cave': {'isAttackingSomeCreature': True}}
    task = ClickInClosestCreatureTask()
    assert task.did(context) == True

def test_should_do_and_ignore_hotkey_attack_when_there_are_players(mocker):
    context = {
        'ng_cave': {
            'closestCreature': {'windowCoordinate': (0, 0)},
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {
            'players': [('Bubble', 'player', True, (0, 0), (0, 0, 0), (0, 0), (0, 0), False)],
            'monsters': [],
        },
        'ng_targeting': {'hasIgnorableCreatures': False},
    }
    pressSpy = mocker.patch('src.utils.keyboard.press')
    attackClickSpy = mocker.patch('src.utils.mouse.attackClick')
    task = ClickInClosestCreatureTask()
    assert task.do(context) == context
    attackClickSpy.assert_called_once_with(context['ng_cave']['closestCreature']['windowCoordinate'])
    pressSpy.assert_not_called()

def test_should_do_and_ignore_hotkey_attack_when_there_ignorable_creatures(mocker):
    context = {
        'ng_cave': {
            'closestCreature': {'windowCoordinate': (0, 0)},
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {'players': [], 'monsters': []},
        'ng_targeting': {'hasIgnorableCreatures': True}
    }
    pressSpy = mocker.patch('src.utils.keyboard.press')
    attackClickSpy = mocker.patch('src.utils.mouse.attackClick')
    task = ClickInClosestCreatureTask()
    assert task.do(context) == context
    attackClickSpy.assert_called_once_with(context['ng_cave']['closestCreature']['windowCoordinate'])
    pressSpy.assert_not_called()

def test_should_do_and_click_precisely_even_without_players_or_ignorable_creatures(mocker):
    context = {
        'ng_cave': {
            'closestCreature': {'windowCoordinate': (0, 0)},
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {'players': [], 'monsters': []},
        'ng_targeting': {'hasIgnorableCreatures': False}
    }
    pressSpy = mocker.patch('src.utils.keyboard.press')
    attackClickSpy = mocker.patch('src.utils.mouse.attackClick')
    task = ClickInClosestCreatureTask()
    assert task.do(context) == context
    attackClickSpy.assert_called_once_with((0, 0))
    pressSpy.assert_not_called()


def test_should_do_and_press_space_only_without_window_coordinate(mocker):
    context = {
        'ng_cave': {
            'closestCreature': {'windowCoordinate': None},
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {'players': [], 'monsters': []},
        'ng_targeting': {'hasIgnorableCreatures': False}
    }
    pressSpy = mocker.patch('src.utils.keyboard.press')
    attackClickSpy = mocker.patch('src.utils.mouse.attackClick')
    task = ClickInClosestCreatureTask()
    assert task.do(context) == context
    attackClickSpy.assert_not_called()
    pressSpy.assert_called_once_with('space')


def test_should_attack_ignored_creature_when_it_blocks_the_path(mocker):
    blocker = {
        'windowCoordinate': (120, 240),
        'gameWindowCoordinate': (608, 352),
        'coordinate': [32402, 32230, 8],
    }
    context = {
        'ng_cave': {
            'closestCreature': None,
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {
            'players': [],
            'monsters': [blocker],
            'image': type('Image', (), {'shape': (704, 960)})(),
        },
        'ng_targeting': {'hasIgnorableCreatures': True},
        'ng_radar': {'coordinate': [32400, 32230, 8]},
    }
    attack = mocker.patch('src.utils.mouse.attackClick')
    press = mocker.patch('src.utils.keyboard.press')
    task = ClickInClosestCreatureTask()
    trappedWalk = type('WalkTask', (), {'isTrapped': True})()
    attackBox = type('AttackBox', (), {
        'name': 'attackMonstersBox',
        'parentTask': trappedWalk,
    })()
    task.parentTask = type('AttackClosest', (), {'parentTask': attackBox})()

    assert task.do(context) == context
    attack.assert_called_once_with(blocker['windowCoordinate'])
    press.assert_not_called()


def test_should_not_attack_ignored_creature_on_normal_waypoint(mocker):
    blocker = {'windowCoordinate': (120, 240)}
    context = {
        'ng_cave': {
            'closestCreature': None,
            'isAttackingSomeCreature': False,
        },
        'gameWindow': {'players': [], 'monsters': [blocker]},
        'ng_targeting': {'hasIgnorableCreatures': True},
    }
    attack = mocker.patch('src.utils.mouse.attackClick')
    task = ClickInClosestCreatureTask()
    normalWalk = type('WalkTask', (), {'isTrapped': False})()
    attackBox = type('AttackBox', (), {
        'name': 'attackMonstersBox',
        'parentTask': normalWalk,
    })()
    task.parentTask = type('AttackClosest', (), {'parentTask': attackBox})()

    assert task.do(context) == context
    attack.assert_not_called()


def test_should_switch_to_route_blocker_while_already_attacking(mocker):
    fleeing = {
        'windowCoordinate': (80, 120),
        'gameWindowCoordinate': (700, 352),
        'coordinate': [32404, 32230, 8],
    }
    blocker = {
        'windowCoordinate': (120, 240),
        'gameWindowCoordinate': (608, 352),
        'coordinate': [32402, 32230, 8],
    }
    context = {
        'ng_cave': {
            'closestCreature': fleeing,
            'isAttackingSomeCreature': True,
        },
        'gameWindow': {
            'players': [],
            'monsters': [fleeing, blocker],
            'image': type('Image', (), {'shape': (704, 960)})(),
        },
        'ng_targeting': {'hasIgnorableCreatures': True},
    }
    attack = mocker.patch('src.utils.mouse.attackClick')
    press = mocker.patch('src.utils.keyboard.press')
    task = ClickInClosestCreatureTask()
    trappedWalk = type('WalkTask', (), {
        'isTrapped': True,
        'blockingCreature': blocker,
    })()
    attackBox = type('AttackBox', (), {
        'name': 'attackMonstersBox',
        'parentTask': trappedWalk,
    })()
    task.parentTask = type('AttackClosest', (), {'parentTask': attackBox})()

    assert task.do(context) == context
    attack.assert_called_once_with(blocker['windowCoordinate'])
    press.assert_not_called()
