from src.gameplay import sessionRhythm as rhythmModule
from src.gameplay.sessionRhythm import sessionRhythm


def _context(coordinate=(100, 100, 7), enabled=True, state=None):
    session = {
        'enabled': enabled, 'huntMinutes': 240, 'offlineMinutes': 30,
    }
    if state:
        session['state'] = state
    return {
        'ng_pause': False,
        '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 test_disabled_does_nothing(mocker):
    context = _context(enabled=False)
    assert sessionRhythm(context)['ng_session']['enabled'] is False


def test_hunt_time_up_goes_to_safe_label(mocker):
    logSpy = mocker.patch.object(rhythmModule, 'logEvent')
    mocker.patch.object(rhythmModule, 'monotonic', return_value=0)
    context = _context()
    sessionRhythm(context)
    mocker.patch.object(
        rhythmModule, 'monotonic', return_value=240 * 60 + 40 * 60)

    sessionRhythm(context)

    waypoints = context['ng_cave']['waypoints']
    assert waypoints['currentIndex'] == 2  # Depot-Label
    assert context['ng_session']['state'] == 'goSafe'
    assert logSpy.call_args[0][0] == 'sessionGoSafe'


def test_arrival_at_safe_zone_logs_out(mocker):
    hotkeySpy = mocker.patch.object(rhythmModule, 'hotkey')
    mocker.patch.object(rhythmModule, 'press')
    mocker.patch.object(rhythmModule, 'monotonic', return_value=0)
    context = _context(coordinate=(90, 91, 7), state='goSafe')
    context['ng_session']['safeIndex'] = 2
    context['ng_session']['safeDeadline'] = 600

    sessionRhythm(context)

    hotkeySpy.assert_called_once_with('ctrl', 'q')
    assert context['ng_session']['state'] == 'logout'


def test_safe_timeout_pauses_instead_of_field_logout(mocker):
    hotkeySpy = mocker.patch.object(rhythmModule, 'hotkey')
    mocker.patch.object(rhythmModule, 'monotonic', return_value=601)
    context = _context(coordinate=(120, 100, 7), state='goSafe')
    context['ng_session']['safeIndex'] = 2
    context['ng_session']['safeDeadline'] = 600

    sessionRhythm(context)

    hotkeySpy.assert_not_called()
    assert context['ng_session']['state'] == 'paused'
    assert context['ng_pause'] is True


def test_full_cycle_back_to_hunting(mocker):
    mocker.patch.object(rhythmModule, 'press')
    mocker.patch.object(rhythmModule, 'hotkey')
    context = _context(coordinate=None, state='logout')
    context['ng_session']['cycle'] = (100, 100)
    context['ng_session']['logoutDeadline'] = 9999
    context['ng_session']['logoutAttempts'] = 1
    mocker.patch.object(rhythmModule, 'monotonic', return_value=0)

    sessionRhythm(context)
    assert context['ng_session']['state'] == 'offline'
    assert context['ng_pause'] is True

    mocker.patch.object(rhythmModule, 'monotonic', return_value=101)
    sessionRhythm(context)
    assert context['ng_session']['state'] == 'relogin'

    context['ng_radar']['coordinate'] = [100, 100, 7]
    mocker.patch.object(rhythmModule, 'monotonic', return_value=130)
    sessionRhythm(context)
    session = context['ng_session']
    assert session['state'] == 'hunting'
    assert context['ng_pause'] is False
    assert context['ng_cave']['waypoints']['currentIndex'] == 0  # Start


def test_cycle_durations_have_jitter():
    for _ in range(50):
        hunt, offline = rhythmModule._cycleDurations(
            {'huntMinutes': 240, 'offlineMinutes': 30})
        assert 210 * 60 <= hunt <= 270 * 60
        assert 20 * 60 <= offline <= 40 * 60
