import pytest

from src.gameplay.core.tasks.moveDown import MoveDown
from src.gameplay.core.tasks.moveUp import MoveUp


MODUL = 'src.gameplay.core.tasks.common.floorChange'


@pytest.fixture(autouse=True)
def ohneKarte(mocker):
    """Standardmaessig sagt die Karte nichts - so wie bisher."""
    return mocker.patch(f'{MODUL}.getFloorChangeDirection', return_value=None)


@pytest.mark.parametrize(
    ('task_class', 'start_floor', 'target_floor', 'direction', 'key'),
    [
        (MoveDown, 7, 8, 'south', 'down'),
        (MoveUp, 8, 7, 'north', 'up'),
    ],
)
def test_transition_retries_until_floor_changes(
        mocker, task_class, start_floor, target_floor, direction, key):
    clock = mocker.patch(f'{MODUL}.time', return_value=100.0)
    press = mocker.patch(f'{MODUL}.press')
    context = {'ng_radar': {'coordinate': [100, 200, start_floor]}}
    task = task_class(context, direction)

    task.do(context)

    press.assert_called_once_with(key)
    assert task.did(context) is False
    clock.return_value = 100.4
    assert task.shouldRestart(context) is True

    context['ng_radar']['coordinate'] = [100, 200, target_floor]
    assert task.did(context) is True
    assert task.shouldRestart(context) is False


def test_falsche_richtung_fuehrt_zurueck_und_zur_naechsten(mocker, ohneKarte):
    """Der Fall aus dem Depot von Thais.

    Der Wegpunkt sagte 'north', die Treppe lag im Westen. Frueher lief der
    Bot nach Norden bis zur Wand und drueckte dort endlos weiter.
    """
    mocker.patch(f'{MODUL}.time', return_value=100.0)
    ohneKarte.return_value = 'west'
    press = mocker.patch(f'{MODUL}.press')
    context = {'ng_radar': {'coordinate': [32349, 32231, 8]}}
    task = MoveUp(context, 'north')

    task.do(context)
    assert press.call_args.args == ('up',)

    # Ein Feld nach Norden gelaufen, die Etage ist dieselbe geblieben.
    context['ng_radar']['coordinate'] = [32349, 32230, 8]
    task.do(context)
    assert press.call_args.args == ('down',), 'muss aufs Startfeld zurueck'

    context['ng_radar']['coordinate'] = [32349, 32231, 8]
    task.do(context)
    assert press.call_args.args == ('left',), 'danach die Richtung aus der Karte'


def test_rueckweg_ueber_zwei_felder(mocker, ohneKarte):
    """Auch mehrere Felder daneben findet er zurueck - und zaehlt nur einmal weiter."""
    mocker.patch(f'{MODUL}.time', return_value=100.0)
    press = mocker.patch(f'{MODUL}.press')
    context = {'ng_radar': {'coordinate': [32349, 32231, 8]}}
    task = MoveUp(context, 'north')

    context['ng_radar']['coordinate'] = [32349, 32229, 8]
    task.do(context)
    assert press.call_args.args == ('down',)
    context['ng_radar']['coordinate'] = [32349, 32230, 8]
    task.do(context)
    assert press.call_args.args == ('down',)

    assert task.currentDirection() == 'south', 'genau eine Richtung weiter'


def test_karte_kommt_direkt_nach_der_eingestellten_richtung(mocker, ohneKarte):
    ohneKarte.return_value = 'east'
    mocker.patch(f'{MODUL}.time', return_value=100.0)
    mocker.patch(f'{MODUL}.press')
    context = {'ng_radar': {'coordinate': [100, 200, 8]}}

    task = MoveUp(context, 'north')

    assert task.directions[:2] == ['north', 'east']
    assert sorted(task.directions) == ['east', 'north', 'south', 'west']


def test_ohne_position_wird_einfach_gedrueckt(mocker, ohneKarte):
    """Ohne Ortung nicht raten, wo man steht - nur weiter probieren."""
    mocker.patch(f'{MODUL}.time', return_value=100.0)
    press = mocker.patch(f'{MODUL}.press')
    context = {'ng_radar': {'coordinate': [100, 200, 8]}}
    task = MoveUp(context, 'west')

    context['ng_radar']['coordinate'] = None
    task.do(context)

    assert press.call_args.args == ('left',)
    assert task.did(context) is False
