# -*- coding: utf-8 -*-

from random import shuffle
from copy import deepcopy
from django.conf import settings
from django.core.exceptions import ValidationError

from events.media.models import Image
from events.rest_framework_contrib.fields import ImageField
from events.surveyme.fields.base.fields import PaymentField


class BaseField(object):
    widget = None
    tag = None
    tag_type = None

    def __init__(self, question, **kwargs):
        self.question = question
        self.kwargs = kwargs
        self.help_text = self.get_param('param_help_text')
        self.label = self.get_param('label')
        self.is_required = self.get_param('param_is_required')
        self.is_hidden = self.get_param('param_is_hidden')
        self.initial = self.get_param('initial')
        self.position = kwargs.get('position', 1)
        self.page = kwargs.get('page', 1)
        self.hints = kwargs.get('hints', [])
        self.errors = kwargs.get('errors', [])

    def get_param(self, param_name, default=None):
        return self.kwargs.get(param_name, getattr(self.question, param_name, default))

    def get_tag_type(self):
        if self.get_param('param_is_hidden', False):
            return 'hidden'
        return self.tag_type

    def get_tags(self):
        attrs = {
            'name': self.question.param_slug,
            'id': 'id_' + self.question.param_slug,
        }
        if self.initial is not None:
            attrs['value'] = str(self.initial)
        return [{
            'tag': self.tag,
            'attrs': attrs,
        }]

    def get_other_data(self):
        return {
            'widget': None,
        }

    def get_hints(self):
        data_source = self.get_param('param_hint_data_source')
        if data_source:
            self.question.param_hint_data_source = data_source
            return self.question.get_hint_data(
                user=None,
                questions_dict={},
            )
        return []

    def get_integer(self, field_name, kwargs, raise_on_error=True):
        result = kwargs.get(field_name)
        if result is not None:
            try:
                return int(result)
            except ValueError as exc:
                if raise_on_error:
                    raise ValidationError({field_name: exc})

    def get_image_representation(self, image):
        data = None
        if image:
            links = ImageField(sizes=settings.IMAGE_SIZES)
            data = {
                'links': links.to_representation(image),
            }
        return data

    def get_image(self, kwargs):
        if 'label_image' in kwargs:
            label_image_id = self.get_integer('label_image', kwargs)
            try:
                label_image = Image.objects.get(pk=label_image_id)
            except Image.DoesNotExist:
                label_image = None
        else:
            label_image = self.question.label_image

        if label_image:
            return label_image.image

    def get_label_image(self, kwargs):
        return self.get_image_representation(self.get_image(kwargs))

    def as_dict(self):
        return {
            'name': self.question.param_slug,
            'errors': self.errors,
            'help_text': self.help_text,
            'widget': self.widget,
            'label': self.label,
            'label_image': self.get_label_image(self.kwargs),
            'is_required': self.is_required,
            'tags': self.get_tags(),
            'other_data': self.get_other_data(),
            'is_hidden': self.is_hidden,
            'page': self.page,
            'position': self.position,
            'hints': self.get_hints(),
            'group_slug': None,
        }


class AnswerShortTextField(BaseField):
    answer_type = 'answer_short_text'
    widget = 'TextInput'
    tag = 'input'
    tag_type = 'text'

    def get_tags(self):
        tags = super(AnswerShortTextField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
            'min': str(self.get_param('param_min')),
            'max': str(self.get_param('param_max')),
            'maxlength': '255',
        })
        return tags


class AnswerLongTextField(BaseField):
    answer_type = 'answer_long_text'
    widget = 'Textarea'
    tag = 'textarea'

    def get_tags(self):
        tags = super(AnswerLongTextField, self).get_tags()
        tags[0].update({
            'content': '\n',
        })
        tags[0]['attrs'].update({
            'min': str(self.get_param('param_min')),
            'max': str(self.get_param('param_max')),
            'rows': '10',
            'cols': '40',
        })
        return tags


