# coding: utf-8
# pylint: disable=R0912,E0102
from __future__ import unicode_literals

import pytest
from django.utils.safestring import SafeData, mark_safe
from django.utils.translation import ugettext_lazy

import django_tables2 as tables

from ..app.models import Person
from ..utils import build_request, parse, warns

request = build_request('/')


def test_column_render_supports_kwargs():
    class TestColumn(tables.Column):
        def render(self, **kwargs):
            expected = {"record", "value", "column", "bound_column", "bound_row", "table"}
            actual = set(kwargs.keys())
            assert actual == expected
            return "success"

    class TestTable(tables.Table):
        foo = TestColumn()

    table = TestTable([{"foo": "bar"}])
    assert table.rows[0]["foo"] == "success"


def test_column_header_should_use_titlised_verbose_name_unless_given_explicitly():
    class SimpleTable(tables.Table):
        basic = tables.Column()
        acronym = tables.Column(verbose_name="has FBI help")

    table = SimpleTable([])
    assert table.columns["basic"].header == "Basic"
    assert table.columns["acronym"].header == "has FBI help"


def test_should_support_safe_verbose_name():
    class SimpleTable(tables.Table):
        safe = tables.Column(verbose_name=mark_safe("<b>Safe</b>"))

    table = SimpleTable([])
    assert isinstance(table.columns["safe"].header, SafeData)


def test_should_support_safe_verbose_name_via_model():
    class PersonTable(tables.Table):
        safe = tables.Column()

    table = PersonTable(Person.objects.all())
    assert isinstance(table.columns["safe"].header, SafeData)


def test_should_support_empty_string_as_explicit_verbose_name():
    class SimpleTable(tables.Table):
        acronym = tables.Column(verbose_name="")

    table = SimpleTable([])
    assert table.columns["acronym"].header == ""


@pytest.mark.django_db
def test_handle_verbose_name_of_many2onerel():

    class Table(tables.Table):
        count = tables.Column(accessor='info_list.count')

    Person.objects.create(first_name='bradley', last_name='ayers')
    table = Table(Person.objects.all())
    assert table.columns['count'].verbose_name == 'Information'


def test_sortable_backwards_compatibility():
    # Table.Meta.sortable (not set)
    class SimpleTable(tables.Table):
        name = tables.Column()
    table = SimpleTable([])
    with warns(DeprecationWarning):
        assert table.columns['name'].sortable is True

    # Table.Meta.sortable = False
    with warns(DeprecationWarning):
        class SimpleTable(tables.Table):
            name = tables.Column()

            class Meta:
                sortable = False
    table = SimpleTable([])
    with warns(DeprecationWarning):
        assert table.columns['name'].sortable is False  # backwards compatible
    assert table.columns['name'].orderable is False

    # Table.Meta.sortable = True
    with warns(DeprecationWarning):
        class SimpleTable(tables.Table):
            name = tables.Column()

            class Meta:
                sortable = True
    table = SimpleTable([])
    with warns(DeprecationWarning):
        assert table.columns['name'].sortable is True  # backwards compatible
    assert table.columns['name'].orderable is True


def test_orderable():
    # Table.Meta.orderable = False
    class SimpleTable(tables.Table):
        name = tables.Column()
    table = SimpleTable([])
    assert table.columns['name'].orderable is True

    # Table.Meta.orderable = False
    class SimpleTable(tables.Table):
        name = tables.Column()

        class Meta:
            orderable = False
    table = SimpleTable([])
    assert table.columns['name'].orderable is False

    with warns(DeprecationWarning):
        assert table.columns['name'].sortable is False  # backwards compatible

    # Table.Meta.orderable = True
    class SimpleTable(tables.Table):
        name = tables.Column()

        class Meta:
            orderable = True
    table = SimpleTable([])
    with warns(DeprecationWarning):
        assert table.columns['name'].sortable is True  # backwards compatible
    assert table.columns['name'].orderable is True


def test_order_by_defaults_to_accessor():
    class SimpleTable(tables.Table):
        foo = tables.Column(accessor="bar")

    table = SimpleTable([])
    assert table.columns["foo"].order_by == ("bar", )


def test_supports_order_by():
    class SimpleTable(tables.Table):
        name = tables.Column(order_by=("last_name", "-first_name"))
        age = tables.Column()

    table = SimpleTable([], order_by=("-age", ))
    # alias
    assert table.columns["name"].order_by_alias == "name"
    assert table.columns["age"].order_by_alias == "-age"
    # order by
    assert table.columns["name"].order_by == ("last_name", "-first_name")
    assert table.columns["age"].order_by == ("-age", )

    # now try with name ordered
    table = SimpleTable([], order_by=("-name", ))
    # alias
    assert table.columns["name"].order_by_alias == "-name"
    assert table.columns["age"].order_by_alias == "age"
    # alias next
    assert table.columns["name"].order_by_alias.next == "name"
    assert table.columns["age"].order_by_alias.next == "age"
    # order by
    assert table.columns["name"].order_by == ("-last_name", "first_name")
    assert table.columns["age"].order_by == ("age", )


def test_supports_is_ordered():
    class SimpleTable(tables.Table):
        name = tables.Column()

    # sorted
    table = SimpleTable([], order_by='name')
    assert table.columns["name"].is_ordered
    # unsorted
    table = SimpleTable([])
    assert not table.columns["name"].is_ordered


