from django import forms
from django.db.models import Q
from django.utils import html
from django.utils.six import text_type

from misago.core.utils import format_plaintext_for_html


__all__ = [
    'ProfileField',
    'TextProfileField',
    'UrlProfileField',
    'TextareaProfileField',
    'ChoiceProfileField',
]


class ProfileField(object):
    """
    Basic profile field
    """
    fieldname = None
    label = None
    help_text = None
    readonly = False

    def is_editable(self, request, user):
        return not self.readonly

    def get_label(self, user):
        if not self.label:
            raise NotImplementedError(
                "profile field class has to define label "
                "attribute or get_label(user) method"
            )
        return self.label

    def get_help_text(self, user):
        return self.help_text

    def get_form_field(self, request, user):
        return None

    def get_form_field_json(self, request, user):
        return {
            'fieldname': self.fieldname,
            'label': self.get_label(user),
            'help_text': self.get_help_text(user),
            'initial': user.profile_fields.get(self.fieldname, ''),
            'input': self.get_input_json(request, user),
        }

    def get_input_json(self, request, user):
        return None

    def clean(self, request, user, data):
        return data

    def get_display_data(self, request, user):
        value = user.profile_fields.get(self.fieldname, '')
        if not self.readonly and not len(value):
            return None

        data = self.get_value_display_data(request, user, value)
        if not data:
            return None

        data.update({
            'fieldname': self.fieldname,
            'name': text_type(self.get_label(user)),
        })

        return data

    def get_value_display_data(self, request, user, value):
        return {
            'text': value
        }

    def search_users(self, criteria):
        if self.readonly:
            return None

        return Q(**{
            'profile_fields__{}__contains'.format(self.fieldname): criteria
        })


class ChoiceProfileField(ProfileField):
    choices = None

    def get_choices(self, user=None):
        if not self.choices:
            raise NotImplementedError(
                "profile field ChoiceProfileField has to define "
                "choices attribute or get_choices(user) method"
            )
        return self.choices

    def get_form_field(self, request, user):
        return forms.ChoiceField(
            label=self.get_label(user),
            help_text=self.get_help_text(user),
            initial=user.profile_fields.get(self.fieldname),
            choices=self.get_choices(user),
            disabled=self.readonly,
            required=False,
        )

    def get_input_json(self, request, user):
        choices = []
        for key, choice in self.get_choices():
            choices.append({
                'value': key,
                'label': choice,
            })

        return {
            'type': 'select',
            'choices': choices,
        }

    def get_value_display_data(self, request, user, value):
        for key, name in self.get_choices():
            if key == value:
                return {
                    'text': text_type(name),
                }
        return None

    def search_users(self, criteria):
        """custom search implementation for choice fields"""
        q_obj = Q(**{
            'profile_fields__{}__contains'.format(self.fieldname): criteria
        })

        for key, choice in self.get_choices():
            if key and criteria.lower() in text_type(choice).lower():
                q_obj = q_obj | Q(**{
                    'profile_fields__{}'.format(self.fieldname): key
                })

        return q_obj


class TextProfileField(ProfileField):
    def get_form_field(self, request, user):
        return forms.CharField(
            label=self.get_label(user),
            help_text=self.get_help_text(user),
            initial=user.profile_fields.get(self.fieldname),
            max_length=250,
            disabled=self.readonly,
            required=False,
        )

    def get_input_json(self, request, user):
        return {
            'type': 'text',
        }


class TextareaProfileField(ProfileField):
    def get_form_field(self, request, user):
        return forms.CharField(
            label=self.get_label(user),
            help_text=self.get_help_text(user),
            initial=user.profile_fields.get(self.fieldname),
            max_length=500,
            widget=forms.Textarea(
                attrs={'rows': 4},
            ),
            disabled=self.readonly,
            required=False,
        )

    def get_input_json(self, request, user):
        return {
            'type': 'textarea',
        }

    def get_value_display_data(self, request, user, value):
        return {
            'html': html.linebreaks(html.escape(value)),
        }


class UrlifiedTextareaProfileField(TextareaProfileField):
    def get_value_display_data(self, request, user, value):
        return {
            'html': format_plaintext_for_html(value),
        }


class UrlProfileField(TextProfileField):
    def get_form_field(self, request, user):
        return forms.URLField(
            label=self.get_label(user),
            help_text=self.get_help_text(user),
            initial=user.profile_fields.get(self.fieldname),
            max_length=250,
            disabled=self.readonly,
            required=False,
        )

    def get_value_display_data(self, request, user, value):
        return {
            'text': value,
            'url': value,
        }