from django.db.transaction import atomic
from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers

from account.serializers import UserSerializer
from utils.exceptions import CRMException
from store.models import Checklist, GlobalSettings, Category, PaymentMethod, Promocode, Image, Gift
from store.utils import get_primary_key_related_model
from poizonstore.utils import PriceField


class ImageSerializer(serializers.ModelSerializer):
    image = Base64ImageField()

    class Meta:
        model = Image
        fields = ('image',)


class ImageListSerializer(serializers.ListSerializer):
    child = ImageSerializer()

    def to_internal_value(self, data):
        if isinstance(data, list):
            data = [{"image": x} for x in data]
        return super().to_internal_value(data)

    def to_representation(self, data):
        images = super().to_representation(data)
        return [image['image'] for image in images]


class CategoryChecklistSerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ('id', 'name')


class CategorySerializer(serializers.ModelSerializer):
    chinarush = PriceField(source='delivery_price_CN_RU')

    class Meta:
        model = Category
        fields = ('id', 'name', 'chinarush', 'commission')


class CategoryFullSerializer(CategorySerializer):
    children = serializers.SerializerMethodField()

    def get_children(self, obj):
        return CategoryFullSerializer(obj.get_children(), many=True).data

    class Meta:
        model = CategorySerializer.Meta.model
        fields = CategorySerializer.Meta.fields + ('children',)


class GiftSerializer(serializers.ModelSerializer):
    image = Base64ImageField(required=False, allow_null=True)
    min_price = PriceField()

    class Meta:
        model = Gift
        fields = ('id', 'name', 'image', 'min_price', 'available_count')


class ChecklistSerializer(serializers.ModelSerializer):
    id = serializers.CharField(read_only=True)
    manager_id = serializers.PrimaryKeyRelatedField(read_only=True, allow_null=True)
    link = serializers.URLField(source='product_link', required=False)
    category = get_primary_key_related_model(CategoryChecklistSerializer, required=False, allow_null=True)

    size = serializers.CharField(required=False, allow_null=True)

    image = ImageListSerializer(source='main_images', required=False)
    preview_image_url = serializers.ImageField(read_only=True)

    promocode = serializers.SlugRelatedField(slug_field='name', queryset=Promocode.objects.active(),
                                             required=False, allow_null=True)

    use_bonuses = serializers.BooleanField(write_only=True, default=False)
    gift = get_primary_key_related_model(GiftSerializer, required=False, allow_null=True)

    yuan_rate = PriceField(read_only=True)
    price_yuan = PriceField(required=False)
    price_rub = serializers.IntegerField(read_only=True)

    delivery_price_CN = PriceField(read_only=True)
    delivery_price_CN_RU = PriceField(read_only=True)

    full_price = PriceField(read_only=True)
    real_price = PriceField(required=False, allow_null=True)

    commission_rub = PriceField(read_only=True)

    customer = get_primary_key_related_model(UserSerializer, required=False, allow_null=True)

    receiver_name = serializers.CharField(required=False, allow_null=True)
    receiver_phone = serializers.CharField(required=False, allow_null=True)

    is_split_payment = serializers.BooleanField(required=False)
    payment_method = serializers.SlugRelatedField(slug_field='slug',
                                                  queryset=PaymentMethod.objects.all(),
                                                  required=False, allow_null=True)
    payment_proof = Base64ImageField(required=False, allow_null=True)
    split_payment_proof = Base64ImageField(required=False, allow_null=True)
    receipt = Base64ImageField(required=False, allow_null=True)
    poizon_tracking = serializers.CharField(required=False, allow_null=True)
    cdek_tracking = serializers.CharField(required=False, allow_null=True)
    delivery = serializers.ChoiceField(choices=Checklist.DeliveryType.CHOICES, required=False, allow_null=True)
    delivery_display = serializers.CharField(source='get_delivery_display', read_only=True)

    created_at = serializers.DateTimeField(read_only=True)
    status_updated_at = serializers.DateTimeField(read_only=True)

    def _collect_images_by_fields(self, validated_data):
        images = {}
        for k in ('main_images',):
            if k in validated_data:
                images[k] = validated_data.pop(k)

        return images

    def _create_main_images(self, instance, images):
        if images is None:
            return

        img_objs = [Image(image=img['image'], type=Image.DEFAULT) for img in images]
        img_objs = Image.objects.bulk_create(img_objs)
        instance.images.set(img_objs)
        instance.generate_preview(next(iter(img_objs), None))

    @atomic
    def create(self, validated_data):
        images = self._collect_images_by_fields(validated_data)

        # Managers can create orders with arbitrary customers
        # Client orders are created with client's account
        user = self.context['request'].user
        if not user.is_manager or validated_data.get('customer') is None:
            validated_data['customer'] = user

        use_bonuses = validated_data.pop('use_bonuses')

        instance = super().create(validated_data)
        self._create_main_images(instance, images.get('main_images'))

        if use_bonuses:
            instance.customer.spend_bonuses(order=instance)

        return instance

    def update(self, instance, validated_data):
        images = self._collect_images_by_fields(validated_data)

        # Perform update
        instance: Checklist = super().update(instance, validated_data)

        self._create_main_images(instance, images.get('main_images'))

        # Invalidate prefetched images
        delattr(instance, '_images')

        if instance.status == Checklist.Status.NEW:
            has_payment_info = instance.payment_proof is not None and instance.payment_method is not None
            if has_payment_info:
                instance.status = Checklist.Status.PAYMENT
                instance.save()

        return instance

    @staticmethod
    def get_image(obj: Checklist):
        return obj.images.all()

    class Meta:
        model = Checklist
        fields = ('id', 'status', 'manager_id', 'link',
                  'category',
                  'brand', 'model', 'size',
                  'image',
                  'preview_image_url',
                  'yuan_rate', 'price_yuan', 'price_rub', 'delivery_price_CN', 'delivery_price_CN_RU', 'commission_rub',
                  'promocode', 'gift', 'use_bonuses', 'bonus_used',
                  'comment',
                  'full_price', 'real_price',
                  'customer',
                  'receiver_name', 'receiver_phone',
                  'is_split_payment', 'payment_method', 'payment_proof', 'split_payment_proof', 'split_accepted', 'receipt',
                  'poizon_tracking', 'cdek_tracking', 'cdek_barcode_pdf', 'delivery', 'delivery_display',
                  'created_at', 'status_updated_at', 'buy_time_remaining'
                  )


