import pytest
from django.core.exceptions import ValidationError
from django.forms import inlineformset_factory, modelformset_factory
from lxml.html import html5parser

from i18nfield.forms import (
    I18nForm, I18nFormField, I18nInlineFormSet, I18nModelFormSet,
    I18nTextInput,
)
from i18nfield.strings import LazyI18nString

from .testapp.forms import BookForm, SimpleForm
from .testapp.models import Author, Book


def test_field_defaults():
    f = I18nFormField(widget=I18nTextInput)
    assert len(f.fields) == 3
    assert f.clean(LazyI18nString('Foo')) == LazyI18nString('Foo')
    assert f.clean(['A', 'B', 'C']) == LazyI18nString({
        'de': 'A', 'en': 'B', 'fr': 'C'
    })


def test_limited_locales():
    f = I18nFormField(widget=I18nTextInput, locales=['de', 'fr'])
    assert len(f.fields) == 2
    assert f.clean(['A', 'B']) == LazyI18nString({
        'de': 'A', 'fr': 'B'
    })
    assert f.widget.enabled_locales == ['de', 'fr']


def test_required():
    f = I18nFormField(widget=I18nTextInput, required=True)
    assert f.clean(['A', ''])
    assert f.clean(['', 'B'])
    with pytest.raises(ValidationError):
        assert f.clean(['', ''])


def test_not_required():
    f = I18nFormField(widget=I18nTextInput, required=False)
    f.clean(['', ''])


def test_require_all_fields():
    f = I18nFormField(widget=I18nTextInput, require_all_fields=True)
    assert f.clean(['A', 'B', 'C'])
    with pytest.raises(ValidationError):
        f.clean(['A', '', ''])
    with pytest.raises(ValidationError):
        f.clean(['', 'B', ''])
    with pytest.raises(ValidationError):
        f.clean(['', '', 'C'])


def test_require_all_fields_limited_locales():
    f = I18nFormField(widget=I18nTextInput, require_all_fields=True, locales=['de', 'fr'])
    assert f.clean(['A', 'B'])
    with pytest.raises(ValidationError):
        f.clean(['A', ''])
    with pytest.raises(ValidationError):
        f.clean(['', 'B'])


def test_require_all_fields_limited_locales_in_form():
    class SimpleRequiredForm(I18nForm):
        title = I18nFormField(widget=I18nTextInput, require_all_fields=True)

    sf = SimpleRequiredForm({'title_1': 'A'}, locales=['en'])  # de, en, fr
    assert sf.is_valid()
    sf = SimpleRequiredForm({'title_1': 'A', 'title_2': 'B'}, locales=['en', 'fr'])  # de, en, fr
    assert sf.is_valid()
    sf = SimpleRequiredForm({'title_0': 'A', 'title_1': 'B', 'title_2': 'C'}, locales=['de', 'en', 'fr'])
    assert sf.is_valid()
    sf = SimpleRequiredForm({'title_1': 'A', 'title_2': ''}, locales=['en', 'fr'])  # de, en, fr
    assert not sf.is_valid()
    sf = SimpleRequiredForm({'title_1': '', 'title_2': 'B'}, locales=['en', 'fr'])  # de, en, fr
    assert not sf.is_valid()
    sf = SimpleRequiredForm({'title_0': 'A', 'title_1': 'B', 'title_2': ''}, locales=['de', 'en', 'fr'])
    assert not sf.is_valid()


def test_max_length():
    f = I18nFormField(widget=I18nTextInput, required=False, max_length=20)
    assert f.clean(['123', ''])
    with pytest.raises(ValidationError):
        f.clean(['1234567890123456789012', ''])


def test_modelform_pass_locales_down():
    bf = BookForm(locales=['de', 'fr'])
    assert bf.fields['title'].widget.enabled_locales == ['de', 'fr']


def test_form_pass_locales_down():
    sf = SimpleForm(locales=['de', 'fr'])
    assert sf.fields['title'].widget.enabled_locales == ['de', 'fr']


