from unittest.mock import patch, Mock

import pytest

from main import HangBot


@pytest.fixture(scope="module")
def mock_game_engine():
    with patch('main.GameEngine') as mock:
        yield mock


@pytest.fixture(scope="module")
def mock_slack_client():
    with patch('main.SlackClient') as mock:
        yield mock


@pytest.fixture(scope="module")
def mock_get_bot_information():
    with patch('main.HangBot._get_bot_information') as mock:
        yield mock


def test_init(mock_game_engine, mock_slack_client, mock_get_bot_information):
    slack_token = Mock()
    h = HangBot('somefile.txt', slack_token)

    mock_slack_client.assert_called_with(slack_token)
    assert h.client == mock_slack_client()


def test_init_name(mock_game_engine, mock_slack_client, mock_get_bot_information):
    h = HangBot('somefile.txt', Mock(), name='custom_name')

    assert h.name == 'custom_name'
    mock_get_bot_information.assert_called_with()
    assert h.info == mock_get_bot_information()


def test_init_name_default(mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())

    assert h.name == 'hangbot'


def test_command_play_no_params(mock_game_engine):
    h = HangBot('somefile.txt', Mock())
    h.command_play(params=[])

    mock_game_engine().start.assert_called_with()


def test_command_play_params(mock_game_engine):
    h = HangBot('somefile.txt', Mock())
    h.command_play(params=[1, 2, 3])

    mock_game_engine().start.assert_called_with()


def test_command_play_output(mock_game_engine):
    mock_return = Mock()
    mock_game_engine().start.return_value = mock_return

    h = HangBot('somefile.txt', Mock())
    res = h.command_play(params=[])

    mock_game_engine().start.assert_called_with()
    assert res == mock_return


def test_command_try_no_params(mock_game_engine):
    h = HangBot('somefile.txt', Mock())
    res = h.command_try(params=[])

    assert res == (h.ERROR_NO_PARAMS, mock_game_engine().status)
    mock_game_engine().guess_letter.assert_not_called()


def test_command_try_single_param(mock_game_engine):
    mock_result = Mock()
    mock_game_engine().guess_letter.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    res = h.command_try(params=['a'])

    assert res == mock_result
    mock_game_engine().guess_letter.assert_called_with('a')


def test_command_try_multiple_params(mock_game_engine):
    mock_result = Mock()
    mock_game_engine().guess_letter.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    res = h.command_try(params=['a', 'b'])

    assert res == mock_result
    mock_game_engine().guess_letter.assert_called_with('a')


def test_command_guess_no_params(mock_game_engine):
    h = HangBot('somefile.txt', Mock())
    res = h.command_guess(params=[])

    assert res == (h.ERROR_NO_PARAMS, mock_game_engine().status)
    mock_game_engine().guess_word.assert_not_called()


def test_command_guess_single_param(mock_game_engine):
    mock_result = Mock()
    mock_game_engine().guess_word.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    res = h.command_guess(params=['someword'])

    assert res == mock_result
    mock_game_engine().guess_word.assert_called_with('someword')


def test_command_guess_multiple_params(mock_game_engine):
    mock_result = Mock()
    mock_game_engine().guess_word.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    res = h.command_guess(params=['someword', 'otherword'])

    assert res == mock_result
    mock_game_engine().guess_word.assert_called_with('someword otherword')


@patch('main.HangBot.command_try')
@patch('main.GameEngine')
def test_handle_command_try(mock_game_engine, mock_command_try):
    mock_result = Mock()
    mock_command_try.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    params = Mock()

    res = h.handle_command('try', params)
    mock_command_try.assert_called_with(params)
    assert res == mock_result


@patch('main.HangBot.command_guess')
@patch('main.GameEngine')
def test_handle_command_guess(mock_game_engine, mock_command_guess):
    mock_result = Mock()
    mock_command_guess.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    params = Mock()

    res = h.handle_command('guess', params)
    mock_command_guess.assert_called_with(params)
    assert res == mock_result


@patch('main.HangBot.command_play')
@patch('main.GameEngine')
def test_handle_command_play(mock_game_engine, mock_command_play):
    mock_result = Mock()
    mock_command_play.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    params = Mock()

    res = h.handle_command('play', params)
    mock_command_play.assert_called_with(params)
    assert res == mock_result


