from django.conf import settings
from django.core.exceptions import ValidationError
from django.forms.formsets import formset_factory
from django.template import Context, Template
from django.test import TestCase
from django.utils.translation import ugettext_lazy as _

import floppyforms as forms

from .base import InvalidVariable


def render(template, context=None):
    if context is None:
        context = {}
    c = Context(context)
    t = Template('{% load floppyforms %}' + template)
    return t.render(c)


class HiddenForm(forms.Form):
    hide = forms.CharField(widget=forms.HiddenInput())


class OneFieldForm(forms.Form):
    text = forms.CharField()

    def clean(self):
        if self.errors:
            raise ValidationError(u'Please correct the errors below.')


class ShortForm(forms.Form):
    name = forms.CharField(label=_(u'Your first name?'))
    age = forms.IntegerField(required=False)
    metadata = forms.CharField(required=False, widget=forms.HiddenInput)


class RegistrationForm(forms.Form):
    honeypot = forms.CharField(required=False, widget=forms.HiddenInput)
    firstname = forms.CharField(label=_(u'Your first name?'))
    lastname = forms.CharField(label=_(u'Your last name:'))
    username = forms.CharField(max_length=30)
    password = forms.CharField(
        widget=forms.PasswordInput,
        help_text=_(u'Make sure to use a secure password.'),
    )
    password2 = forms.CharField(label=_(u'Retype password'), widget=forms.PasswordInput)
    age = forms.IntegerField(required=False)

    def clean_honeypot(self):
        if self.cleaned_data.get('honeypot'):
            raise ValidationError(u'Haha, you trapped into the honeypot.')
        return self.cleaned_data['honeypot']

    def clean(self):
        if self.errors:
            raise ValidationError(u'Please correct the errors below.')


