from mock import Mock
import pytest

from django.contrib.messages.storage.fallback import FallbackStorage
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.test import RequestFactory

from hid.tabs.view_and_edit_table import (
    ViewAndEditTableTab,
    view_and_edit_table_form_process_items,
    _delete_items,
    _get_view_and_edit_form_request_parameters,
    DELETE_COMMAND,
    REMOVE_QTYPE_COMMAND
)

from tabbed_page.tests.factories import (
    TabbedPageFactory,
    TabInstanceFactory
)

from taxonomies.tests.factories import (
    TaxonomyFactory,
    TermFactory,
)

import transport


ReqFactory = RequestFactory()


def fix_messages(request):
    '''
    Save use of messages with RequestFactory.

    Stored message objects can be found in: request._messages._queued_messages
    (attributes: level, level_tag, message, tags)
    '''
    setattr(request, 'session', 'session')
    messages = FallbackStorage(request)
    setattr(request, '_messages', messages)
    return request


def assert_message(request, level, content):
    messages = [(m.message, m.level)
                for m in request._messages._queued_messages]

    assert (content, level) in messages


def assert_no_messages(request, level):
    messages = [m.message
                for m in request._messages._queued_messages if m.level == level]

    assert messages == []


def check_message(request, content):
    for msg in request._messages._queued_messages:
        if msg.message == content:
            return True
    return False


@pytest.fixture
def request_item():
    '''Create item and request'''

    msg = {'body': "Message text"}
    transport.items.create(msg)

    [item] = list(transport.items.list())

    url = reverse('data-view-process')
    request = ReqFactory.post(url, {
        'action': 'batchupdate-top',
        'batchaction-top': DELETE_COMMAND,
        'select_item_id': [item['id']],
        'next': 'http://localhost/testurl'
    })
    request = fix_messages(request)

    return [request, item]


def check_item_was_deleted(request):
    assert check_message(request, u"1 item deleted.") is True

    items = list(transport.items.list())
    assert len(list(items)) == 0


@pytest.mark.django_db
def test_delete_items_deletes_items(request_item):
    req, item = request_item
    _delete_items(req, [item['id']])
    check_item_was_deleted(req)


@pytest.mark.django_db
def test_process_items_deletes_items(request_item):
    req, item = request_item
    view_and_edit_table_form_process_items(req)
    check_item_was_deleted(req)


@pytest.mark.django_db
def test_process_items_removes_question_type():
    msg = {'body': "Message text"}
    transport.items.create(msg)

    [item] = list(transport.items.list())

    taxonomy = TaxonomyFactory(name="Ebola Questions")
    term_to_delete = TermFactory(name='term to be deleted',
                                 taxonomy=taxonomy)
    transport.items.add_terms(
        item['id'], term_to_delete.taxonomy.slug, term_to_delete.name)

    term_to_keep = TermFactory(name='term not to be deleted')
    transport.items.add_terms(
        item['id'], term_to_keep.taxonomy.slug, term_to_keep.name)

    url = reverse('data-view-process')
    request = ReqFactory.post(url, {
        'action': 'batchupdate-top',
        'batchaction-top': REMOVE_QTYPE_COMMAND,
        'select_item_id': [item['id']],
        'next': 'http://localhost/testurl'
    })

    request = fix_messages(request)
    view_and_edit_table_form_process_items(request)

    [item] = list(transport.items.list())

    term_names = [t['name'] for t in item['terms']]

    assert term_to_keep.name in term_names
    assert term_to_delete.name not in term_names


def test_empty_process_items_redirects_to_data_view():
    url = reverse('data-view-process')
    redirect_url = reverse('tabbed-page', kwargs={
        'name': 'main',
        'tab_name': 'all'
    })

    request = ReqFactory.get(url)

    response = view_and_edit_table_form_process_items(request)
    assert response.url == redirect_url
    assert isinstance(response, HttpResponseRedirect) is True


@pytest.mark.django_db
def test_process_items_redirects_to_provided_url(request_item):
    req, item = request_item
    response = view_and_edit_table_form_process_items(req)
    assert response.url == 'http://localhost/testurl'
    assert isinstance(response, HttpResponseRedirect) is True


@pytest.mark.django_db
def test_get_category_options_uses_terms():
    # TODO: Rewrite tests to use transport layer
    ebola_questions = TaxonomyFactory(name="Ebola Questions")
    other_taxonomy = TaxonomyFactory(name="Should be ignored")
    type_1 = TermFactory(
        name="Measures",
        taxonomy=ebola_questions,
        long_name="What measures could end Ebola?",
    )
    type_2 = TermFactory(
        name="Survivors",
        taxonomy=ebola_questions,
        long_name="Are survivors stigmatized?",
    )
    type_3 = TermFactory(
        name="Victims",
        taxonomy=ebola_questions,
        long_name="Number of Victims?",
    )
    other_term = TermFactory(
        name="Should be ignored",
        taxonomy=other_taxonomy,
    )

    table = ViewAndEditTableTab()
    options = table._get_category_options(categories=[ebola_questions.slug])

    assert (type_1.name, type_1.name) in options
    assert (type_2.name, type_2.name) in options
    assert (type_3.name, type_3.name) in options
    assert (other_term.name, other_term.long_name) not in options


