from django.conf import settings
from django.contrib.gis.geos import Point
from rest_framework import serializers

from location import models
from transfer.mixins import TransferSerializerMixin
from utils.methods import get_point_from_coordinates
from transfer.models import Cepages
from django.utils.text import slugify

from django.contrib.gis.geos import Point
from django.core.exceptions import MultipleObjectsReturned
from gallery.models import Image


class CountrySerializer(serializers.ModelSerializer):
    country_code_2 = serializers.CharField()
    id = serializers.IntegerField()

    class Meta:
        model = models.Country
        fields = (
            "id",
            "country_code_2",
        )

    def validate(self, data):
        data["code"] = self.get_country_code(data)
        del(data['country_code_2'])

        data['old_id'] = data.pop('id')

        return data

    def create(self, validated_data):
        # Some countries already in database
        try:
            country = models.Country.objects.get(code=validated_data['code'])
        except models.Country.DoesNotExist:
            country = models.Country.objects.create(**validated_data)
        return country

    def get_country_code(self, obj):
        return obj.get("country_code_2")


class RegionSerializer(serializers.ModelSerializer):
    region_code = serializers.CharField()
    subregion_code = serializers.CharField(allow_null=True, allow_blank=True)
    country_code_2 = serializers.CharField()
    id = serializers.IntegerField()

    class Meta:
        model = models.Region
        fields = (
            "region_code",
            "country_code_2",
            "subregion_code",
            "id"
        )

    def validate(self, data):
        data = self.set_old_id(data)
        data = self.set_code(data)
        data = self.set_country(data)
        return data

    def create(self, validated_data):
        # Some regions may be already in database
        try:
            region = models.Region.objects.get(old_id=validated_data['old_id'])
        except models.Region.DoesNotExist:
            region = models.Region.objects.create(**validated_data)
        except Exception as e:
            raise ValueError(f"REGION ERROR: {validated_data}: {e}")
        return region

    def set_code(self, data):
        if "subregion_code" in data and data["subregion_code"] is not None and data["subregion_code"].strip() != "":
            try:
                parent_region = models.Region.objects.filter(code=str(data['region_code'])).first()
            except Exception as e:
                raise ValueError(f"Parent region error with {data}: {e}")

            data['parent_region'] = parent_region
            data['code'] = data.pop('subregion_code')
            del(data['region_code'])
        else:
            data['code'] = data.pop('region_code')
            del(data['subregion_code'])

        return data

    def set_country(self, data):
        try:
            country = models.Country.objects.get(code=data['country_code_2'])
        except Exception as e:
            raise ValueError(f"Country error with {data}: {e}")

        data["country"] = country
        del(data['country_code_2'])

        return data

    def set_old_id(self, data):
        data['old_id'] = data.pop("id")
        return data


class CitySerializer(serializers.ModelSerializer):
    country_code_2 = serializers.CharField()
    region_code = serializers.CharField()
    subregion_code = serializers.CharField(allow_null=True, allow_blank=True)
    zip_code = serializers.CharField(allow_null=True, allow_blank=True)
    is_island = serializers.IntegerField(allow_null=True)
    name = serializers.CharField()
    id = serializers.IntegerField()

    class Meta:
        model = models.City
        fields = (
            "country_code_2",
            "region_code",
            "subregion_code",
            "zip_code",
            "is_island",
            "name",
            "id"
        )

    def validate(self, data):
        data = self.set_old_id(data)
        data = self.set_relations(data)
        data = self.set_is_island(data)
        data = self.set_code(data)
        data = self.set_zip_code(data)
        return data

    def create(self, validated_data):
        return models.City.objects.create(**validated_data)

    def set_is_island(self, data):
        data['is_island'] = True if "is_island" in data \
                                    and data['is_island'] is not None \
                                    and data['is_island'] > 0 \
            else False
        return data

    def set_code(self, data):
        data['code'] = data.pop('region_code')
        return data

    def set_relations(self, data):
        try:
            region = models.Region.objects.filter(code=data['region_code']).first()
        except models.Region.DoesNotExist as e:
            try:
                region = models.Region.objects.filter(code=data['subregion_code']).first()
            except models.Region.DoesNotExist as e:
                raise ValueError(f"Region not found with {data}: {e}")

        data['region'] = region
        del(data['subregion_code'])

        try:
            country = models.Country.objects.get(code=data['country_code_2'])
        except models.Country.DoesNotExist as e:
            raise ValueError(f"Country not found with {data}: {e}")

        data['country'] = country
        del(data['country_code_2'])

        return data

    def set_zip_code(self, data):
        data['postal_code'] = data.pop('zip_code')
        if data['postal_code'] is None:
            data['postal_code'] = ""
        return data

    def set_old_id(self, data):
        data['old_id'] = data.pop('id')
        return data


class AddressSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
    city_id = serializers.IntegerField()
    zip_code = serializers.CharField(allow_null=True, allow_blank=True)
    latitude = serializers.DecimalField(max_digits=10, decimal_places=6, allow_null=True)
    longitude = serializers.DecimalField(max_digits=10, decimal_places=6, allow_null=True)
    address = serializers.CharField(allow_null=True, allow_blank=True)

    class Meta:
        model = models.Address
        fields = (
            "id",
            "city_id",
            "zip_code",
            "latitude",
            "longitude",
            "address"
        )

    def validate(self, data):
        data = self.set_old_id(data)
        data = self.set_address(data)
        data = self.set_postal_code(data)
        data = self.set_city(data)
        data = self.set_point(data)
        return data

    def create(self, validated_data):
        return models.Address.objects.create(**validated_data)

    def set_old_id(self, data):
        data['old_id'] = data.pop("id")
        return data

    def set_postal_code(self, data):
        data['postal_code'] = data.pop('zip_code', None)
        if data['postal_code'] is None:
            data['postal_code'] = ""
        return data

    def set_city(self, data):
        try:
            city = models.City.objects.filter(mysql_id=data['city_id']).first()
        except models.City.DoesNotExist as e:
            raise ValueError(f"City not found with {data}: {e}")

        data['city'] = city
        del(data['city_id'])
        return data

    def set_address(self, data):
        if "address" in data and data['address'] is not None and data['address'] != "":
            address_list = data.pop('address').split(' ')
            is_first_street = False
            data['street_name_1'] = []
            data['street_name_2'] = []
            while len(address_list) > 0:
                address_part = address_list.pop()
                try:
                    address_part = int(address_part)
                    data['number'] = address_part
                    is_first_street = True
                except:
                    if is_first_street:
                        data['street_name_1'].append(address_part)
                    else:
                        data['street_name_2'].append(address_part)

            data['street_name_1'] = " ".join(data['street_name_1'])
            data['street_name_2'] = " ".join(data['street_name_2'])
            if "number" not in data:
                data['number'] = 0
        else:
            del(data['address'])
            data['number'] = 0

        return data

    def set_point(self, data):
        if data['latitude'] is not None and data['longitude'] is not None:
            data['coordinates'] = Point(float(data['longitude']), float(data['latitude']))
            # data['coordinates'] = GEOSGeometry(
            #     json.dumps({
            #         "type": "Point",
            #         "coordinates": [data['longitude'], data['latitude']]
            #     }, ensure_ascii=False, default=str)
            # )
        else:
            data['coordinates'] = None

        del(data['latitude'])
        del(data['longitude'])

        return data


class WineRegionSerializer(TransferSerializerMixin):

    id = serializers.IntegerField()
    name = serializers.CharField()
    desc = serializers.CharField(allow_null=True)
    latitude = serializers.FloatField(allow_null=True)
    longitude = serializers.FloatField(allow_null=True)

    class Meta:
        model = models.WineRegion
        fields = (
            'id',
            'name',
            'desc',
            'latitude',
            'longitude',
        )

    def validate(self, attrs):
        latitude = attrs.pop('latitude', None)
        longitude = attrs.pop('longitude', None)

        attrs['old_id'] = attrs.pop('id')
        attrs['description'] = {settings.FALLBACK_LOCALE: attrs.pop('desc', None)}
        attrs['coordinates'] = get_point_from_coordinates(latitude, longitude)
        return attrs



class WineSubRegionSerializer(WineRegionSerializer):

    id = serializers.IntegerField()
    name = serializers.CharField()
    parent_id = serializers.IntegerField()

    class Meta:
        model = models.WineSubRegion
        fields = (
            'id',
            'name',
            'parent_id',
        )

    def validate(self, attrs):
        parent_id = attrs.pop('parent_id', None)

        attrs['old_id'] = attrs.pop('id')
        attrs['wine_region'] = self.get_wine_region(parent_id)
        return attrs

    def get_wine_region(self, parent_id):
        qs = models.WineRegion.objects.filter(old_id=parent_id)
        if qs.exists():
            return qs.first()