def test_translation():
    """
    Tests different types of values for the ``verbose_name`` property of a
    column.
    """
    class TranslationTable(tables.Table):
        text = tables.Column(verbose_name=ugettext_lazy("Text"))

    table = TranslationTable([])
    assert "Text" == table.columns["text"].header


def test_sequence():
    """
    Ensures that the sequence of columns is configurable.
    """
    class TestTable(tables.Table):
        a = tables.Column()
        b = tables.Column()
        c = tables.Column()
    assert ["a", "b", "c"] == TestTable([]).columns.names()
    assert ["b", "a", "c"] == TestTable([], sequence=("b", "a", "c")).columns.names()

    class TestTable2(TestTable):
        class Meta:
            sequence = ("b", "a", "c")
    assert ["b", "a", "c"] == TestTable2([]).columns.names()
    assert ["a", "b", "c"] == TestTable2([], sequence=("a", "b", "c")).columns.names()

    class TestTable3(TestTable):
        class Meta:
            sequence = ("c", )
    assert ["c", "a", "b"] == TestTable3([]).columns.names()
    assert ["c", "a", "b"] == TestTable([], sequence=("c", )).columns.names()

    class TestTable4(TestTable):
        class Meta:
            sequence = ("...", )
    assert ["a", "b", "c"] == TestTable4([]).columns.names()
    assert ["a", "b", "c"] == TestTable([], sequence=("...", )).columns.names()

    class TestTable5(TestTable):
        class Meta:
            sequence = ("b", "...")
    assert ["b", "a", "c"] == TestTable5([]).columns.names()
    assert ["b", "a", "c"] == TestTable([], sequence=("b", "...")).columns.names()

    class TestTable6(TestTable):
        class Meta:
            sequence = ("...", "b")
    assert ["a", "c", "b"] == TestTable6([]).columns.names()
    assert ["a", "c", "b"] == TestTable([], sequence=("...", "b")).columns.names()

    class TestTable7(TestTable):
        class Meta:
            sequence = ("b", "...", "a")
    assert ["b", "c", "a"] == TestTable7([]).columns.names()
    assert ["b", "c", "a"] == TestTable([], sequence=("b", "...", "a")).columns.names()

    # Let's test inheritence
    class TestTable8(TestTable):
        d = tables.Column()
        e = tables.Column()
        f = tables.Column()

        class Meta:
            sequence = ("d", "...")

    class TestTable9(TestTable):
        d = tables.Column()
        e = tables.Column()
        f = tables.Column()

    assert ["d", "a", "b", "c", "e", "f"] == TestTable8([]).columns.names()
    assert ["d", "a", "b", "c", "e", "f"] == TestTable9([], sequence=("d", "...")).columns.names()


def test_should_support_both_meta_sequence_and_constructor_exclude():
    """
    Issue #32 describes a problem when both ``Meta.sequence`` and
    ``Table(..., exclude=...)`` are used on a single table. The bug caused an
    exception to be raised when the table was iterated.
    """
    class SequencedTable(tables.Table):
        a = tables.Column()
        b = tables.Column()
        c = tables.Column()

        class Meta:
            sequence = ('a', '...')

    table = SequencedTable([], exclude=('c', ))
    table.as_html(request)


def test_bound_columns_should_support_indexing():
    class SimpleTable(tables.Table):
        a = tables.Column()
        b = tables.Column()

    table = SimpleTable([])
    assert 'b' == table.columns[1].name
    assert 'b' == table.columns['b'].name


def test_cell_attrs_applies_to_td_and_th():
    class SimpleTable(tables.Table):
        a = tables.Column(attrs={"cell": {"key": "value"}})

    # providing data ensures 1 row is rendered
    table = SimpleTable([{"a": "value"}])
    root = parse(table.as_html(request))

    assert root.findall('.//thead/tr/th')[0].attrib == {"key": "value", "class": "a orderable sortable"}
    assert root.findall('.//tbody/tr/td')[0].attrib == {"key": "value", "class": "a"}


def test_cells_are_automatically_given_column_name_as_class():
    class SimpleTable(tables.Table):
        a = tables.Column()

    table = SimpleTable([{"a": "value"}])
    root = parse(table.as_html(request))
    assert root.findall('.//thead/tr/th')[0].attrib == {"class": "a orderable sortable"}
    assert root.findall('.//tbody/tr/td')[0].attrib == {"class": "a"}


def test_th_are_given_sortable_class_if_column_is_orderable():
    class SimpleTable(tables.Table):
        a = tables.Column()
        b = tables.Column(orderable=False)

    table = SimpleTable([{"a": "value"}])
    root = parse(table.as_html(request))
    # return classes of an element as a set
    classes = lambda x: set(x.attrib["class"].split())
    assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
    assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])

    # Now try with an ordered table
    table = SimpleTable([], order_by="a")
    root = parse(table.as_html(request))
    # return classes of an element as a set
    assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
    assert "asc" in classes(root.findall('.//thead/tr/th')[0])
    assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])


def test_empty_values_triggers_default():
    class Table(tables.Table):
        a = tables.Column(empty_values=(1, 2), default="--")

    table = Table([{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}])
    assert [x["a"] for x in table.rows] == ["--", "--", 3, 4]