@patch('main.HangBot.command_unknown')
@patch('main.GameEngine')
def test_handle_command_unknown(mock_game_engine, mock_command_unknown):
    mock_result = Mock()
    mock_command_unknown.return_value = mock_result

    h = HangBot('somefile.txt', Mock())
    params = Mock()

    res = h.handle_command('some_unknown_command', params)
    mock_command_unknown.assert_called_with(params)
    assert res == mock_result


@patch('main.GameEngine')
def test_handle_command_unknown_output(mock_game_engine):
    h = HangBot('somefile.txt', Mock())
    params = Mock()

    res = h.command_unknown(params)
    assert res == (h.ERROR_COMMAND_UNKNOWN, mock_game_engine().status)


@patch('main.SlackClient')
@patch('main.HangBot.handle_command')
@patch('main.GameEngine')
def test_handle_parse_line(mock_game_engine, mock_handle_command,
                           mock_slack_client):
    mock_result = Mock()
    mock_handle_command.return_value = mock_result

    h = HangBot('somefile.txt', Mock())

    h.parse_line('COMMAND PARAM1 PARAM2')
    mock_handle_command.assert_called_with('COMMAND', ['PARAM1', 'PARAM2'])


@patch('main.SlackClient')
@patch('main.HangBot.no_command')
@patch('main.GameEngine')
def test_handle_parse_line_no_command(mock_game_engine, mock_no_command,
                                      mock_slack_client):
    h = HangBot('somefile.txt', Mock())

    h.parse_line('')
    mock_no_command.assert_called_with()


@patch('main.SlackClient')
@patch('main.HangBot.handle_command')
@patch('main.GameEngine')
def test_handle_parse_line_no_params(mock_game_engine, mock_handle_command,
                                     mock_slack_client):
    mock_result = Mock()
    mock_handle_command.return_value = mock_result

    h = HangBot('somefile.txt', Mock())

    h.parse_line('COMMAND')
    mock_handle_command.assert_called_with('COMMAND', [])


def test_parse_messages_empty(mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())

    res = h.parse_messages([])
    assert res == []


def test_parse_messages_get_command(mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())
    h.atbot = "<@U3BE3TQLD>"

    res = h.parse_messages(
        [
            {
                'team': 'T3B9DF41Z',
                'ts': '1481283486.000005',
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'text': '<@U3BE3TQLD> hi',
                'type': 'message'
            }
        ]
    )

    assert res == [('hi', 'D3C20JDPY')]


def test_parse_messages_filter_empty_messages(
        mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())
    h.atbot = "<@U3BE3TQLD>"

    res = h.parse_messages(
        [
            {
                'team': 'T3B9DF41Z',
                'ts': '1481283486.000005',
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'text': '<@U3BE3TQLD> hi',
                'type': 'message'
            },
            {}
        ]
    )

    assert res == [('hi', 'D3C20JDPY')]


def test_parse_messages_filter_messages_without_text(
        mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())
    h.atbot = "<@U3BE3TQLD>"

    res = h.parse_messages(
        [
            {
                'team': 'T3B9DF41Z',
                'ts': '1481283486.000005',
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'text': '<@U3BE3TQLD> hi',
                'type': 'message'
            },
            {},
            {
                'type': 'hello'
            },
            {
                'presence': 'active',
                'user': 'U3BE3TQLD',
                'type': 'presence_change'
            },
            {
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'type': 'user_typing'
            }
        ]
    )

    assert res == [('hi', 'D3C20JDPY')]


def test_parse_messages_filter_messages_to_other_users(
        mock_game_engine, mock_slack_client):
    h = HangBot('somefile.txt', Mock())
    h.atbot = "<@U3BE3TQLD>"

    res = h.parse_messages(
        [
            {
                'team': 'T3B9DF41Z',
                'ts': '1481283486.000005',
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'text': '<@U3BE3TQLD> hi',
                'type': 'message'
            },
            {
                'team': 'T3B9DF41Z',
                'ts': '1481283486.000005',
                'channel': 'D3C20JDPY',
                'user': 'U3AL11V96',
                'text': '<@U3RTH8CLF> another',
                'type': 'message'
            }
        ]
    )

    assert res == [('hi', 'D3C20JDPY')]