class ClientChecklistSerializerMixin:
    def validate(self, attrs):
        gift = attrs.get('gift')
        if gift is not None:
            if self.instance.price_yuan < gift.min_price:
                raise CRMException("Can't add gift: price of order < min_price of gift")

            if gift.available_count == 0:
                raise CRMException("Gift is not available")

        return attrs


class ClientCreateChecklistSerializer(ClientChecklistSerializerMixin, ChecklistSerializer):
    status = serializers.SerializerMethodField()

    class Meta:
        model = ChecklistSerializer.Meta.model
        fields = ChecklistSerializer.Meta.fields
        writable_fields = {
            'link',
            'brand', 'model', 'size', 'category',
            'price_yuan', 'use_bonuses',
            'comment',
        }
        read_only_fields = tuple(set(ChecklistSerializer.Meta.fields) - writable_fields)

    def get_status(self, obj):
        return Checklist.Status.DRAFT


class ClientUpdateChecklistSerializer(ClientChecklistSerializerMixin, ChecklistSerializer):
    class Meta:
        model = ChecklistSerializer.Meta.model
        fields = ChecklistSerializer.Meta.fields

        writable_fields = {
            'comment',
            'payment_proof', 'payment_method',
            'delivery',
            'receiver_name', 'receiver_phone',
            'gift', 'cdek_barcode_pdf'
        }
        read_only_fields = tuple(set(ChecklistSerializer.Meta.fields) - writable_fields)


class GlobalSettingsSerializer(serializers.ModelSerializer):
    yuan_rate = PriceField(source='full_yuan_rate', read_only=True)
    yuan_rate_commission = PriceField()
    chinadelivery = PriceField(source='delivery_price_CN')
    commission = PriceField(source='commission_rub')
    pickup = serializers.CharField(source='pickup_address')

    class Meta:
        model = GlobalSettings
        fields = ('yuan_rate', 'yuan_rate_last_updated', 'yuan_rate_commission', 'commission',  'chinadelivery', 'pickup', 'time_to_buy')
        read_only_fields = ('yuan_rate_last_updated',)


class AnonymousGlobalSettingsSerializer(GlobalSettingsSerializer):
    class Meta:
        model = GlobalSettingsSerializer.Meta.model
        fields = tuple(set(GlobalSettingsSerializer.Meta.fields) - {'yuan_rate_commission', 'yuan_rate_last_updated'})


class PaymentMethodSerializer(serializers.ModelSerializer):
    class Meta:
        model = PaymentMethod
        fields = ('slug', 'name', 'requisites', 'cardnumber')


class PromocodeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Promocode
        fields = ('id', 'name', 'discount', 'free_delivery', 'no_comission', 'is_active')