class PLayoutTests(TestCase):
    def test_default_layout_is_same_as_p_layout(self):
        form = RegistrationForm()
        default = render('{% form form %}', {'form': form})
        layout = render('{% form form using "floppyforms/layouts/table.html" %}', {'form': form})
        self.assertEqual(default, layout)

    def test_layout(self):
        form = RegistrationForm()
        with self.assertTemplateUsed('floppyforms/layouts/p.html'):
            with self.assertTemplateUsed('floppyforms/rows/p.html'):
                layout = render('{% form form using "floppyforms/layouts/p.html" %}', {'form': form})
        self.maxDiff = None
        self.assertHTMLEqual(layout, """
        <p><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required />
        </p>
        <p><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required />
        </p>
        <p><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required />
        </p>
        <p><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
         <span class="helptext">Make sure to use a secure password.</span></p>
        <p><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required />
        </p>
        <p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
        <input type="hidden" name="honeypot" id="id_honeypot" />
        </p>
        """)

    def test_layout_with_errors(self):
        form = RegistrationForm({'non_field_errors': True})
        layout = render('{% form form using "floppyforms/layouts/p.html" %}', {'form': form})
        self.maxDiff = None
        self.assertHTMLEqual(layout, """
        <ul class="errorlist"><li>Please correct the errors below.</li></ul>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p>
            <label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
            <span class="helptext">Make sure to use a secure password.</span>
        </p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required /></p>
        <p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" /></p>
        """)

        form = RegistrationForm({'non_field_errors': True, 'honeypot': 1})
        layout = render('{% form form using "floppyforms/layouts/p.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <ul class="errorlist">
            <li>Please correct the errors below.</li>
            <li>Haha, you trapped into the honeypot.</li>
        </ul>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required /></p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p>
            <label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
            <span class="helptext">Make sure to use a secure password.</span>
        </p>
        <ul class="errorlist"><li>This field is required.</li></ul>
        <p><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required /></p>
        <p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" value="1" /></p>
        """)

    def test_layout_with_custom_label(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/p.html" with label="Custom label" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <p><label for="id_text">Custom label:</label> <input type="text" name="text" id="id_text" required /></p>
        """)

    def test_layout_with_custom_help_text(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/p.html" with help_text="Would you mind entering text here?" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <p>
            <label for="id_text">Text:</label> <input type="text" name="text" id="id_text" required />
            <span class="helptext">Would you mind entering text here?</span>
        </p>
        """)

    def test_hidden_only_fields(self):
        form = HiddenForm()
        rendered = render("""{% form form using "floppyforms/layouts/p.html" %}""", {'form': form})
        self.assertHTMLEqual(rendered, """
        <input type="hidden" name="hide" id="id_hide" required>
        """)

    def test_formsets_with_hidden_fields(self):
        ShortFormset = formset_factory(form=ShortForm, extra=1)
        formset = ShortFormset(initial=[{'name': 'Johnson', 'age': 23, 'metadata': 'Hidden details'}])
        rendered = render("""{% form formset using "floppyforms/layouts/p.html" %}""", {'formset': formset})
        self.assertHTMLEqual(rendered, """
        <p>
            <label for="id_form-0-name">Your first name?</label>
            <input type="text" name="form-0-name" value="Johnson" required id="id_form-0-name">
        </p>
        <p>
            <label for="id_form-0-age">Age:</label>
            <input type="number" name="form-0-age" value="23" id="id_form-0-age">
            <input type="hidden" name="form-0-metadata" value="Hidden details" id="id_form-0-metadata">
        </p>
        <p>
            <label for="id_form-1-name">Your first name?</label>
            <input type="text" name="form-1-name" required id="id_form-1-name">
        </p>
        <p>
            <label for="id_form-1-age">Age:</label>
            <input type="number" name="form-1-age" id="id_form-1-age">
            <input type="hidden" name="form-1-metadata" id="id_form-1-metadata">
        </p>
        """)


class TableLayoutTests(TestCase):
    def test_layout(self):
        form = RegistrationForm()
        with self.assertTemplateUsed('floppyforms/layouts/table.html'):
            with self.assertTemplateUsed('floppyforms/rows/tr.html'):
                layout = render('{% form form using "floppyforms/layouts/table.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <tr><th><label for="id_firstname">Your first name?</label></th><td><input type="text" name="firstname" id="id_firstname" required /></td></tr>
        <tr><th><label for="id_lastname">Your last name:</label></th><td><input type="text" name="lastname" id="id_lastname" required /></td></tr>
        <tr><th><label for="id_username">Username:</label></th><td><input type="text" name="username" id="id_username" maxlength="30" required /></td></tr>
        <tr><th>
            <label for="id_password">Password:</label></th><td><input type="password" name="password" id="id_password" required />
            <br /><span class="helptext">Make sure to use a secure password.</span></td></tr>
        <tr><th><label for="id_password2">Retype password:</label></th><td><input type="password" name="password2" id="id_password2" required /></td></tr>
        <tr><th>
            <label for="id_age">Age:</label></th><td><input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" />
        </td></tr>
        """)

    def test_layout_with_errors(self):
        form = RegistrationForm({'non_field_errors': True})
        layout = render('{% form form using "floppyforms/layouts/table.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <tr><td colspan="2"><ul class="errorlist"><li>Please correct the errors below.</li></ul></td></tr>
        <tr><th><label for="id_firstname">Your first name?</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="firstname" id="id_firstname" required /></td></tr>
        <tr><th><label for="id_lastname">Your last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="lastname" id="id_lastname" required /></td></tr>
        <tr><th><label for="id_username">Username:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" id="id_username" maxlength="30" required /></td></tr>
        <tr>
            <th><label for="id_password">Password:</label></th>
            <td>
                <ul class="errorlist"><li>This field is required.</li></ul>
                <input type="password" name="password" id="id_password" required />
                <br /><span class="helptext">Make sure to use a secure password.</span>
            </td>
        </tr>
        <tr>
            <th><label for="id_password2">Retype password:</label></th>
            <td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" id="id_password2" required /></td>
        </tr>
        <tr><th><label for="id_age">Age:</label></th><td><input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" /></td></tr>
        """)

        form = RegistrationForm({'non_field_errors': True, 'honeypot': 1})
        layout = render('{% form form using "floppyforms/layouts/table.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <tr><td colspan="2"><ul class="errorlist"><li>Please correct the errors below.</li><li>Haha, you trapped into the honeypot.</li></ul></td></tr>
        <tr><th><label for="id_firstname">Your first name?</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="firstname" id="id_firstname" required /></td></tr>
        <tr><th><label for="id_lastname">Your last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="lastname" id="id_lastname" required /></td></tr>
        <tr><th><label for="id_username">Username:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" id="id_username" maxlength="30" required /></td></tr>
        <tr><th><label for="id_password">Password:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password" id="id_password" required /><br /><span class="helptext">Make sure to use a secure password.</span></td></tr>
        <tr><th><label for="id_password2">Retype password:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" id="id_password2" required /></td></tr>
        <tr><th><label for="id_age">Age:</label></th><td><input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" value="1" id="id_honeypot" /></td></tr>
        """)

    def test_layout_with_custom_label(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/tr.html" with label="Custom label" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <tr><th><label for="id_text">Custom label:</label></th><td><input type="text" name="text" id="id_text" required /></td></tr>
        """)

    def test_layout_with_custom_help_text(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/tr.html" with help_text="Would you mind entering text here?" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <tr><th>
            <label for="id_text">Text:</label>
        </th><td>
            <input type="text" name="text" id="id_text" required />
            <br /><span class="helptext">Would you mind entering text here?</span>
        </td></tr>
        """)

    def test_hidden_only_fields(self):
        form = HiddenForm()
        rendered = render("""{% form form using "floppyforms/layouts/table.html" %}""", {'form': form})
        self.assertHTMLEqual(rendered, """
        <input type="hidden" name="hide" id="id_hide" required>
        """)

    def test_formsets_with_hidden_fields(self):
        ShortFormset = formset_factory(form=ShortForm, extra=1)
        formset = ShortFormset(initial=[{'name': 'Johnson', 'age': 23, 'metadata': 'Hidden details'}])
        rendered = render("""{% form formset using "floppyforms/layouts/table.html" %}""", {'formset': formset})
        self.assertHTMLEqual(rendered, """
        <tr>
            <th><label for="id_form-0-name">Your first name?</label></th>
            <td>
                <input type="text" name="form-0-name" value="Johnson" required id="id_form-0-name">
            </td>
        </tr>
        <tr>
            <th><label for="id_form-0-age">Age:</label></th>
            <td>
                <input type="number" name="form-0-age" value="23" id="id_form-0-age">
                <input type="hidden" name="form-0-metadata" value="Hidden details" id="id_form-0-metadata">
            </td>
        </tr>
        <tr>
            <th><label for="id_form-1-name">Your first name?</label></th>
            <td>
                <input type="text" name="form-1-name" required id="id_form-1-name">
            </td>
        </tr>
        <tr>
            <th><label for="id_form-1-age">Age:</label></th>
            <td>
                <input type="number" name="form-1-age" id="id_form-1-age">
                <input type="hidden" name="form-1-metadata" id="id_form-1-metadata">
            </td>
        </tr>
        """)


