"""messages.cli tests."""

import builtins
import os
import sys

import pytest

import click
from click import Context
from click.testing import CliRunner

import messages.cli
from messages.cli import get_body_from_file
from messages.cli import trim_args
from messages.cli import send_message
from messages.cli import main
from messages.email_ import Email
from messages._exceptions import UnsupportedMessageTypeError

from conftest import skip_if_on_travisCI


##############################################################################
# FIXTURES
##############################################################################

class ClickConfig(Context):
    """Used to pass ctx for click applications."""
    def __init__(self): pass
    def get_usage(self): pass


@pytest.fixture()
def get_ctx():
    """Returns a mocked click.Context() object."""
    return ClickConfig()


@pytest.fixture()
def main_mocks(mocker):
    """Returns mocks for all funcs inside main()."""
    body_mk = mocker.patch.object(messages.cli, 'get_body_from_file')
    config_mk = mocker.patch.object(messages.cli, 'create_config_entry')
    send_mk = mocker.patch.object(messages.cli, 'send')
    return (body_mk, config_mk, send_mk)


##############################################################################
# TESTS: cli.get_body_from_file
##############################################################################

@skip_if_on_travisCI
def test_get_body_from_file(tmpdir):
    """
    GIVEN a call to messages via the CLI
    WHEN a message is specified by filename
    THEN assert the file contents are read and set into the message body
        * this uses the tmpdir pytest built-in fixture to create temporary
          directory and file for the test
    """
    msg_file = tmpdir.join('msg.txt')
    msg_file.write('This is the message to send!')
    kwds = {'body': None, 'file': msg_file}
    body = get_body_from_file(kwds)
    assert kwds == {'body': 'This is the message to send!', 'file': None}


def test_get_body_from_file_travis(mocker):
    """
    GIVEN a call to messages via the CLI
    WHEN a message is specified by filename
    THEN assert the correct file operations are called and the kwds
        dict is updated
    """
    isfile_mock = mocker.patch.object(os.path, 'isfile')
    open_mock = mocker.patch.object(builtins, 'open')
    isfile_mock.return_value = True
    kwds = {'file': 'file.txt'}
    get_body_from_file(kwds)
    assert kwds['file'] is None
    assert 'body' in kwds.keys()


##############################################################################
# TESTS: cli.trim_args
##############################################################################

def test_trim_args_rejectedKV():
    """
    GIVEN a call to messages via the CLI
    WHEN trim_args is called on the CLI args
    THEN assert the correct args are removed
    """
    kwds = {'type': 'email', 'arg1': None, 'arg2': (), 'arg3': 'valid'}
    kwargs = trim_args(kwds)
    assert kwargs == {'arg3': 'valid'}


def test_trim_args_ListItems():
    """
    GIVEN a call to messages via the CLI
    WHEN trim_args is called on the CLI args
    THEN assert values with keys to/cc/bcc/attach are returned as a list
        instead of the tuple generated by the click package
    """
    kwds = {'to': ('me@here.com',), 'cc': ('her@there.com',),
            'bcc': ('him@there.com', 'her@there.com'), 'attachments': ('file1',)}
    kwargs = trim_args(kwds)
    assert kwargs == {'to': ['me@here.com'], 'cc': ['her@there.com'],
            'bcc': ['him@there.com', 'her@there.com'], 'attachments': ['file1']}


##############################################################################
# TESTS: cli.send_message
##############################################################################

def test_send_message(mocker):
    """
    GIVEN a call to messages via the CLI
    WHEN send_message is called with a valid message type with
        kwds['file'] = None
    THEN assert the correct sequence is called
    """
    send_mock = mocker.patch.object(messages.cli, 'send')
    trim_mock = mocker.patch.object(messages.cli, 'trim_args')
    file_mock = mocker.patch.object(messages.cli, 'get_body_from_file')
    send_message('email', kwds={'file': None})
    assert trim_mock.call_count == 1
    assert send_mock.call_count == 1
    assert file_mock.call_count == 0


def test_send_message_withFile(mocker):
    """
    GIVEN a call to messages via the CLI
    WHEN send_message is called with a valid message type with
        kwds['file'] != None
    THEN assert the correct sequence is called
    """
    send_mock = mocker.patch.object(messages.cli, 'send')
    trim_mock = mocker.patch.object(messages.cli, 'trim_args')
    file_mock = mocker.patch.object(messages.cli, 'get_body_from_file')
    send_message('email', kwds={'file': 'Some File'})
    assert trim_mock.call_count == 1
    assert send_mock.call_count == 1
    assert file_mock.call_count == 1


##############################################################################
# TESTS: cli.main
##############################################################################

def test_main_configure(mocker):
    """
    GIVEN a call to messages via the CLI
    WHEN subcommand = configure
    THEN assert the correct sequence is called
    """
    config_mock = mocker.patch.object(messages.cli, 'create_config_profile')
    runner = CliRunner()
    runner.invoke(main, ['configure', 'email'], catch_exceptions=False)
    assert config_mock.call_count == 1


@pytest.mark.parametrize('subcommand',
                         ['email',
                          'twilio',
                          'slackwebhook',
                          'slackpost',
                          'telegram',
                          'whatsapp'
                         ])
def test_main_message(subcommand, mocker):
    """
    GIVEN a call to a messages via the CLI
    WHEN subcommand = one of the message types
    THEN assert the correct sequence is called
    """
    send_mock = mocker.patch.object(messages.cli, 'send_message')
    runner = CliRunner()
    runner.invoke(main, [subcommand, '--verbose'], catch_exceptions=False)
    assert send_mock.call_count == 1
