"""Establishment serializers."""
from django.utils.translation import ugettext_lazy as _
from phonenumber_field.phonenumber import to_python as str_to_phonenumber
from rest_framework import serializers

from comment import models as comment_models
from comment.serializers import common as comment_serializers
from establishment import models
from location.serializers import AddressBaseSerializer, CitySerializer, AddressDetailSerializer, \
    CityShortSerializer
from main.serializers import AwardSerializer, CurrencySerializer
from review.serializers import ReviewShortSerializer
from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer
from utils import exceptions as utils_exceptions
from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer
from utils.serializers import (ProjectModelSerializer, TranslatedField,
                               FavoritesCreateSerializer)
from location.serializers import EstablishmentWineRegionBaseSerializer, \
    EstablishmentWineOriginBaseSerializer


class ContactPhonesSerializer(serializers.ModelSerializer):
    """Contact phone serializer"""

    class Meta:
        model = models.ContactPhone
        fields = [
            'phone'
        ]


class ContactEmailsSerializer(serializers.ModelSerializer):
    """Contact email serializer"""

    class Meta:
        model = models.ContactEmail
        fields = [
            'email'
        ]


class SocialNetworkRelatedSerializers(serializers.ModelSerializer):
    """Social network serializers."""

    class Meta:
        model = models.SocialNetwork
        fields = [
            'id',
            'network',
            'url',
        ]


class PlateSerializer(ProjectModelSerializer):
    name_translated = TranslatedField()
    currency = CurrencySerializer(read_only=True)

    class Meta:
        model = models.Plate
        fields = [
            'name_translated',
            'currency',
            'price',
            'is_signature_plate',
        ]


class MenuSerializers(ProjectModelSerializer):
    plates = PlateSerializer(read_only=True, many=True, source='plate_set')
    category_translated = serializers.CharField(read_only=True)

    class Meta:
        model = models.Menu
        fields = [
            'id',
            'category',
            'category_translated',
            'plates',
            'establishment'
        ]


class MenuRUDSerializers(ProjectModelSerializer):
    plates = PlateSerializer(read_only=True, many=True, source='plate_set')

    class Meta:
        model = models.Menu
        fields = [
            'id',
            'category',
            'plates',
            'establishment'
        ]


class EstablishmentTypeBaseSerializer(serializers.ModelSerializer):
    """Serializer for EstablishmentType model."""
    name_translated = TranslatedField()
    default_image_url = serializers.ImageField(source='default_image.image',
                                               allow_null=True)
    preview_image = serializers.URLField(source='preview_image_url',
                                         allow_null=True,
                                         read_only=True)

    class Meta:
        """Meta class."""
        model = models.EstablishmentType
        fields = [
            'id',
            'name',
            'name_translated',
            'use_subtypes',
            'index_name',
            'default_image_url',
            'preview_image',
        ]
        extra_kwargs = {
            'name': {'write_only': True},
            'use_subtypes': {'write_only': True},
        }


class EstablishmentTypeGeoSerializer(EstablishmentTypeBaseSerializer):
    """Serializer for EstablishmentType model w/ index_name."""

    class Meta(EstablishmentTypeBaseSerializer.Meta):
        fields = EstablishmentTypeBaseSerializer.Meta.fields + [
            'index_name'
        ]
        extra_kwargs = {
            **EstablishmentTypeBaseSerializer.Meta.extra_kwargs,
            'index_name': {'read_only': True},
        }


class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer):
    """Serializer for EstablishmentSubType models."""
    name_translated = TranslatedField()
    default_image_url = serializers.ImageField(source='default_image.image',
                                               allow_null=True)
    preview_image = serializers.URLField(source='preview_image_url',
                                         allow_null=True,
                                         read_only=True)

    class Meta:
        """Meta class."""
        model = models.EstablishmentSubType
        fields = [
            'id',
            'name',
            'name_translated',
            'establishment_type',
            'index_name',
            'default_image_url',
            'preview_image',
        ]
        extra_kwargs = {
            'name': {'write_only': True},
            'establishment_type': {'write_only': True}
        }


class EstablishmentSubTypeGeoSerializer(EstablishmentSubTypeBaseSerializer):
    """Serializer for EstablishmentSuType model w/ index_name."""

    class Meta(EstablishmentSubTypeBaseSerializer.Meta):
        fields = EstablishmentSubTypeBaseSerializer.Meta.fields + [
            'index_name'
        ]
        extra_kwargs = {
            **EstablishmentSubTypeBaseSerializer.Meta.extra_kwargs,
            'index_name': {'read_only': True},
        }


