# coding: utf-8

# This file is part of compotista.
#
# compotista is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# compotista is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>

from django.db import transaction

import representatives.models as models
from rest_framework import serializers


class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Country
        fields = ('name', 'code')


class EmailSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Email
        fields = ('email', 'kind')


class WebsiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.WebSite
        fields = ('url', 'kind')

    def validate_url(self, value):
        '''
        Don’t validate url, because it could break import of not proper formed url
        '''
        return value


class PhoneSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Phone
        fields = ('number', 'kind')

    def validate_phone(self, value):
        return value


class AddressSerializer(serializers.ModelSerializer):
    country = CountrySerializer()
    phones = PhoneSerializer(many=True)
    class Meta:
        model = models.Address
        fields = ('country', 'city', 'street',
                  'number', 'postcode', 'floor',
                  'office_number', 'kind', 'phones',
        )


class ContactField(serializers.Serializer):
    emails = EmailSerializer(many=True)
    phones = PhoneSerializer(many=True)
    websites = WebsiteSerializer(many=True)
    address = AddressSerializer(many=True)

    def get_attribute(self, obj):
        return {
            'emails': obj.email_set.all(),
            'websites': obj.website_set.all(),
            'phones': obj.phone_set.all(),
            'address': obj.address_set.all(),
        }


class MandateSerializer(serializers.ModelSerializer):
    name = serializers.CharField(source='group.name')
    short_id = serializers.CharField(source='group.abbreviation', allow_blank=True)
    kind = serializers.CharField(source='group.kind')
    constituency = serializers.CharField(source='constituency.name')

    class Meta:
        model = models.Mandate
        fields = (
            'id',
            'name',
            'short_id',
            'kind',
            'constituency',
            'representative',
            'role',
            'representative',
            'begin_date',
            'end_date',
            'fingerprint',
        )


class MandateHyperLinkedSerializer(MandateSerializer):
    class Meta(MandateSerializer.Meta):
        fields = MandateSerializer.Meta.fields + ('url',)


class RepresentativeMandateSerializer(MandateSerializer):
    class Meta(MandateSerializer.Meta):
        extra_kwargs = {
            'fingerprint': {
                'validators': [],
            },
        }

        fields = [elem for elem in MandateSerializer.Meta.fields if elem != 'representative']


class RepresentativeSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Representative
        fields = (
            'id',
            'slug',
            'remote_id',
            'first_name',
            'last_name',
            'full_name',
            'gender',
            'birth_place',
            'birth_date',
            'photo',
            'active',
            'fingerprint'
        )


class RepresentativeHyperLinkedSerializer(RepresentativeSerializer):
    class Meta(RepresentativeSerializer.Meta):
        fields = RepresentativeSerializer.Meta.fields + ('url',)


class RepresentativeDetailSerializer(RepresentativeSerializer):
    contacts = ContactField()
    mandates = RepresentativeMandateSerializer(many=True)

    class Meta(RepresentativeSerializer.Meta):
        fields = RepresentativeSerializer.Meta.fields + (
            'cv',
            'contacts',
            'mandates'
        )

    @transaction.atomic
    def create(self, validated_data):
        """
        Nested creation is not implemented yet in DRF, it sucks We made an
        intensive use of get_or_create to avoid recreating
        representatives The idea here is to truncate all models except
        representatives and recreate them every import.
        TODO : fix this code when it will be implemented
        """

        contacts_data = validated_data.pop('contacts')
        mandates_data = validated_data.pop('mandates')
        representative = models.Representative.objects.create(
            **validated_data
        )
        self._create_mandates(mandates_data, representative)
        self._create_contacts(contacts_data, representative)
        return representative

    @transaction.atomic
    def update(self, instance, validated_data):
        contacts_data = validated_data.pop('contacts')
        mandates_data = validated_data.pop('mandates')

        for attr, value in validated_data.iteritems():
            setattr(instance, attr, value)
        instance.save()

        self._create_mandates(mandates_data, instance)
        self._create_contacts(contacts_data, instance)
        return instance

    def touch_model(self, model, **data):
        '''
        This method create or look up a model with the given data
        it saves the given model if it exists, updating its
        updated field
        '''
        instance, created = model.objects.get_or_create(**data)

        if not created:
            instance.save()

        return (instance, created)

    def _create_contacts(self, contacts_data, representative):
        for contact_data in contacts_data['emails']:
            contact_data['representative'] = representative
            self.touch_model(model=models.Email, **contact_data)

        for contact_data in contacts_data['websites']:
            contact_data['representative'] = representative
            self.touch_model(model=models.WebSite, **contact_data)

        for contact_data in contacts_data['address']:
            country, _ = models.Country.objects.get_or_create(
                **contact_data.pop('country')
            )
            phone_set = contact_data.pop('phones')
            contact_data['representative'] = representative
            contact_data['country'] = country
            contact, _ = self.touch_model(model=models.Address, **contact_data)

            for phone_data in phone_set:
                phone_data['representative'] = representative
                phone_data['address'] = contact
                self.touch_model(model=models.Phone, **phone_data)

    def _create_mandates(self, mandates_data, representative):
        for mandate_data in mandates_data:
            # serializer = MandateSerializer(data=mandate_data)
            constituency, _ = self.touch_model(model=models.Constituency,
                **mandate_data.pop('constituency')
            )
            group, _ = self.touch_model(model=models.Group,
                **mandate_data.pop('group')
            )
            mandate_data['representative'] = representative
            mandate_data['constituency'] = constituency
            mandate_data['group'] = group

            models.Mandate.objects.update_or_create(
                fingerprint=mandate_data['fingerprint'],
                defaults=mandate_data
            )