@pytest.mark.django_db
def test_modelformset_pass_locales_down():
    a = Author.objects.create(name='Tolkien')
    title = 'The Lord of the Rings'
    abstract = 'Frodo tries to destroy a ring'
    Book.objects.create(author=a, title=title, abstract=abstract)

    FormSetClass = modelformset_factory(Book, form=BookForm, formset=I18nModelFormSet)
    fs = FormSetClass(locales=['de', 'fr'], queryset=Book.objects.all())
    assert fs.forms[0].fields['title'].widget.enabled_locales == ['de', 'fr']
    assert fs.empty_form.fields['title'].widget.enabled_locales == ['de', 'fr']


@pytest.mark.django_db
def test_inlineformset_pass_locales_down():
    a = Author.objects.create(name='Tolkien')
    title = 'The Lord of the Rings'
    abstract = 'Frodo tries to destroy a ring'
    Book.objects.create(author=a, title=title, abstract=abstract)

    FormSetClass = inlineformset_factory(Author, Book, form=BookForm, formset=I18nInlineFormSet)
    fs = FormSetClass(locales=['de', 'fr'], instance=a)
    assert fs.forms[0].fields['title'].widget.enabled_locales == ['de', 'fr']
    assert fs.empty_form.fields['title'].widget.enabled_locales == ['de', 'fr']


def test_widget():
    f = I18nFormField(widget=I18nTextInput, required=False, localize=True)
    rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'}))
    tree = html5parser.fromstring(rendered)
    assert tree[0].attrib == {
        'lang': 'de', 'name': 'foo_0', 'type': 'text', 'value': 'Hallo'
    }
    assert tree[1].attrib == {
        'lang': 'en', 'name': 'foo_1', 'type': 'text', 'value': 'Hello'
    }
    assert tree[2].attrib == {
        'lang': 'fr', 'name': 'foo_2', 'type': 'text'
    }


def test_widget_empty():
    f = I18nFormField(widget=I18nTextInput, required=False, localize=True)
    rendered = f.widget.render('foo', [])
    tree = html5parser.fromstring(rendered)
    assert tree[0].attrib == {
        'lang': 'de', 'name': 'foo_0', 'type': 'text'
    }
    assert tree[1].attrib == {
        'lang': 'en', 'name': 'foo_1', 'type': 'text'
    }
    assert tree[2].attrib == {
        'lang': 'fr', 'name': 'foo_2', 'type': 'text'
    }


def test_widget_required():
    f = I18nFormField(widget=I18nTextInput, required=True, localize=True)
    rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'}))
    assert 'required' not in rendered


def test_custom_id():
    f = I18nFormField(widget=I18nTextInput, required=True, localize=True)
    rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'}), attrs={'id': 'bla'})
    assert 'id="bla_0"' in rendered
    assert 'id="bla_1"' in rendered


def test_widget_enabled_locales():
    f = I18nFormField(widget=I18nTextInput, required=False)
    f.widget.enabled_locales = ['de', 'fr']
    rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'}))

    tree = html5parser.fromstring(rendered)
    assert tree[0].attrib == {
        'lang': 'de', 'name': 'foo_0', 'type': 'text', 'value': 'Hallo'
    }
    assert tree[1].attrib == {
        'lang': 'fr', 'name': 'foo_2', 'type': 'text'
    }


def test_widget_decompress_naive():
    f = I18nFormField(widget=I18nTextInput, required=False)
    assert f.widget.decompress('Foo') == [
        None, 'Foo', None
    ]


def test_widget_decompress_missing():
    f = I18nFormField(widget=I18nTextInput, required=False)
    assert f.widget.decompress({'de': 'Hallo', 'en': 'Hello'}) == [
        'Hallo', 'Hello', None
    ]


def test_widget_decompress_all_enabled_missing():
    f = I18nFormField(widget=I18nTextInput, required=False)
    f.widget.enabled_locales = ['de', 'fr']
    assert f.widget.decompress({'en': 'Hello'}) == [
        None, 'Hello', 'Hello'
    ]


def test_widget_decompress_first_two_enabled_not_filled():
    f = I18nFormField(widget=I18nTextInput, required=False)
    f.widget.enabled_locales = ['de', 'en', 'fr']
    assert f.widget.decompress({'fr': 'Bonjour'}) == [
        None, None, 'Bonjour'
    ]