"""Tag serializers."""
from rest_framework import serializers
from rest_framework.fields import SerializerMethodField

from establishment.models import Establishment, EstablishmentType
from news.models import News
from news.models import NewsType
from tag import models
from utils.exceptions import BindingObjectNotFound, ObjectAlreadyAdded, RemovedBindingObjectNotFound
from utils.serializers import TranslatedField
from utils.models import get_default_locale, get_language, to_locale


def translate_obj(obj):
    if not obj.translation or not isinstance(obj.translation.text, dict):
        return None
    try:
        field = obj.translation.text
        return field.get(to_locale(get_language()),
                         field.get(get_default_locale(),
                                   next(iter(field.values()))))
    except StopIteration:
        return None


class TagBaseSerializer(serializers.ModelSerializer):
    """Serializer for model Tag."""

    def get_extra_kwargs(self):
        return super().get_extra_kwargs()

    index_name = serializers.CharField(source='value', read_only=True, allow_null=True)
    label_translated = serializers.SerializerMethodField(read_only=True, allow_null=True)

    def get_label_translated(self, obj):
        return translate_obj(obj)

    class Meta:
        """Meta class."""

        model = models.Tag
        fields = (
            'id',
            'label_translated',
            'index_name',
        )


class TagBackOfficeSerializer(TagBaseSerializer):
    """Serializer for Tag model for Back office users."""

    label = serializers.DictField(source='translation.text')

    class Meta(TagBaseSerializer.Meta):
        """Meta class."""

        fields = TagBaseSerializer.Meta.fields + (
            'label',
            'category'
        )


class TagCategoryProductSerializer(serializers.ModelSerializer):
    """SHORT Serializer for TagCategory"""

    def get_label_translated(self, obj):
        return translate_obj(obj)

    class Meta:
        """Meta class."""

        model = models.TagCategory
        fields = (
            'id',
            'index_name',
        )


class TagCategoryBaseSerializer(serializers.ModelSerializer):
    """Serializer for model TagCategory."""

    tags = TagBaseSerializer(many=True, allow_null=True)
    label_translated = serializers.SerializerMethodField(read_only=True, allow_null=True)

    class Meta:
        """Meta class."""

        model = models.TagCategory
        fields = (
            'id',
            'label_translated',
            'index_name',
            'tags',
        )

    def get_label_translated(self, obj):
        return translate_obj(obj)


class FiltersTagCategoryBaseSerializer(serializers.ModelSerializer):
    """Serializer for model TagCategory."""

    filters = SerializerMethodField()
    param_name = SerializerMethodField()
    type = SerializerMethodField()
    label_translated = serializers.SerializerMethodField(allow_null=True, read_only=True)

    class Meta:
        """Meta class."""

        model = models.TagCategory
        fields = (
            'id',
            'label_translated',
            'index_name',
            'param_name',
            'type',
            'filters',
        )

    def get_type(self, obj):
        return obj in ['open_now', ]

    def get_param_name(self, obj):
        if obj.index_name == 'wine-color':
            return 'wine_colors_id__in'
        return 'tags_id__in'

    def get_label_translated(self, obj):
        return translate_obj(obj)

    def get_fields(self, *args, **kwargs):
        fields = super(FiltersTagCategoryBaseSerializer, self).get_fields()

        if self.get_type(self):
            fields.pop('filters', None)
        else:
            fields.pop('type', None)

        return fields

    def get_filters(self, obj):
        query_params = dict(self.context['request'].query_params)

        params = {}
        if 'establishment_type' in query_params:
            params = {
                'establishments__isnull': False,
            }
        elif 'product_type' in query_params:
            params = {
                'products__isnull': False,
            }

        tags = obj.tags.filter(**params).distinct()
        return TagBaseSerializer(instance=tags, many=True, read_only=True).data


class TagCategoryShortSerializer(serializers.ModelSerializer):
    """Serializer for model TagCategory."""

    label_translated = serializers.SerializerMethodField(allow_null=True, read_only=True)
    value_type_display = serializers.CharField(source='get_value_type_display',
                                               read_only=True)

    def get_label_translated(self, obj):
        return translate_obj(obj)

    class Meta(TagCategoryBaseSerializer.Meta):
        """Meta class."""
        fields = [
            'label_translated',
            'value_type_display',
        ]


