from time import time

from src.gameplay import supplies as suppliesModule
from src.gameplay.supplies import watchSupplies


def _context(rules, enabled=True):
    return {
        'ng_screenshot': [],
        'ng_supplies': {'enabled': enabled, 'rules': rules, 'lastCheckAt': 0},
        'ng_actionBar': {'slots': {'266': 3}},
        'ng_bbot': {'itemSlots': {}},
        'ng_cave': {'waypoints': {
            'items': [{'label': 'Hunt'}, {'label': 'Refill'}],
            'currentIndex': 0,
            'state': None,
        }},
    }


def _itemRule(**overrides):
    rule = {
        'source': 'item', 'itemId': 266, 'operator': '<', 'target': 25,
        'label': 'Refill', 'enabled': True,
    }
    rule.update(overrides)
    return rule


def test_disabled_watcher_does_nothing(mocker):
    countSpy = mocker.patch.object(suppliesModule, 'getSlotCount')

    watchSupplies(_context([_itemRule()], enabled=False))

    countSpy.assert_not_called()


def test_low_stock_jumps_to_label_and_disarms(mocker):
    mocker.patch.object(suppliesModule, 'getSlotCount', return_value=10)
    logSpy = mocker.patch.object(suppliesModule, 'logEvent')
    rule = _itemRule()
    context = _context([rule])

    watchSupplies(context)

    waypoints = context['ng_cave']['waypoints']
    assert waypoints['currentIndex'] == 1
    assert waypoints['state'] is None
    assert rule['armed'] is False
    logSpy.assert_called_once()
    assert logSpy.call_args[0][0] == 'suppliesRefill'


def test_disarmed_rule_does_not_jump_again(mocker):
    mocker.patch.object(suppliesModule, 'getSlotCount', return_value=10)
    rule = _itemRule()
    context = _context([rule])
    watchSupplies(context)
    context['ng_cave']['waypoints']['currentIndex'] = 0
    context['ng_supplies']['lastCheckAt'] = 0

    watchSupplies(context)

    assert context['ng_cave']['waypoints']['currentIndex'] == 0


def test_rule_rearms_when_stock_recovers(mocker):
    countMock = mocker.patch.object(
        suppliesModule, 'getSlotCount', return_value=10)
    rule = _itemRule()
    context = _context([rule])
    watchSupplies(context)
    assert rule['armed'] is False

    countMock.return_value = 30
    context['ng_supplies']['lastCheckAt'] = 0
    watchSupplies(context)

    assert rule['armed'] is True


def test_missing_label_logs_once_and_stays_armed(mocker):
    mocker.patch.object(suppliesModule, 'getSlotCount', return_value=10)
    logSpy = mocker.patch.object(suppliesModule, 'logEvent')
    rule = _itemRule(label='GibtsNicht')
    context = _context([rule])

    watchSupplies(context)
    context['ng_supplies']['lastCheckAt'] = 0
    watchSupplies(context)

    assert context['ng_cave']['waypoints']['currentIndex'] == 0
    assert rule.get('armed', True) is True
    assert logSpy.call_count == 1
    assert logSpy.call_args[0][0] == 'suppliesLabelMissing'


def test_throttles_checks(mocker):
    countSpy = mocker.patch.object(suppliesModule, 'getSlotCount')
    context = _context([_itemRule()])
    context['ng_supplies']['lastCheckAt'] = time()

    watchSupplies(context)

    countSpy.assert_not_called()


def test_bbot_item_slots_are_used_as_fallback(mocker):
    countSpy = mocker.patch.object(
        suppliesModule, 'getSlotCount', return_value=10)
    context = _context([_itemRule()])
    context['ng_actionBar'] = {'slots': {}}
    context['ng_bbot'] = {'itemSlots': {'266': 5}}

    watchSupplies(context)

    countSpy.assert_called_once_with([], 5)


def test_emergency_after_refill_timeout(mocker):
    mocker.patch.object(suppliesModule, 'getSlotCount', return_value=10)
    logSpy = mocker.patch.object(suppliesModule, 'logEvent')
    rule = _itemRule()
    context = _context([rule])
    context['ng_cave']['waypoints']['items'].append({'label': 'Depot'})
    timeMock = mocker.patch.object(suppliesModule, 'time')

    timeMock.return_value = 1000
    watchSupplies(context)
    assert context['ng_cave']['waypoints']['currentIndex'] == 1  # Refill
    assert rule['armed'] is False

    timeMock.return_value = 1000 + 481
    context['ng_supplies']['lastCheckAt'] = 0
    watchSupplies(context)

    assert context['ng_cave']['waypoints']['currentIndex'] == 2  # Depot
    assert context['ng_pause'] is True
    assert logSpy.call_args[0][0] == 'suppliesEmergency'


def test_recovery_clears_trigger_state(mocker):
    countMock = mocker.patch.object(
        suppliesModule, 'getSlotCount', return_value=10)
    rule = _itemRule()
    context = _context([rule])
    timeMock = mocker.patch.object(suppliesModule, 'time')
    timeMock.return_value = 1000

    watchSupplies(context)
    assert rule.get('triggeredAt') == 1000

    countMock.return_value = 50
    context['ng_supplies']['lastCheckAt'] = 0
    watchSupplies(context)

    assert rule['armed'] is True
    assert 'triggeredAt' not in rule