class EstablishmentEmployeeSerializer(serializers.ModelSerializer):
    """Serializer for actual employees."""

    id = serializers.IntegerField(source='employee.id')
    name = serializers.CharField(source='employee.name')
    position_translated = serializers.CharField(source='position.name_translated')
    awards = AwardSerializer(source='employee.awards', many=True)
    priority = serializers.IntegerField(source='position.priority')
    position_index_name = serializers.CharField(source='position.index_name')
    status = serializers.CharField()

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

        model = models.Employee
        fields = ('id', 'name', 'position_translated', 'awards', 'priority', 'position_index_name', 'status')


class EstablishmentEmployeeCreateSerializer(serializers.ModelSerializer):
    """Serializer for establishment employee relation."""

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

        model = models.EstablishmentEmployee
        fields = ('id',)

    def _validate_entity(self, entity_id_param: str, entity_class):
        entity_id = self.context.get('request').parser_context.get('kwargs').get(entity_id_param)
        entity_qs = entity_class.objects.filter(id=entity_id)
        if not entity_qs.exists():
            raise serializers.ValidationError({'detail': _(f'{entity_class.__name__} not found.')})
        return entity_qs.first()

    def validate(self, attrs):
        """Override validate method"""
        establishment = self._validate_entity("establishment_id", models.Establishment)
        employee = self._validate_entity("employee_id", models.Employee)
        position = self._validate_entity("position_id", models.Position)

        attrs['establishment'] = establishment
        attrs['employee'] = employee
        attrs['position'] = position

        return attrs

    def create(self, validated_data, *args, **kwargs):
        """Override create method"""
        validated_data.update({
            'employee': validated_data.pop('employee'),
            'establishment': validated_data.pop('establishment'),
            'position': validated_data.pop("position")
        })
        return super().create(validated_data)


class EstablishmentShortSerializer(serializers.ModelSerializer):
    """Short serializer for establishment."""
    city = CitySerializer(source='address.city', allow_null=True)
    establishment_type = EstablishmentTypeGeoSerializer()
    establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True)
    currency = CurrencySerializer(read_only=True)

    class Meta:
        """Meta class."""
        model = models.Establishment
        fields = [
            'id',
            'name',
            'index_name',
            'slug',
            'city',
            'establishment_type',
            'establishment_subtypes',
            'currency',
        ]


class _EstablishmentAddressShortSerializer(serializers.ModelSerializer):
    """Short serializer for establishment."""
    city = CitySerializer(source='address.city', allow_null=True)
    establishment_type = EstablishmentTypeGeoSerializer()
    establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True)
    currency = CurrencySerializer(read_only=True)
    address = AddressBaseSerializer(read_only=True)

    class Meta:
        """Meta class."""
        model = models.Establishment
        fields = [
            'id',
            'name',
            'index_name',
            'slug',
            'city',
            'establishment_type',
            'establishment_subtypes',
            'currency',
            'address',
        ]


class EstablishmentProductShortSerializer(serializers.ModelSerializer):
    """SHORT Serializer for displaying info about an establishment on product page."""
    establishment_type = EstablishmentTypeGeoSerializer()
    establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True)
    address = AddressBaseSerializer()
    city = CityShortSerializer(source='address.city', allow_null=True)
    currency_detail = CurrencySerializer(source='currency', read_only=True)

    class Meta:
        """Meta class."""
        model = models.Establishment
        fields = [
            'id',
            'name',
            'index_name',
            'slug',
            'city',
            'establishment_type',
            'establishment_subtypes',
            'address',
            'currency_detail',
        ]


class EstablishmentProductSerializer(EstablishmentShortSerializer):
    """Serializer for displaying info about an establishment on product page."""

    address = AddressBaseSerializer()

    class Meta(EstablishmentShortSerializer.Meta):
        """Meta class."""
        fields = EstablishmentShortSerializer.Meta.fields + [
            'address',
        ]


class EstablishmentBaseSerializer(ProjectModelSerializer):
    """Base serializer for Establishment model."""

    address = AddressBaseSerializer()
    in_favorites = serializers.BooleanField(allow_null=True)
    tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags')
    currency = CurrencySerializer()
    type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
    subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
    image = serializers.URLField(source='image_url', read_only=True)
    wine_regions = EstablishmentWineRegionBaseSerializer(many=True, source='wine_origins_unique',
                                                         read_only=True, allow_null=True)
    preview_image = serializers.URLField(source='preview_image_url',
                                         allow_null=True,
                                         read_only=True)
    tz = serializers.CharField(read_only=True, source='timezone_as_str')
    new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True)

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

        model = models.Establishment
        fields = [
            'id',
            'name',
            'transliterated_name',
            'index_name',
            'price_level',
            'toque_number',
            'public_mark',
            'slug',
            'in_favorites',
            'address',
            'tags',
            'currency',
            'type',
            'subtypes',
            'image',
            'preview_image',
            'new_image',
            'tz',
            'wine_regions',
        ]


