import pytest
from unittest.mock import patch
from prompt_toolkit.completion import Completion
from prompt_toolkit.document import Document
import mycli.packages.special.main as special

metadata = {
    'users': ['id', 'email', 'first_name', 'last_name'],
    'orders': ['id', 'ordered_date', 'status'],
    'select': ['id', 'insert', 'ABC'],
    'réveillé': ['id', 'insert', 'ABC']
}


@pytest.fixture
def completer():

    import mycli.sqlcompleter as sqlcompleter
    comp = sqlcompleter.SQLCompleter(smart_completion=True)

    tables, columns = [], []

    for table, cols in metadata.items():
        tables.append((table,))
        columns.extend([(table, col) for col in cols])

    comp.set_dbname('test')
    comp.extend_schemata('test')
    comp.extend_relations(tables, kind='tables')
    comp.extend_columns(columns, kind='tables')
    comp.extend_special_commands(special.COMMANDS)

    return comp


@pytest.fixture
def complete_event():
    from unittest.mock import Mock
    return Mock()


def test_special_name_completion(completer, complete_event):
    text = '\\d'
    position = len('\\d')
    result = completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event)
    assert result == [Completion(text='\\dt', start_position=-2)]


def test_empty_string_completion(completer, complete_event):
    text = ''
    position = 0
    result = list(
        completer.get_completions(
            Document(text=text, cursor_position=position),
            complete_event))
    assert list(map(Completion, sorted(completer.keywords) +
                    sorted(completer.special_commands))) == result


def test_select_keyword_completion(completer, complete_event):
    text = 'SEL'
    position = len('SEL')
    result = completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event)
    assert list(result) == list([Completion(text='SELECT', start_position=-3)])


def test_table_completion(completer, complete_event):
    text = 'SELECT * FROM '
    position = len(text)
    result = completer.get_completions(
        Document(text=text, cursor_position=position), complete_event)
    assert list(result) == list([
        Completion(text='`réveillé`', start_position=0),
        Completion(text='`select`', start_position=0),
        Completion(text='orders', start_position=0),
        Completion(text='users', start_position=0),
    ])


def test_function_name_completion(completer, complete_event):
    text = 'SELECT MA'
    position = len('SELECT MA')
    result = completer.get_completions(
        Document(text=text, cursor_position=position), complete_event)
    assert list(result) == list([Completion(text='MAX', start_position=-2),
                               Completion(text='MASTER', start_position=-2),
                               ])


def test_suggested_column_names(completer, complete_event):
    """Suggest column and function names when selecting from table.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT  from users'
    position = len('SELECT ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0),
    ] +
        list(map(Completion, completer.functions)) +
        [Completion(text='users', start_position=0)] +
        list(map(Completion, completer.keywords)))


def test_suggested_column_names_in_function(completer, complete_event):
    """Suggest column and function names when selecting multiple columns from
    table.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT MAX( from users'
    position = len('SELECT MAX(')
    result = completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event)
    assert list(result) == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)])


def test_suggested_column_names_with_table_dot(completer, complete_event):
    """Suggest column names on table name and dot.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT users. from users'
    position = len('SELECT users.')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)])


def test_suggested_column_names_with_alias(completer, complete_event):
    """Suggest column names on table alias and dot.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT u. from users u'
    position = len('SELECT u.')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)])


def test_suggested_multiple_column_names(completer, complete_event):
    """Suggest column and function names when selecting multiple columns from
    table.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT id,  from users u'
    position = len('SELECT id, ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)] +
        list(map(Completion, completer.functions)) +
        [Completion(text='u', start_position=0)] +
        list(map(Completion, completer.keywords)))


def test_suggested_multiple_column_names_with_alias(completer, complete_event):
    """Suggest column names on table alias and dot when selecting multiple
    columns from table.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT u.id, u. from users u'
    position = len('SELECT u.id, u.')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)])


