"""messages.cli tests."""

import sys
from unittest.mock import patch

import pytest
import conftest

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

from messages import MESSAGES

import messages.cli
import messages.api
from messages.cli import check_args
from messages.cli import check_type
from messages.cli import get_body
from messages.cli import trim_args
from messages.cli import main
from messages.exceptions import UnsupportedMessageTypeError
from messages.email_ import Email


##############################################################################
# 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()


##############################################################################
# TESTS: cli.check_args
##############################################################################

def test_check_args_withArgs(get_ctx):
    """
    GIVEN a call to messages via the CLI
    WHEN check_args is called with args given
    THEN assert no errors occur
    """
    ctx = get_ctx
    kwds = {'test': 1}
    check_args(ctx, kwds)


@patch.object(click, 'echo')
def test_check_args_withNoArgs(echo_mock, get_ctx):
    """
    GIVEN a call to messages via the CLI
    WHEN check_args is called with NO args given
    THEN assert click.echo and sys.exit are called
    """
    ctx = get_ctx
    kwds = {}
    with pytest.raises(SystemExit) as sysext:
        check_args(ctx, kwds)
        assert echo_mock.call_count == 2
        assert sysext.code == 0


##############################################################################
# TESTS: cli.check_type
##############################################################################

def test_check_type_normal():
    """
    GIVEN a call to messages via the CLI
    WHEN a valid message type is given
    THEN assert no errors occur
    """
    kwds = {'type': 'email'}
    check_type(kwds)


@patch.object(click, 'echo')
def test_check_type_NoType(echo_mock):
    """
    GIVEN a call to messages via the CLI
    WHEN a NO message type is given
    THEN assert click.echo and sys.exit are called
    """
    kwds = {'type': None}
    with pytest.raises(SystemExit) as sysext:
        check_type(kwds)
        assert echo_mock.call_count == 2
        assert sysext.code == 0


def test_check_type_badType():
    """
    GIVEN a call to messages via the CLI
    WHEN an invalid message type is given
    THEN assert UnsupportedMessageTypeError is raised
    """
    kwds = {'type': 'badType'}
    with pytest.raises(UnsupportedMessageTypeError):
        check_type(kwds)


##############################################################################
# TESTS: cli.get_body
##############################################################################

@conftest.travis
def test_get_body(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(kwds)
    assert kwds == {'body': 'This is the message to send!', 'file': None}


##############################################################################
# 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'), 'attach': ('file1',)}
    kwargs = trim_args(kwds)
    assert kwargs == {'to': ['me@here.com'], 'cc': ['her@there.com'],
            'bcc': ['him@there.com', 'her@there.com'], 'attach': ['file1']}


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

@patch.object(messages.api, 'send')
@patch.object(messages.cli, 'get_body')
@patch.object(messages.cli, 'check_type')
@patch.object(messages.cli, 'check_args')
def test_main(args_mock, type_mock, body_mock, send_mock):
    runner = CliRunner()
    result = runner.invoke(main, ['-t', 'email'], catch_exceptions=True)