class EstablishmentListRetrieveSerializer(EstablishmentBaseSerializer):
    """Establishment with city serializer."""

    address = AddressDetailSerializer()
    schedule = ScheduleRUDSerializer(many=True, allow_null=True)
    restaurant_category = TagBaseSerializer(read_only=True, many=True, allow_null=True)
    restaurant_cuisine = TagBaseSerializer(read_only=True, many=True, allow_null=True)
    artisan_category = TagBaseSerializer(read_only=True, many=True, allow_null=True)

    class Meta(EstablishmentBaseSerializer.Meta):
        """Meta class."""

        fields = EstablishmentBaseSerializer.Meta.fields + [
            'schedule',
            'restaurant_category',
            'restaurant_cuisine',
            'artisan_category',
        ]


class EstablishmentGeoSerializer(EstablishmentBaseSerializer):
    """Serializer for Geo view."""

    type = EstablishmentTypeGeoSerializer(source='establishment_type', read_only=True)
    subtypes = EstablishmentSubTypeGeoSerializer(many=True, source='establishment_subtypes')

    class Meta(EstablishmentBaseSerializer.Meta):
        """Meta class."""

        fields = EstablishmentBaseSerializer.Meta.fields


class RangePriceSerializer(serializers.Serializer):
    max = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True, default=0)
    min = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True, default=0)


class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
    """Serializer for Establishment model."""

    description_translated = TranslatedField()
    awards = AwardSerializer(many=True)
    schedule = ScheduleRUDSerializer(many=True, allow_null=True)
    phones = ContactPhonesSerializer(read_only=True, many=True)
    emails = ContactEmailsSerializer(read_only=True, many=True)
    review = ReviewShortSerializer(source='last_published_review', allow_null=True)
    employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees',
                                                many=True)
    address = AddressDetailSerializer(read_only=True)
    tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags_detail')
    menu = MenuSerializers(source='menu_set', many=True, read_only=True)
    best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    range_price_menu = RangePriceSerializer(read_only=True)
    range_price_carte = RangePriceSerializer(read_only=True)
    vintage_year = serializers.ReadOnlyField()
    gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True)
    wine_origins = EstablishmentWineOriginBaseSerializer(many=True, read_only=True)

    class Meta(EstablishmentBaseSerializer.Meta):
        """Meta class."""

        fields = EstablishmentBaseSerializer.Meta.fields + [
            'description_translated',
            'image',
            'awards',
            'schedule',
            'website',
            'facebook',
            'twitter',
            'lafourchette',
            'booking',
            'phones',
            'emails',
            'review',
            'employees',
            'menu',
            'best_price_menu',
            'best_price_carte',
            'range_price_menu',
            'range_price_carte',
            'transportation',
            'vintage_year',
            'gallery',
            'wine_origins',
        ]


class MobileEstablishmentDetailSerializer(EstablishmentDetailSerializer):
    """Serializer for Establishment model for mobiles."""

    last_comment = comment_serializers.CommentRUDSerializer(allow_null=True)

    class Meta(EstablishmentDetailSerializer.Meta):
        """Meta class."""

        fields = EstablishmentDetailSerializer.Meta.fields + [
            'last_comment',
        ]


class EstablishmentSimilarSerializer(EstablishmentBaseSerializer):
    """Serializer for Establishment model."""

    address = AddressDetailSerializer(read_only=True)
    schedule = ScheduleRUDSerializer(many=True, allow_null=True)
    type = EstablishmentTypeGeoSerializer(source='establishment_type')
    artisan_category = TagBaseSerializer(many=True, allow_null=True, read_only=True)
    restaurant_category = TagBaseSerializer(many=True, allow_null=True, read_only=True)
    restaurant_cuisine = TagBaseSerializer(many=True, allow_null=True, read_only=True)

    class Meta(EstablishmentBaseSerializer.Meta):
        fields = EstablishmentBaseSerializer.Meta.fields + [
            'schedule',
            'type',
            'artisan_category',
            'restaurant_category',
            'restaurant_cuisine',
        ]


class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer):
    """Create comment serializer"""
    mark = serializers.IntegerField()

    class Meta:
        """Serializer for model Comment"""
        model = comment_models.Comment
        fields = [
            'id',
            'created',
            'text',
            'mark',
            'nickname',
            'profile_pic',
        ]

    def validate(self, attrs):
        """Override validate method"""
        # Check establishment object
        establishment_slug = self.context.get('request').parser_context.get('kwargs').get('slug')
        establishment_qs = models.Establishment.objects.filter(slug=establishment_slug)
        if not establishment_qs.exists():
            raise serializers.ValidationError({'detail': _('Establishment not found.')})
        attrs['establishment'] = establishment_qs.first()
        return attrs

    def create(self, validated_data, *args, **kwargs):
        """Override create method"""
        validated_data.update({
            'user': self.context.get('request').user,
            'content_object': validated_data.pop('establishment')
        })
        return super().create(validated_data)