class UlLayoutTests(TestCase):
    def test_layout(self):
        form = RegistrationForm()
        with self.assertTemplateUsed('floppyforms/layouts/ul.html'):
            with self.assertTemplateUsed('floppyforms/rows/li.html'):
                layout = render('{% form form using "floppyforms/layouts/ul.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <li><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required /></li>
        <li><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required /></li>
        <li><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required /></li>
        <li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
            <span class="helptext">Make sure to use a secure password.</span></li>
        <li><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required /></li>
        <li><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" /></li>
        """)

    def test_layout_with_errors(self):
        form = RegistrationForm({'non_field_errors': True})
        layout = render('{% form form using "floppyforms/layouts/ul.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <li><ul class="errorlist"><li>Please correct the errors below.</li></ul></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
            <span class="helptext">Make sure to use a secure password.</span></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required /></li>
        <li><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" id="id_honeypot" /></li>
        """)

        form = RegistrationForm({'non_field_errors': True, 'honeypot': 1})
        layout = render('{% form form using "floppyforms/layouts/ul.html" %}', {'form': form})
        self.assertHTMLEqual(layout, """
        <li><ul class="errorlist"><li>Please correct the errors below.</li><li>Haha, you trapped into the honeypot.</li></ul></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_firstname">Your first name?</label> <input type="text" name="firstname" id="id_firstname" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_lastname">Your last name:</label> <input type="text" name="lastname" id="id_lastname" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_username">Username:</label> <input type="text" name="username" id="id_username" maxlength="30" required /></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" required />
            <span class="helptext">Make sure to use a secure password.</span></li>
        <li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_password2">Retype password:</label> <input type="password" name="password2" id="id_password2" required /></li>
        <li><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" />
            <input type="hidden" name="honeypot" value="1" id="id_honeypot" /></li>
        """)

    def test_layout_with_custom_label(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/li.html" with label="Custom label" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <li><label for="id_text">Custom label:</label><input type="text" name="text" id="id_text" required /></li>
        """)

    def test_layout_with_custom_help_text(self):
        form = OneFieldForm()
        layout = render("""
            {% form form using %}
                {% formrow form.text using "floppyforms/rows/li.html" with help_text="Would you mind entering text here?" %}
            {% endform %}
        """, {'form': form})
        self.assertHTMLEqual(layout, """
        <li>
            <label for="id_text">Text:</label>
            <input type="text" name="text" id="id_text" required />
            <span class="helptext">Would you mind entering text here?</span>
        </li>
        """)

    def test_hidden_only_fields(self):
        form = HiddenForm()
        rendered = render("""{% form form using "floppyforms/layouts/ul.html" %}""", {'form': form})
        self.assertHTMLEqual(rendered, """
        <input type="hidden" name="hide" id="id_hide" required>
        """)

    def test_formsets_with_hidden_fields(self):
        ShortFormset = formset_factory(form=ShortForm, extra=1)
        formset = ShortFormset(initial=[{'name': 'Johnson', 'age': 23, 'metadata': 'Hidden details'}])
        rendered = render("""{% form formset using "floppyforms/layouts/ul.html" %}""", {'formset': formset})
        self.assertHTMLEqual(rendered, """
        <li>
            <label for="id_form-0-name">Your first name?</label>
            <input type="text" name="form-0-name" value="Johnson" required id="id_form-0-name">
        </li>
        <li>
            <label for="id_form-0-age">Age:</label>
            <input type="number" name="form-0-age" value="23" id="id_form-0-age">
            <input type="hidden" name="form-0-metadata" value="Hidden details" id="id_form-0-metadata">

        </li>
        <li>
            <label for="id_form-1-name">Your first name?</label>
            <input type="text" name="form-1-name" required id="id_form-1-name">
        </li>
        <li>
            <label for="id_form-1-age">Age:</label>
            <input type="number" name="form-1-age" id="id_form-1-age">
            <input type="hidden" name="form-1-metadata" id="id_form-1-metadata">
        </li>
        """)


class TemplateStringIfInvalidTests(TestCase):
    '''
    Regression tests for issue #37.
    '''
    def setUp(self):
        self.original_TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID

    def tearDown(self):
        settings.TEMPLATE_STRING_IF_INVALID = self.original_TEMPLATE_STRING_IF_INVALID

    def test_none(self):
        settings.TEMPLATE_STRING_IF_INVALID = None

        layout = OneFieldForm().as_p()
        self.assertHTMLEqual(layout, """
        <p><label for="id_text">Text:</label> <input type="text" name="text" id="id_text" required /></p>
        """)

    def test_non_empty(self):
        settings.TEMPLATE_STRING_IF_INVALID = InvalidVariable(u'INVALID')

        layout = OneFieldForm().as_p()
        self.assertHTMLEqual(layout, """
        <p><label for="id_text">Text:</label> <input type="text" name="text" id="id_text" required /></p>
        """)