class WineVillageSerializer(TransferSerializerMixin):

    id = serializers.IntegerField()
    name = serializers.CharField()
    parent_id = serializers.IntegerField()

    class Meta:
        model = models.WineVillage
        fields = (
            'id',
            'name',
            'parent_id',
        )

    def validate(self, attrs):
        parent_id = attrs.pop('parent_id', None)
        attrs['old_id'] = attrs.pop('id')
        attrs['wine_region'] = self.get_wine_region(parent_id)
        return attrs


class CityMapSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
    map1 = serializers.CharField(allow_blank=True, allow_null=True)
    map2 = serializers.CharField(allow_blank=True, allow_null=True)
    map_ref = serializers.CharField(allow_blank=True, allow_null=True)
    situation = serializers.CharField(allow_blank=True, allow_null=True)
    
    class Meta:
        model = models.City
        fields = (
            "id",
            "map1",
            "map2",
            "map_ref",
            "situation",
        )

    def validate(self, data):
        data = self.set_old_id(data)
        data = self.set_city(data)
        data = self.set_null_fields(data)
        return data

    def create(self, validated_data):
        city = validated_data.pop('city')
        city.update(
            map1=validated_data['map1'],
            map2=validated_data['map2'],
            map_ref=validated_data['map_ref'],
            situation=validated_data['situation']
        )

    def set_old_id(self, data):
        data['old_id'] = data.pop("id")
        return data

    def set_city(self, data):
        try:
            city = models.City.objects.get(old_id=data['old_id'])
        except models.City.DoesNotExist as e:
            raise ValueError(f"Cannot find city with id = {data['old_id']}: {e}")
        except MultipleObjectsReturned as e:
            raise ValueError(f"Find multiple cities with id = {data['old_id']}: {e}")

        data['city'] = city
        return data

    def set_null_fields(self, data):
        for field in ["map1", "map2", "map_ref", "situation"]:
            if field not in data or data[field] is None:
                data[field] = ""
        return data


class CityMapCorrectSerializer(CityMapSerializer):
    def set_city(self, data):
        try:
            city = models.City.objects.get(mysql_id=data['old_id'])
        except models.City.DoesNotExist as e:
            raise ValueError(f"Cannot find city with id = {data['old_id']}: {e}")
        except MultipleObjectsReturned as e:
            raise ValueError(f"Find multiple cities with id = {data['old_id']}: {e}")

        data['city'] = city
        return data

    def create(self, validated_data):
        city = validated_data.pop('city')
        city.map1=validated_data['map1'],
        city.map2=validated_data['map2'],
        city.map_ref=validated_data['map_ref'],
        city.situation=validated_data['situation']
        city.save()


class CityGallerySerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
    city_id = serializers.IntegerField()
    attachment_file_name = serializers.CharField()

    class Meta:
        model = models.CityGallery
        fields = ("id", "city_id", "attachment_file_name")

    def validate(self, data):
        data = self.set_old_id(data)
        data = self.set_gallery(data)
        data = self.set_city(data)
        return data

    def create(self, validated_data):
        return models.CityGallery.objects.create(**validated_data)

    def set_old_id(self, data):
        data['old_id'] = data.pop('id')
        return data

    def set_gallery(self, data):
        link_prefix = "city_photos/00baf486523f62cdf131fa1b19c5df2bf21fc9f8/"
        try:
            data['image'] = Image.objects.create(
                image=f"{link_prefix}{data['attachment_file_name']}"
            )
        except Exception as e:
            raise ValueError(f"Cannot create image with {data}: {e}")
        del(data['attachment_file_name'])
        return data

    def set_city(self, data):
        try:
            data['city'] = models.City.objects.get(old_id=data.pop('city_id'))
        except models.City.DoesNotExist as e:
            raise ValueError(f"Cannot get city with {data}: {e}")
        except MultipleObjectsReturned as e:
            raise ValueError(f"Multiple cities find with {data}: {e}")

        return data


class CepageWineRegionSerializer(TransferSerializerMixin):

    CATEGORY_LABEL = 'Cepage'
    CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL)

    cepage_id = serializers.PrimaryKeyRelatedField(
        queryset=Cepages.objects.all())
    wine_region_id = serializers.IntegerField()

    class Meta(WineRegionSerializer.Meta):
        fields = [
            'cepage_id',
            'wine_region_id',
        ]

    def create(self, validated_data):
        obj = self.get_wine_region(validated_data['wine_region_id'])
        cepage = validated_data.get('cepage_id')
        tag = self.get_tag(cepage.name, self.CATEGORY_INDEX_NAME)

        if obj and tag not in obj.tags.all():
            obj.tags.add(tag)
            return obj