def test_suggested_multiple_column_names_with_dot(completer, complete_event):
    """Suggest column names on table names and dot when selecting multiple
    columns from table.

    :param completer:
    :param complete_event:
    :return:

    """
    text = 'SELECT users.id, users. from users u'
    position = len('SELECT users.id, users.')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='email', start_position=0),
        Completion(text='first_name', start_position=0),
        Completion(text='id', start_position=0),
        Completion(text='last_name', start_position=0)])


def test_suggested_aliases_after_on(completer, complete_event):
    text = 'SELECT u.name, o.id FROM users u JOIN orders o ON '
    position = len('SELECT u.name, o.id FROM users u JOIN orders o ON ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='o', start_position=0),
        Completion(text='u', start_position=0)])


def test_suggested_aliases_after_on_right_side(completer, complete_event):
    text = 'SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = '
    position = len(
        'SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='o', start_position=0),
        Completion(text='u', start_position=0)])


def test_suggested_tables_after_on(completer, complete_event):
    text = 'SELECT users.name, orders.id FROM users JOIN orders ON '
    position = len('SELECT users.name, orders.id FROM users JOIN orders ON ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='orders', start_position=0),
        Completion(text='users', start_position=0)])


def test_suggested_tables_after_on_right_side(completer, complete_event):
    text = 'SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = '
    position = len(
        'SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='orders', start_position=0),
        Completion(text='users', start_position=0)])


def test_table_names_after_from(completer, complete_event):
    text = 'SELECT * FROM '
    position = len('SELECT * FROM ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='`réveillé`', start_position=0),
        Completion(text='`select`', start_position=0),
        Completion(text='orders', start_position=0),
        Completion(text='users', start_position=0),
    ])


def test_auto_escaped_col_names(completer, complete_event):
    text = 'SELECT  from `select`'
    position = len('SELECT ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == [
        Completion(text='*', start_position=0),
        Completion(text='`ABC`', start_position=0),
        Completion(text='`insert`', start_position=0),
        Completion(text='id', start_position=0),
    ] + \
        list(map(Completion, completer.functions)) + \
        [Completion(text='`select`', start_position=0)] + \
        list(map(Completion, completer.keywords))


def test_un_escaped_table_names(completer, complete_event):
    text = 'SELECT  from réveillé'
    position = len('SELECT ')
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    assert result == list([
        Completion(text='*', start_position=0),
        Completion(text='`ABC`', start_position=0),
        Completion(text='`insert`', start_position=0),
        Completion(text='id', start_position=0),
    ] +
        list(map(Completion, completer.functions)) +
        [Completion(text='réveillé', start_position=0)] +
        list(map(Completion, completer.keywords)))


def dummy_list_path(dir_name):
    dirs = {
        '/': [
            'dir1',
            'file1.sql',
            'file2.sql',
        ],
        '/dir1': [
            'subdir1',
            'subfile1.sql',
            'subfile2.sql',
        ],
        '/dir1/subdir1': [
            'lastfile.sql',
        ],
    }
    return dirs.get(dir_name, [])


@patch('mycli.packages.filepaths.list_path', new=dummy_list_path)
@pytest.mark.parametrize('text,expected', [
    #    ('source ',  [('~', 0),
    #                  ('/', 0),
    #                  ('.', 0),
    #                  ('..', 0)]),
    ('source /', [('dir1', 0),
                  ('file1.sql', 0),
                  ('file2.sql', 0)]),
    ('source /dir1/', [('subdir1', 0),
                       ('subfile1.sql', 0),
                       ('subfile2.sql', 0)]),
    ('source /dir1/subdir1/', [('lastfile.sql', 0)]),
])
def test_file_name_completion(completer, complete_event, text, expected):
    position = len(text)
    result = list(completer.get_completions(
        Document(text=text, cursor_position=position),
        complete_event))
    expected = list((Completion(txt, pos) for txt, pos in expected))
    assert result == expected