class EstablishmentCommentRUDSerializer(comment_serializers.CommentSerializer):
    """Retrieve/Update/Destroy comment serializer."""

    class Meta:
        """Meta class."""
        model = comment_models.Comment
        fields = [
            'id',
            'created',
            'text',
            'mark',
            'nickname',
            'profile_pic',
        ]


class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer):
    """Serializer to favorite object w/ model Establishment."""

    def validate(self, attrs):
        """Overridden validate method"""
        # Check establishment object
        establishment_qs = models.Establishment.objects.filter(slug=self.slug)

        # Check establishment obj by slug from lookup_kwarg
        if not establishment_qs.exists():
            raise serializers.ValidationError({'detail': _('Object not found.')})
        else:
            establishment = establishment_qs.first()

        # Check existence in favorites
        if establishment.favorites.filter(user=self.user).exists():
            raise utils_exceptions.FavoritesError()

        attrs['establishment'] = establishment
        return attrs

    def create(self, validated_data, *args, **kwargs):
        """Overridden create method"""
        validated_data.update({
            'user': self.user,
            'content_object': validated_data.pop('establishment')
        })
        return super().create(validated_data)


class EstablishmentCarouselCreateSerializer(CarouselCreateSerializer):
    """Serializer to carousel object w/ model News."""

    def validate(self, attrs):
        search_kwargs = {'pk': self.pk} if self.pk else {'slug': self.slug}

        establishment = models.Establishment.objects.filter(**search_kwargs).first()
        if not establishment:
            raise serializers.ValidationError({'detail': _('Object not found.')})

        if establishment.carousels.exists():
            raise utils_exceptions.CarouselError()

        attrs['establishment'] = establishment
        return attrs

    def create(self, validated_data, *args, **kwargs):
        validated_data.update({
            'content_object': validated_data.pop('establishment')
        })
        return super().create(validated_data)


class CompanyBaseSerializer(serializers.ModelSerializer):
    """Company base serializer"""
    phone_list = serializers.SerializerMethodField(source='phones', read_only=True)
    fax_list = serializers.SerializerMethodField(source='faxes', read_only=True)
    address_detail = AddressDetailSerializer(source='address', read_only=True)

    class Meta:
        """Meta class."""
        model = models.Company
        fields = [
            'id',
            'establishment',
            'name',
            'phones',
            'faxes',
            'legal_entity',
            'registry_number',
            'vat_number',
            'sic_code',
            'address',
            'phone_list',
            'fax_list',
            'address_detail',
        ]
        extra_kwargs = {
            'establishment': {'write_only': True},
            'phones': {'write_only': True},
            'faxes': {'write_only': True},
            'address': {'write_only': True}
        }

    def get_phone_list(self, instance):
        """Return list of phone numbers."""
        return instance.phones

    def get_fax_list(self, instance):
        """Return list of fax numbers."""
        return instance.faxes

    def validate(self, attrs):
        """Overridden validate method"""
        phones = [str_to_phonenumber(phone).as_national for phone in attrs.get('phones')]
        faxes = [str_to_phonenumber(fax).as_national for fax in attrs.get('faxes')]

        if faxes:
            if models.Company.objects.filter(faxes__overlap=faxes).exists():
                raise serializers.ValidationError({'detail': _('Fax is already reserved.')})

        if phones:
            if models.Company.objects.filter(phones__overlap=phones).exists():
                raise serializers.ValidationError({'detail': _('Phones is already reserved.')})
        return attrs


class EstablishmentGuideElementSerializer(serializers.ModelSerializer):
    """Serializer for Guide serializer."""
    type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
    subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
    address = AddressBaseSerializer()
    tz = serializers.CharField(read_only=True, source='timezone_as_str')
    schedule = ScheduleRUDSerializer(many=True, allow_null=True)
    best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    range_price_menu = RangePriceSerializer(read_only=True)
    range_price_carte = RangePriceSerializer(read_only=True)
    currency = CurrencySerializer()

    class Meta(EstablishmentBaseSerializer.Meta):
        """Meta class."""
        fields = [
            'id',
            'type',
            'name',
            'subtypes',
            'address',
            'tz',
            'schedule',
            'best_price_menu',
            'best_price_carte',
            'range_price_menu',
            'range_price_carte',
            'currency',
        ]
