from django.conf import settings
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.db import transaction
from django.utils.text import slugify
from rest_framework import serializers

from account.models import User
from establishment.models import Establishment, ContactEmail, ContactPhone, \
    EstablishmentType, EstablishmentSubType, EstablishmentNote
from location.models import Address
from timetable.models import Timetable
from utils.legacy_parser import parse_legacy_schedule_content
from utils.serializers import TimeZoneChoiceField
from utils.slug_generator import generate_unique_slug


class EstablishmentSerializer(serializers.ModelSerializer):
    slug = serializers.CharField(allow_null=True, allow_blank=True)
    type = serializers.CharField()
    description = serializers.DictField(
        allow_null=True,
        child=serializers.CharField(allow_null=True),
    )
    schedules = serializers.CharField(allow_null=True, allow_blank=True)
    location = serializers.IntegerField(allow_null=True)
    email = serializers.CharField(allow_null=True, allow_blank=True)
    phone = serializers.CharField(allow_null=True, allow_blank=True)
    website = serializers.CharField(allow_null=True, allow_blank=True)
    facebook = serializers.CharField(allow_null=True, allow_blank=True)
    twitter = serializers.CharField(allow_null=True, allow_blank=True)
    booking = serializers.CharField(allow_null=True, allow_blank=True)
    state = serializers.CharField(allow_null=True)
    tz = TimeZoneChoiceField()
    created = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')

    class Meta:
        model = Establishment
        fields = (
            'created',
            'old_id',  # +
            'name',  # +
            'transliterated_name',  # +
            'tz',  # +
            'website',  # +
            'facebook',  # +
            'twitter',  # +
            'lafourchette',  # +
            'booking',  # +
            'type',  # + см в JIRA
            'slug',  # + сгенерировать уникальный слаг
            'description',  # + (разобрал в transfer_data)
            'schedules',  # + разобрать RUBY словать (2 варианта)
            'location',  # + получить новые объекты Address по old_id
            'email',  # + создать объект для ContactEmail
            'phone',  # + создать объект для ContactPhone
            'state',  # + создать объект для ContactPhone
        )

    def validate(self, data):
        old_type = data.pop('type', None)

        data.update({
            'slug': generate_unique_slug(Establishment, data['slug'] if data['slug'] else data['name']),
            'address_id': self.get_address(data['location']),
            'establishment_type_id': self.get_type(old_type),
            'is_publish': data.get('state') == 'published',
            'subtype': self.get_subtype(old_type),
        })
        data.pop('location')
        data.pop('state')
        return data

    @transaction.atomic
    def create(self, validated_data):
        email = validated_data.pop('email')
        phone = validated_data.pop('phone')
        schedules = validated_data.pop('schedules')
        subtypes = [validated_data.pop('subtype', None)]

        # establishment = Establishment.objects.create(**validated_data)
        establishment, _ = Establishment.objects.update_or_create(
            old_id=validated_data['old_id'],
            defaults=validated_data,
        )
        if email:
            ContactEmail.objects.get_or_create(
                email=email,
                establishment=establishment,
            )
        if phone:
            ContactPhone.objects.get_or_create(
                phone=phone,
                establishment=establishment,
            )
        if schedules:
            new_schedules = self.get_schedules(schedules)
            for schedule in new_schedules:
                establishment.schedule.add(schedule)
            establishment.save()
        if subtypes:
            establishment.establishment_subtypes.add(*[i for i in subtypes if i])

        return establishment

    @staticmethod
    def get_address(address):
        address = Address.objects.filter(old_id=address).first()
        if address:
            return address.id
        return None

    @staticmethod
    def get_type(old_type):
        types = {
            'Restaurant': EstablishmentType.RESTAURANT,
            'Shop': EstablishmentType.ARTISAN,
            'Wineyard': EstablishmentType.PRODUCER,
        }
        index_name = types.get(old_type)
        if index_name:
            obj, _ = EstablishmentType.objects.get_or_create(index_name=index_name)
            return obj.id

    @staticmethod
    def get_schedules(schedules):
        result = []
        legacy_dict = parse_legacy_schedule_content(schedules)

        weekdays = {
            'su': Timetable.SUNDAY,
            'mo': Timetable.MONDAY,
            'tu': Timetable.TUESDAY,
            'we': Timetable.WEDNESDAY,
            'th': Timetable.THURSDAY,
            'fr': Timetable.FRIDAY,
            'sa': Timetable.SATURDAY,
        }

        for key, val in legacy_dict.items():
            payload = {
                'weekday': weekdays[key],
                'lunch_start': None,
                'lunch_end': None,
                'dinner_start': None,
                'dinner_end': None,
                'opening_at': None,
                'closed_at': None,
            }
            if val.get('morning'):
                payload.update({
                    'lunch_start': val['morning'].get('start'),
                    'lunch_end': val['morning'].get('end'),
                })
            if val.get('afternoon'):
                payload.update({
                    'dinner_start': val['afternoon'].get('start'),
                    'dinner_end': val['afternoon'].get('end'),
                })

            try:
                obj, _ = Timetable.objects.get_or_create(**payload)
            except ValidationError:
                obj = None
            except MultipleObjectsReturned:
                obj = Timetable.objects.filter(**payload).first()

            if obj:
                result.append(obj)

        return result

    def get_subtype(self, old_type):
        if old_type == 'Wineyard':
            subtype_name = 'Winery'
            establishment_type_id = self.get_type(old_type)
            subtype, _ = EstablishmentSubType.objects.get_or_create(
                name={settings.FALLBACK_LOCALE: subtype_name},
                index_name=slugify(subtype_name),
                establishment_type_id=establishment_type_id)
            return subtype


class EstablishmentNoteSerializer(serializers.ModelSerializer):

    id = serializers.IntegerField()
    establishment_id = serializers.IntegerField()
    account_id = serializers.IntegerField(allow_null=True)
    text = serializers.CharField(allow_blank=True, allow_null=True)

    class Meta:
        model = EstablishmentNote
        fields = (
            'id',
            'establishment_id',
            'account_id',
            'text',
        )

    def validate(self, attrs):
        attrs['old_id'] = attrs['id']
        attrs['establishment'] = self.get_establishment(attrs.pop('establishment_id'))
        attrs['user'] = self.get_user(attrs.pop('account_id'))
        return attrs

    def create(self, validated_data):
        qs = self.Meta.model.objects.filter(**validated_data)
        establishment = validated_data.get('establishment')

        if not qs.exists() and establishment:
            obj = super().create(validated_data)
            return obj

    def get_establishment(self, old_id):
        if old_id:
            qs = Establishment.objects.filter(old_id=old_id)
            if qs.exists():
                return qs.first()

    def get_user(self, old_id):
        qs = User.objects.exclude(old_id__isnull=True).filter(old_id=old_id)
        if qs.exists():
            return qs.first()