class AnswerChoicesField(BaseField):
    answer_type = 'answer_choices'

    @property
    def widget(self):
        return self.question.param_widget

    def get_item(self, choice, additional_fields=None):
        if not additional_fields:
            additional_fields = []
        item = {
            'text': choice.get('label'),
            'label_image': self.get_label_image(choice),
        }
        for field in additional_fields:
            field_data = choice.get(field)
            if field_data:
                item[field] = field_data
        return item

    def get_items(self):
        choices = self.kwargs.get('choices') or []
        additional_fields = ('id', 'slug', 'position', )
        matrix_choices = self.kwargs.get('matrix_titles') or []
        if matrix_choices:
            choices = matrix_choices
            additional_fields = additional_fields + ('type', )

        items = [
            self.get_item(choice, additional_fields)
            for choice in choices
        ]
        modify_choices = self.get_param('param_modify_choices')
        if modify_choices == 'sort':
            items = list(sorted(items, key=lambda item: item['text']))
        elif modify_choices == 'shuffle':
            shuffle(items)
        return items

    def get_data_source_params(self):
        # data_source_params = self.get_param('param_data_source_params') # FORMS-2494
        data_source_params = self.kwargs.get('param_data_source_params', {})
        if not data_source_params.get('filters', []):
            data_source_params = self.question.param_data_source_params or {}
        return data_source_params

    def check_items_in_db(self):
        return 'choices' not in self.kwargs and 'matrix_titles' not in self.kwargs

    def as_dict(self):
        result = super(AnswerChoicesField, self).as_dict()

        data_source = self.get_param('param_data_source')

        result.update({
            'is_allow_multiple_choice': self.get_param('param_is_allow_multiple_choice'),
            'is_disabled_init_item': self.get_param('param_is_disabled_init_item'),
            'suggest_choices': self.get_param('param_suggest_choices'),
            'type': 'choices',
            'value': None,
            'show_conditions': None,
            'data_source': {},
        })
        if data_source:
            data_source_params = self.get_data_source_params()

            self.question.param_data_source = data_source
            self.question.param_data_source_params = data_source_params
            result['data_source'].update(
                self.question.get_data_source_data(
                    user=None,
                    questions_dict={},
                )
            )
            if not self.check_items_in_db():
                result['data_source']['items'] = self.get_items()
        else:
            result['data_source'].update({
                'items': self.get_items(),
                'content_type': None,
            })
        del result['hints']
        del result['tags']
        del result['other_data']
        return result


class AnswerNumberField(BaseField):
    answer_type = 'answer_number'
    widget = 'NumberInput'
    tag = 'input'
    tag_type = 'number'

    def get_tags(self):
        tags = super(AnswerNumberField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
            'min': str(self.get_param('param_min')),
            'max': str(self.get_param('param_max') or "2147483647"),
        })
        return tags

    def get_other_data(self):
        return {
            'widget': 'number',
        }


class AnswerUrlField(BaseField):
    answer_type = 'answer_url'
    widget = 'URLInput'
    tag = 'input'
    tag_type = 'url'

    def get_tags(self):
        tags = super(AnswerUrlField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
            'maxlength': '1024',
        })
        return tags


class AnswerEmailField(BaseField):
    answer_type = 'answer_non_profile_email'
    widget = 'EmailInput'
    tag = 'input'
    tag_type = 'email'

    def get_tags(self):
        tags = super(AnswerEmailField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
            'maxlength': '254',
        })
        return tags


class AnswerNameField(BaseField):
    answer_type = 'answer_name'
    widget = 'TextInput'
    tag = 'input'
    tag_type = 'text'

    def get_tags(self):
        tags = super(AnswerNameField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
        })
        return tags


class AnswerSurnameField(AnswerNameField):
    answer_type = 'answer_surname'


class AnswerPhoneField(BaseField):
    answer_type = 'answer_phone'
    widget = 'TextInput'
    tag = 'input'
    tag_type = 'text'

    def get_tags(self):
        tags = super(AnswerPhoneField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
        })
        return tags


class AnswerStatementField(BaseField):
    answer_type = 'answer_statement'
    widget = 'EmptyWidget'

    def as_dict(self):
        result = super(AnswerStatementField, self).as_dict()
        result['is_required'] = False
        return result

    def get_tags(self):
        return []

    def get_other_data(self):
        return {
            'widget': 'statement',
            'is_section_header': False,
        }


class AnswerBooleanField(BaseField):
    answer_type = 'answer_boolean'
    widget = 'CheckboxInput'
    tag = 'input'
    tag_type = 'checkbox'

    def get_tags(self):
        tags = super(AnswerBooleanField, self).get_tags()
        tags[0].update({
            'label': '',
        })
        tags[0]['attrs'].update({
            'type': self.tag_type,
        })
        return tags

    def get_other_data(self):
        return {
            'widget': 'checkbox',
        }


