from src.gameplay.core.tasks.walkToCoordinate import WalkToCoordinateTask


context = {'ng_radar': {'coordinate': (1, 2, 3)}}
coordinate = (4, 5, 6)

def test_should_test_default_params():
    task = WalkToCoordinateTask(coordinate)
    assert task.name == 'walkToCoordinate'
    assert task.coordinate == coordinate
    assert task.blockedCoordinates == []


def test_block_walkpoint_forces_path_recalculation():
    task = WalkToCoordinateTask(coordinate)
    task.tasks = [object(), object()]
    task.currentTaskIndex = 1

    task.blockWalkpoint([4, 4, 6])

    assert task.blockedCoordinates == [[4, 4, 6]]
    assert task.tasks == []
    assert task.currentTaskIndex == 0


def test_calculate_walkpoint_avoids_failed_tile(mocker):
    generated = mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.generateFloorWalkpoints',
        side_effect=[[], [], []],
    )
    task = WalkToCoordinateTask([32371, 32215, 7])
    task.blockedCoordinates = [[32372, 32222, 7]]
    live_context = {
        'ng_radar': {'coordinate': [32372, 32223, 7]},
        'ng_cave': {
            'holesOrStairs': [],
            'waypoints': {'currentIndex': 16, 'items': []},
        },
        'gameWindow': {'monsters': [], 'players': []},
        'ng_battleList': {'creatures': []},
    }

    task.calculateWalkpoint(live_context)

    assert generated.call_args_list[0] == mocker.call(
        [32372, 32223, 7],
        [32371, 32215, 7],
        nonWalkableCoordinates=[[32372, 32222, 7]],
    )
    assert generated.call_args_list[1] == mocker.call(
        [32372, 32223, 7],
        [32371, 32215, 7],
        nonWalkableCoordinates=[],
    )
    assert task.blockedCoordinates == []


def test_trapped_walk_remembers_first_monster_blocking_route(mocker):
    blocker = {
        'name': 'Cave Rat',
        'coordinate': [32391, 32231, 8],
        'windowCoordinate': (400, 300),
    }
    generated = mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.generateFloorWalkpoints',
        side_effect=[[], [[32391, 32231, 8], [32392, 32231, 8]]],
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.getContent',
        return_value=object(),
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.hasSkull',
        return_value=False,
    )
    task = WalkToCoordinateTask([32392, 32231, 8])
    live_context = {
        'ng_radar': {'coordinate': [32390, 32231, 8]},
        'ng_cave': {
            'holesOrStairs': [],
            'waypoints': {'currentIndex': 0, 'items': []},
        },
        'ng_screenshot': object(),
        'gameWindow': {'monsters': [blocker], 'players': []},
        'ng_battleList': {'creatures': [('Cave Rat', False)]},
    }

    task.calculateWalkpoint(live_context)

    assert generated.call_count == 2
    assert task.isTrapped is True
    assert task.blockingCreature is blocker
    assert task.tasks[0].name == 'attackMonstersBox'


def test_trapped_walk_does_not_complete():
    task = WalkToCoordinateTask([32371, 32215, 7])
    live_context = {'ng_radar': {'coordinate': [32372, 32223, 7]}}

    assert task.did(live_context) is False


def test_non_exact_point_completes_inside_three_by_three_area():
    task = WalkToCoordinateTask([32385, 32215, 7], tolerance=1)
    live_context = {'ng_radar': {'coordinate': [32384, 32214, 7]}}

    assert task.did(live_context) is True


def test_non_exact_point_skips_pathfinding_when_already_reached(mocker):
    generated = mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.generateFloorWalkpoints',
    )
    task = WalkToCoordinateTask([32385, 32215, 7], tolerance=1)
    task.tasks = [object()]
    live_context = {'ng_radar': {'coordinate': [32384, 32215, 7]}}

    task.calculateWalkpoint(live_context)

    assert task.tasks == []
    assert task.isTrapped is False
    generated.assert_not_called()


