import numpy as np

from src.gameplay import chatReply as chatReplyModule
from src.gameplay.chatReply import watchChatReply


class _Orchestrator:
    def __init__(self):
        self.resetCount = 0

    def reset(self):
        self.resetCount += 1


def _context():
    return {
        'ng_pause': False,
        'ng_screenshot': np.zeros((200, 400), dtype=np.uint8),
        'ng_tasksOrchestrator': _Orchestrator(),
    }


def _mock_chat_area(mocker, box=(10, 100, 200, 50)):
    mocker.patch.object(
        chatReplyModule, 'getChatMessagesContainerPosition',
        return_value=box)


def test_first_scan_only_seeds(mocker, monkeypatch):
    monkeypatch.delenv(chatReplyModule.CHATREPLY_ENV, raising=False)
    _mock_chat_area(mocker)
    llmSpy = mocker.patch.object(chatReplyModule, '_askLlm')
    context = _context()

    watchChatReply(context)

    llmSpy.assert_not_called()
    assert context['ng_chatReply']['lastHash'] is not None


def test_new_line_triggers_llm_and_reply(mocker, monkeypatch):
    monkeypatch.delenv(chatReplyModule.CHATREPLY_ENV, raising=False)
    _mock_chat_area(mocker)
    mocker.patch.object(
        chatReplyModule, '_llmEndpoint',
        return_value=('http://localhost:11434', 'qwen2.5vl:7b'))
    results = {}

    def _fakeSubmit(fn):
        results['job'] = fn()
        return 1

    mocker.patch.object(chatReplyModule, 'submit', side_effect=_fakeSubmit)
    mocker.patch.object(
        chatReplyModule, 'poll',
        side_effect=lambda _jobId: results.pop('job', chatReplyModule.PENDING))
    mocker.patch.object(
        chatReplyModule, '_askLlm',
        return_value={'speaker': 'Random Dude', 'text': 'jo', 'reply': 'jo'})
    writeSpy = mocker.patch.object(chatReplyModule, 'write')
    pressSpy = mocker.patch.object(chatReplyModule, 'press')
    logSpy = mocker.patch.object(chatReplyModule, 'logEvent')
    mocker.patch.object(chatReplyModule.humanize, 'pause')
    context = _context()

    watchChatReply(context)
    context['ng_screenshot'][100:150, 10:60] = 255  # neue Zeile erscheint
    context['ng_chatReply']['lastScanAt'] = 0
    watchChatReply(context)  # LLM-Job eingereicht
    assert context['ng_chatReply']['llmJob'] is not None

    watchChatReply(context)  # Ergebnis wird verarbeitet

    writeSpy.assert_called_once_with('jo')
    pressSpy.assert_called_once_with('enter')
    assert context['ng_tasksOrchestrator'].resetCount == 1
    assert logSpy.call_args[0][0] == 'chatReply'
    assert context['ng_chatReply']['speakers']['random dude'] > 0


def test_speaker_cooldown_blocks_second_reply(mocker, monkeypatch):
    monkeypatch.delenv(chatReplyModule.CHATREPLY_ENV, raising=False)
    _mock_chat_area(mocker)
    context = _context()
    settings = context.setdefault('ng_chatReply', {})
    settings['lastReplyAt'] = 0
    settings['speakers'] = {'random dude': chatReplyModule.monotonic()}

    answered = chatReplyModule._respond(
        context, settings,
        {'speaker': 'Random Dude', 'text': 'jo', 'reply': 'jo'},
        chatReplyModule.monotonic())

    assert answered is False
    assert context['ng_tasksOrchestrator'].resetCount == 0


def test_empty_or_long_reply_is_rejected(mocker):
    assert chatReplyModule._sanitize('') is None
    assert chatReplyModule._sanitize('x' * 80) is None
    assert chatReplyModule._sanitize('  noch ne   runde ') == 'noch ne runde'


def test_no_llm_no_action(mocker, monkeypatch):
    monkeypatch.delenv(chatReplyModule.CHATREPLY_ENV, raising=False)
    _mock_chat_area(mocker)
    mocker.patch.object(
        chatReplyModule, '_llmEndpoint', return_value=(None, None))
    llmSpy = mocker.patch.object(chatReplyModule, '_askLlm')
    context = _context()

    watchChatReply(context)
    context['ng_screenshot'][100:150, 10:60] = 255
    context['ng_chatReply']['lastScanAt'] = 0

    watchChatReply(context)

    llmSpy.assert_not_called()


def test_other_language_gets_english_question(mocker):
    context = _context()
    settings = context.setdefault('ng_chatReply', {})
    settings['lastReplyAt'] = 0
    writeSpy = mocker.patch.object(chatReplyModule, 'write')
    mocker.patch.object(chatReplyModule, 'press')
    mocker.patch.object(chatReplyModule.humanize, 'pause')
    mocker.patch.object(chatReplyModule, 'logEvent')

    answered = chatReplyModule._respond(
        context, settings,
        {'speaker': 'Ze Maria', 'language': 'other',
         'text': 'oi tudo bem?', 'reply': ''},
        chatReplyModule.monotonic())

    assert answered is True
    writeSpy.assert_called_once_with('english?')


def test_english_language_uses_llm_reply(mocker):
    context = _context()
    settings = context.setdefault('ng_chatReply', {})
    settings['lastReplyAt'] = 0
    writeSpy = mocker.patch.object(chatReplyModule, 'write')
    mocker.patch.object(chatReplyModule, 'press')
    mocker.patch.object(chatReplyModule.humanize, 'pause')
    mocker.patch.object(chatReplyModule, 'logEvent')

    answered = chatReplyModule._respond(
        context, settings,
        {'speaker': 'Dude', 'language': 'english',
         'text': 'how long?', 'reply': 'a few more rounds'},
        chatReplyModule.monotonic())

    assert answered is True
    writeSpy.assert_called_once_with('a few more rounds')