class AnswerDateField(BaseField):
    answer_type = 'answer_date'
    tag = 'input'
    tag_type = 'text'

    @property
    def widget(self):
        if self.is_daterange():
            return 'DateRangeFieldInput'
        else:
            return 'DateInput'

    def get_tag_type(self):
        if self.is_daterange():
            return self.tag_type
        else:
            return super(AnswerDateField, self).get_tag_type()

    def as_dict(self):
        result = super(AnswerDateField, self).as_dict()
        if self.is_daterange():
            result['is_hidden'] = False
        return result

    def get_tags(self):
        tags = super(AnswerDateField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
        })
        if self.is_daterange():
            tags.append(deepcopy(tags[0]))
            for i, tag in enumerate(tags):
                tag['attrs']['name'] = '%s_%s' % (tag['attrs']['name'], i)
                tag['attrs']['id'] = '%s_%s' % (tag['attrs']['id'], i)
                if self.is_required:
                    tag['attrs']['required'] = ''
        return tags

    def is_daterange(self):
        return self.get_param('param_date_field_type') == 'daterange'

    def get_date(self, value):
        return value.split('T')[0]

    def get_allowed_range(self):
        allowed_range = {}
        min_date = self.get_param('param_date_field_min')
        if min_date:
            allowed_range['from'] = self.get_date(min_date)
        max_date = self.get_param('param_date_field_max')
        if max_date:
            allowed_range['to'] = self.get_date(max_date)
        return allowed_range

    def get_other_data(self):
        result = {}
        if self.is_daterange():
            result['widget'] = 'date_range'
        else:
            result['widget'] = 'date'
        allowed_range = self.get_allowed_range()
        if allowed_range:
            result['allowed_range'] = allowed_range
        return result


class AnswerCountOfTicketsField(BaseField):
    answer_type = 'answer_count_of_tickets'
    widget = 'TicketsQuantityInput'
    tag = 'input'
    tag_type = 'number'

    def get_tickets_info(self):
        min_tickets = self.get_param('param_min')
        if self.get_param('param_is_required'):
            if not min_tickets:
                min_tickets = 1
        tickets_info = self.question.survey.tickets_info
        return {
            'currency': tickets_info.currency,
            'price': str(tickets_info.price),
            'min': str(min_tickets),
            'max': str(self.get_param('param_max')),
        }

    def get_tags(self):
        tags = super(AnswerCountOfTicketsField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
        })
        tags[0]['attrs'].update(self.get_tickets_info())
        return tags

    def get_other_data(self):
        return {
            'widget': 'tickets',
        }


class AnswerFilesField(BaseField):
    answer_type = 'answer_files'
    widget = 'MultiFileInput'
    tag = 'input'
    tag_type = 'file'

    def get_tags(self):
        tags = super(AnswerFilesField, self).get_tags()
        tags[0]['attrs'].update({
            'type': self.get_tag_type(),
            'multiple': 'true',
        })
        return tags

    def get_other_data(self):
        return {
            'widget': 'multifile',
            'max_file_size': self.get_param('param_max_file_size'),
            'max_files_count': self.get_param('param_max_files_count'),
        }


class AnswerPayment(BaseField):
    answer_type = 'answer_payment'
    widget = 'PaymentWidget'
    tag = 'input'
    tag_type = 'text'
    field = PaymentField

    def get_other_data(self):
        param_payment = self.get_param('param_payment', {}) or {}
        return {
            'is_fixed': bool(param_payment.get('is_fixed')),
            'min': self.field.min_amount,
            'max': self.field.max_amount,
            'widget': None,
        }


ANSWER_TYPE_FIELDS = [
    AnswerShortTextField,
    AnswerLongTextField,
    AnswerChoicesField,
    AnswerNumberField,
    AnswerUrlField,
    AnswerEmailField,
    AnswerNameField,
    AnswerSurnameField,
    AnswerPhoneField,
    AnswerStatementField,
    AnswerBooleanField,
    AnswerDateField,
    AnswerCountOfTicketsField,
    AnswerFilesField,
    AnswerPayment,
]


ANSWER_TYPE_FIELD_BY_SLUG = {
    class_name.answer_type: class_name
    for class_name in ANSWER_TYPE_FIELDS
}


def get_preview_class_type(answer_type_slug):
    return ANSWER_TYPE_FIELD_BY_SLUG.get(answer_type_slug, AnswerShortTextField)