class TagCategoryBackOfficeDetailSerializer(TagCategoryBaseSerializer):
    """Tag Category detail serializer for back-office users."""

    country_translated = TranslatedField(source='country.name_translated')
    label = serializers.DictField(source='translation.text')

    class Meta(TagCategoryBaseSerializer.Meta):
        """Meta class."""

        fields = TagCategoryBaseSerializer.Meta.fields + (
            'label',
            'country',
            'country_translated',
        )


class TagBindObjectSerializer(serializers.Serializer):
    """Serializer for binding tag category and objects"""

    ESTABLISHMENT = 'establishment'
    NEWS = 'news'

    TYPE_CHOICES = (
        (ESTABLISHMENT, 'Establishment type'),
        (NEWS, 'News type'),
    )

    type = serializers.ChoiceField(TYPE_CHOICES)
    object_id = serializers.IntegerField()

    def validate(self, attrs):
        view = self.context.get('view')
        request = self.context.get('request')

        obj_type = attrs.get('type')
        obj_id = attrs.get('object_id')

        tag = view.get_object()
        attrs['tag'] = tag

        if obj_type == self.ESTABLISHMENT:
            establishment = Establishment.objects.filter(pk=obj_id).first()
            if not establishment:
                raise BindingObjectNotFound()
            if request.method == 'POST' and tag.establishments.filter(
                    pk=establishment.pk).exists():
                raise ObjectAlreadyAdded()
            if request.method == 'DELETE' and not tag.establishments.filter(
                    pk=establishment.pk).exists():
                raise RemovedBindingObjectNotFound()
            attrs['related_object'] = establishment
        elif obj_type == self.NEWS:
            news = News.objects.filter(pk=obj_id).first()
            if not news:
                raise BindingObjectNotFound()
            if request.method == 'POST' and tag.news.filter(pk=news.pk).exists():
                raise ObjectAlreadyAdded()
            if request.method == 'DELETE' and not tag.news.filter(
                    pk=news.pk).exists():
                raise RemovedBindingObjectNotFound()
            attrs['related_object'] = news
        return attrs


class TagCategoryBindObjectSerializer(serializers.Serializer):
    """Serializer for binding tag category and objects"""

    ESTABLISHMENT_TYPE = 'establishment_type'
    NEWS_TYPE = 'news_type'

    TYPE_CHOICES = (
        (ESTABLISHMENT_TYPE, 'Establishment type'),
        (NEWS_TYPE, 'News type'),
    )

    type = serializers.ChoiceField(TYPE_CHOICES)
    object_id = serializers.IntegerField()

    def validate(self, attrs):
        view = self.context.get('view')
        request = self.context.get('request')

        obj_type = attrs.get('type')
        obj_id = attrs.get('object_id')

        tag_category = view.get_object()
        attrs['tag_category'] = tag_category

        if obj_type == self.ESTABLISHMENT_TYPE:
            establishment_type = EstablishmentType.objects.filter(pk=obj_id). \
                first()
            if not establishment_type:
                raise BindingObjectNotFound()
            if request.method == 'POST' and tag_category.establishment_types. \
                    filter(pk=establishment_type.pk).exists():
                raise ObjectAlreadyAdded()
            if request.method == 'DELETE' and not tag_category. \
                    establishment_types.filter(pk=establishment_type.pk). \
                    exists():
                raise RemovedBindingObjectNotFound()
            attrs['related_object'] = establishment_type
        elif obj_type == self.NEWS_TYPE:
            news_type = NewsType.objects.filter(pk=obj_id).first()
            if not news_type:
                raise BindingObjectNotFound()
            if request.method == 'POST' and tag_category.news_types. \
                    filter(pk=news_type.pk).exists():
                raise ObjectAlreadyAdded()
            if request.method == 'DELETE' and not tag_category.news_types. \
                    filter(pk=news_type.pk).exists():
                raise RemovedBindingObjectNotFound()
            attrs['related_object'] = news_type
        return attrs
