from collections import Counter

from django import forms
from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList
from django.utils.translation import ugettext_lazy as _
from django.utils.text import mark_safe

from wagtail.wagtailcore.blocks import StaticBlock
from opentech.apply.stream_forms.blocks import FormFieldsBlock, FormFieldBlock

from opentech.apply.categories.blocks import CategoryQuestionBlock


def find_duplicates(items):
    counted = Counter(items)
    duplicates = [
        name for name, count in counted.items() if count > 1
    ]
    return duplicates


class CustomFormFieldsBlock(FormFieldsBlock):
    category = CategoryQuestionBlock(group=_('Custom'))

    def __init__(self, *args, **kwargs):
        child_blocks = [(block.name, block(group=_('Required'))) for block in MustIncludeFieldBlock.__subclasses__()]
        super().__init__(child_blocks, *args, **kwargs)

    def clean(self, value):
        try:
            value = super().clean(value)
        except ValidationError as e:
            error_dict = e.params
        else:
            error_dict = dict()

        block_types = [block.block_type for block in value]
        missing = set(REQUIRED_BLOCK_NAMES) - set(block_types)

        duplicates = [
            name for name in find_duplicates(block_types)
            if name in REQUIRED_BLOCK_NAMES
        ]

        all_errors = list()
        if missing:
            all_errors.append(
                'You are missing the following required fields: {}'.format(', '.join(missing).title())
            )

        if duplicates:
            all_errors.append(
                'You have duplicates of the following required fields: {}'.format(', '.join(duplicates).title())
            )
            for i, block_name in enumerate(block_types):
                if block_name in duplicates:
                    self.add_error_to_child(error_dict, i, 'info', 'Duplicate field')

        if all_errors or error_dict:
            error_dict['__all__'] = all_errors
            raise ValidationError('Error', params=error_dict)

        return value

    def add_error_to_child(self, errors, child_number, field, message):
        new_error = ErrorList([message])
        try:
            errors[child_number].data[0].params[field] = new_error
        except KeyError:
            errors[child_number] = ErrorList(
                [ValidationError('Error', params={field: new_error})]
            )


class MustIncludeStatic(StaticBlock):
    """Helper block which displays additional information about the must include block and
    helps display the error in a noticeable way.
    """
    def __init__(self, *args, description='', **kwargs):
        self.description = description
        super().__init__(*args, **kwargs)

    class Meta:
        admin_text = 'Much be included in the form only once.'

    def render_form(self, *args, **kwargs):
        errors = kwargs.pop('errors')
        if errors:
            # Pretend the error is a readonly input so that we get nice formatting
            # Issue discussed here: https://github.com/wagtail/wagtail/issues/4122
            error_message = '<div class="error"><input readonly placeholder="{}"></div>'.format(errors[0])
        else:
            error_message = ''
        form = super().render_form(*args, **kwargs)
        form = '<br>'.join([self.description, form]) + error_message
        return mark_safe(form)

    def deconstruct(self):
        return ('wagtail.wagtailcore.blocks.static_block.StaticBlock', (), {})


class MustIncludeFieldBlock(FormFieldBlock):
    """Any block inheriting from this will need to be included in the application forms
    This data will also be available to query on the submission object
    """
    def __init__(self, *args, **kwargs):
        info_name = f'{self.name.title()} Field'
        child_blocks = [('info', MustIncludeStatic(label=info_name, description=self.description))]
        super().__init__(child_blocks, *args, **kwargs)

    def get_field_kwargs(self, struct_value):
        kwargs = super().get_field_kwargs(struct_value)
        kwargs['required'] = True
        return kwargs


class TitleBlock(MustIncludeFieldBlock):
    name = 'title'
    description = 'The title of the project'


class ValueBlock(MustIncludeFieldBlock):
    name = 'value'
    description = 'The value of the project'
    widget = forms.NumberInput


REQUIRED_BLOCK_NAMES = [block.name for block in MustIncludeFieldBlock.__subclasses__()]