@pytest.mark.django_db
def test_get_category_options_orders_by_lowercase_name():
    # TODO: Rewrite tests to use transport layer
    taxonomy = TaxonomyFactory(name="order_by_lowercase_name_test")
    test_term_values = [
        ('test a1', '1'), ('test b1', '2'),
        ('test A2', '3'), ('test B2', '4')
    ]
    for test_value in test_term_values:
        TermFactory(
            name=test_value[0],
            long_name=test_value[1],
            taxonomy=taxonomy
        )

    table = ViewAndEditTableTab()
    options = table._get_category_options(categories=[taxonomy.slug])

    # Expected is the list ordered by lowercase short name.
    expected = [(short, short) for short, long_name in test_term_values]
    expected = tuple(sorted(expected, key=lambda e: e[0].lower()))

    assert options == expected


@pytest.mark.django_db
def test_actions_includes_remove_question_type_option():
    page = TabbedPageFactory()
    tab_instance = TabInstanceFactory(page=page)
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    term = TermFactory()
    categories = [term.taxonomy.slug]

    context_data = tab.get_context_data(tab_instance,
                                        request,
                                        categories=categories)

    actions = context_data['actions'][0]
    assert actions['label'] == 'Actions'
    assert 'remove-question-type' in actions['items']


@pytest.mark.django_db
def test_actions_excludes_remove_question_type_option_for_no_categories():
    page = TabbedPageFactory()
    tab_instance = TabInstanceFactory(page=page)
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    context_data = tab.get_context_data(tab_instance,
                                        request)

    actions = context_data['actions'][0]
    assert actions['label'] == 'Actions'
    assert 'remove-question-type' not in actions['items']


@pytest.mark.django_db
def test_upload_form_source_read_from_settings():
    page = TabbedPageFactory()
    tab_instance = TabInstanceFactory(page=page)
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    context_data = tab.get_context_data(tab_instance,
                                        request,
                                        source='rapidpro')

    form = context_data['upload_form']
    assert form.initial.get('source') == 'rapidpro'


@pytest.mark.django_db
def test_upload_form_next_url_read_from_tab_instance():
    page = TabbedPageFactory(name='main')
    tab_instance = TabInstanceFactory(page=page, name='rumors')
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    context_data = tab.get_context_data(tab_instance,
                                        request,
                                        source='rapidpro')

    form = context_data['upload_form']

    expected_url = reverse('tabbed-page',
                           kwargs={'name': 'main', 'tab_name': 'rumors'})

    assert form.initial.get('next') == expected_url


@pytest.mark.django_db
def test_no_upload_form_when_source_not_set():
    page = TabbedPageFactory(name='main')
    tab_instance = TabInstanceFactory(page=page, name='all')
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    context_data = tab.get_context_data(tab_instance,
                                        request)

    form = context_data['upload_form']

    assert form is None


def test_views_item_get_request_parameters_renames_items_of_active_location():
    query = QueryDict(
        'action=something-bottom&item-top=top-value&item-bottom=bottom-value'
    )
    expected = {
        'action': 'something',
        'item': 'bottom-value',
        'item-top': 'top-value'
    }
    actual = _get_view_and_edit_form_request_parameters(query)
    assert actual.dict() == expected


def test_views_item_get_request_parameters_sets_default_location():
    query = QueryDict(
        'action=something&item-top=top-value&item-bottom=bottom-value'
    )
    expected = {
        'action': 'something',
        'item': 'top-value',
        'item-bottom': 'bottom-value'
    }
    actual = _get_view_and_edit_form_request_parameters(query)
    assert actual.dict() == expected


def test_views_item_get_request_parameters_sets_default_action_and_location():
    query = QueryDict(
        'item-top=top-value&item-bottom=bottom-value'
    )
    expected = {
        'action': 'none',
        'item': 'top-value',
        'item-bottom': 'bottom-value'
    }
    actual = _get_view_and_edit_form_request_parameters(query)
    assert actual.dict() == expected


@pytest.mark.django_db
def test_view_and_edit_table_tab_sets_add_button_context():
    item_types = TaxonomyFactory(slug="item-types", name="Item Types")
    test_item_type = TermFactory(
        name='test-item-type',
        taxonomy=item_types,
        long_name="Test Item Type"
    )
    page = TabbedPageFactory()
    tab_instance = TabInstanceFactory(page=page)
    request = Mock(GET={})
    tab = ViewAndEditTableTab()

    filters = {'terms': ['item-types:test-item-type']}
    context_data = tab.get_context_data(
        tab_instance, request, filters=filters
    )

    assert context_data['add_button_for'] == {
        'taxonomy': 'item-types',
        'name': test_item_type.name,
        'long_name': test_item_type.long_name
    }