from src.gameplay import relogin as reloginModule
from src.gameplay.relogin import watchRelogin


def _context(coordinate=(100, 100, 7), sessionState=None, paused=False):
    session = {'enabled': False}
    if sessionState:
        session['state'] = sessionState
    return {
        'ng_pause': paused,
        'ng_radar': {'coordinate': list(coordinate) if coordinate else None},
        'ng_cave': {'waypoints': {
            'items': [
                {'label': 'Start', 'coordinate': [100, 100, 7]},
                {'label': 'Hunt', 'coordinate': [120, 100, 7]},
                {'label': 'Depot', 'coordinate': [90, 90, 7]},
            ],
            'currentIndex': 1,
            'state': None,
        }},
        'ng_session': session,
    }


def _advance(mocker, context, seconds):
    mocker.patch.object(reloginModule, 'monotonic', return_value=seconds)
    return watchRelogin(context)


def test_coordinate_present_is_noop(mocker):
    pressSpy = mocker.patch.object(reloginModule, 'press')
    _advance(mocker, _context(), 100)

    pressSpy.assert_not_called()


def test_relogin_after_thirty_seconds(mocker):
    pressSpy = mocker.patch.object(reloginModule, 'press')
    logSpy = mocker.patch.object(reloginModule, 'logEvent')
    context = _context(coordinate=None)

    _advance(mocker, context, 0)
    _advance(mocker, context, 20)
    pressSpy.assert_not_called()

    _advance(mocker, context, 31)

    pressSpy.assert_called_once_with('enter')
    assert context['ng_relogin']['attempts'] == 1
    assert context['ng_relogin']['waiting'] is True
    assert logSpy.call_args[0][0] == 'reloginAttempt'


def test_gives_up_after_max_attempts(mocker):
    pressSpy = mocker.patch.object(reloginModule, 'press')
    logSpy = mocker.patch.object(reloginModule, 'logEvent')
    context = _context(coordinate=None)

    _advance(mocker, context, 0)
    for attempt in range(1, 8):
        _advance(mocker, context, 31 + attempt * 20)

    # Bewusst wenige Versuche: Tibia sperrt die IP-Adresse fuer 30 Minuten,
    # wenn sich mehrfach hintereinander nicht anmelden laesst. Lieber
    # anhalten und melden als sich selbst aussperren.
    assert pressSpy.call_count == reloginModule.RELOGIN_MAX_ATTEMPTS
    assert context['ng_pause'] is True
    assert logSpy.call_args[0][0] == 'reloginFailed'


def test_success_resets_route_to_start(mocker):
    mocker.patch.object(reloginModule, 'press')
    logSpy = mocker.patch.object(reloginModule, 'logEvent')
    context = _context(coordinate=None)

    _advance(mocker, context, 0)
    _advance(mocker, context, 31)
    context['ng_radar']['coordinate'] = [100, 100, 7]
    _advance(mocker, context, 40)

    assert context['ng_cave']['waypoints']['currentIndex'] == 0
    assert context['ng_relogin'] == {}
    assert logSpy.call_args[0][0] == 'reloginSuccess'


def test_session_offline_is_not_touched(mocker):
    pressSpy = mocker.patch.object(reloginModule, 'press')
    context = _context(coordinate=None, sessionState='offline')

    _advance(mocker, context, 0)
    _advance(mocker, context, 500)

    pressSpy.assert_not_called()


def test_paused_is_not_touched(mocker):
    pressSpy = mocker.patch.object(reloginModule, 'press')
    context = _context(coordinate=None, paused=True)

    _advance(mocker, context, 0)
    _advance(mocker, context, 500)

    pressSpy.assert_not_called()