def test_trapped_walk_attacks_unmarked_unknown_blocker(mocker):
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.generateFloorWalkpoints',
        return_value=[],
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.getContent',
        return_value=object(),
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.hasSkull',
        return_value=False,
    )
    task = WalkToCoordinateTask([32391, 32231, 8])
    live_context = {
        'ng_radar': {'coordinate': [32390, 32231, 8]},
        'ng_cave': {
            'holesOrStairs': [],
            'waypoints': {'currentIndex': 165, 'items': []},
        },
        'ng_screenshot': object(),
        'gameWindow': {'monsters': [], 'players': []},
        'ng_battleList': {'creatures': [('Unknown', False)]},
    }

    task.calculateWalkpoint(live_context)

    assert task.isTrapped is True
    assert task.tasks[0].name == 'attackMonstersBox'


def test_trapped_walk_does_not_attack_unknown_player(mocker):
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.generateFloorWalkpoints',
        return_value=[],
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.getContent',
        return_value=object(),
    )
    mocker.patch(
        'src.gameplay.core.tasks.walkToCoordinate.hasSkull',
        return_value=True,
    )
    task = WalkToCoordinateTask([32391, 32231, 8])
    live_context = {
        'ng_radar': {'coordinate': [32390, 32231, 8]},
        'ng_cave': {
            'holesOrStairs': [],
            'waypoints': {'currentIndex': 165, 'items': []},
        },
        'ng_screenshot': object(),
        'gameWindow': {'monsters': [], 'players': []},
        'ng_battleList': {'creatures': [('Unknown', False)]},
    }

    task.calculateWalkpoint(live_context)

    assert task.isTrapped is True
    assert task.tasks == []

def test_should_method_onBeforeStart_call_calculateWalkpoint(mocker):
    task = WalkToCoordinateTask(coordinate)
    calculateWalkpointSpy = mocker.patch.object(task, 'calculateWalkpoint')
    assert task.onBeforeStart(context) == context
    calculateWalkpointSpy.assert_called_once_with(context)

def test_should_method_onBeforeRestart_call_onBeforeStart(mocker):
    task = WalkToCoordinateTask(coordinate)
    onBeforeStartSpy = mocker.patch.object(task, 'onBeforeStart', return_value=context)
    assert task.onBeforeRestart(context) == context
    onBeforeStartSpy.assert_called_once_with(context)

def test_should_method_onComplete_call_releaseKeys(mocker):
    task = WalkToCoordinateTask(coordinate)
    releaseKeysSpy = mocker.patch('src.gameplay.utils.releaseKeys', return_value=context)
    assert task.onComplete(context) == context
    releaseKeysSpy.assert_called_once_with(context)

def test_should_call_onComplete(mocker):
    task = WalkToCoordinateTask(coordinate)
    releaseKeysSpy = mocker.patch('src.gameplay.utils.releaseKeys', return_value=context)
    assert task.onComplete(context) == context
    releaseKeysSpy.assert_called_once_with(context)

def test_should_method_onInterrupt_call_releaseKeys(mocker):
    task = WalkToCoordinateTask(coordinate)
    releaseKeysSpy = mocker.patch('src.gameplay.utils.releaseKeys', return_value=context)
    assert task.onInterrupt(context) == context
    releaseKeysSpy.assert_called_once_with(context)

def test_should_method_shouldRestartAfterAllChildrensComplete_return_True_when_there_are_no_tasks(mocker):
    task = WalkToCoordinateTask(coordinate)
    coordinatesAreEqualSpy = mocker.patch('src.gameplay.utils.coordinatesAreEqual')
    assert task.shouldRestartAfterAllChildrensComplete(context) == True
    coordinatesAreEqualSpy.assert_not_called()

def test_should_method_shouldRestartAfterAllChildrensComplete_return_True_when_coordinates_are_different(mocker):
    task = WalkToCoordinateTask(coordinate)
    task.tasks = [1]
    assert task.shouldRestartAfterAllChildrensComplete(context) == True

def test_should_method_shouldRestartAfterAllChildrensComplete_return_False_when_coordinates_are_equal(mocker):
    task = WalkToCoordinateTask(coordinate)
    task.tasks = [1]
    equal_context = {'ng_radar': {'coordinate': coordinate}}
    assert task.shouldRestartAfterAllChildrensComplete(equal_context) == False
