from decimal import Decimal

from django.db import transaction
from rest_framework import serializers

from .models import (
    FertilizerProfile,
    Material,
    PesticideProfile,
    StockTransaction,
)


class FertilizerProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = FertilizerProfile
        fields = ['capacity_kg', 'nitrogen_pct', 'phosphorus_pct', 'potassium_pct']


class PesticideProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = PesticideProfile
        fields = [
            'registration_no',
            'formulation',
            'usage_unit',
            'dilution_ratio',
            'active_ingredient',
            'category',
        ]


class MaterialReadSerializer(serializers.ModelSerializer):
    material_type_display = serializers.CharField(
        source='get_material_type_display',
        read_only=True,
    )
    stock_unit_display = serializers.CharField(
        source='get_stock_unit_display',
        read_only=True,
    )
    fertilizer_profile = FertilizerProfileSerializer(read_only=True)
    pesticide_profile = PesticideProfileSerializer(read_only=True)
    current_stock = serializers.SerializerMethodField()

    class Meta:
        model = Material
        fields = [
            'id',
            'name',
            'material_type',
            'material_type_display',
            'maker',
            'stock_unit',
            'stock_unit_display',
            'is_active',
            'notes',
            'fertilizer_profile',
            'pesticide_profile',
            'current_stock',
            'created_at',
            'updated_at',
        ]

    def get_current_stock(self, obj):
        transactions = list(obj.stock_transactions.all())
        increase = sum(
            transaction.quantity
            for transaction in transactions
            if transaction.transaction_type in StockTransaction.INCREASE_TYPES
        )
        decrease = sum(
            transaction.quantity
            for transaction in transactions
            if transaction.transaction_type in StockTransaction.DECREASE_TYPES
        )
        return increase - decrease


class MaterialWriteSerializer(serializers.ModelSerializer):
    fertilizer_profile = FertilizerProfileSerializer(required=False, allow_null=True)
    pesticide_profile = PesticideProfileSerializer(required=False, allow_null=True)

    class Meta:
        model = Material
        fields = [
            'id',
            'name',
            'material_type',
            'maker',
            'stock_unit',
            'is_active',
            'notes',
            'fertilizer_profile',
            'pesticide_profile',
        ]

    def validate(self, attrs):
        material_type = attrs.get('material_type')
        if self.instance is not None and material_type is None:
            material_type = self.instance.material_type

        fertilizer_profile = attrs.get('fertilizer_profile')
        pesticide_profile = attrs.get('pesticide_profile')

        if material_type == Material.MaterialType.FERTILIZER and pesticide_profile:
            raise serializers.ValidationError(
                {'pesticide_profile': '肥料には農薬詳細を設定できません。'}
            )
        if material_type == Material.MaterialType.PESTICIDE and fertilizer_profile:
            raise serializers.ValidationError(
                {'fertilizer_profile': '農薬には肥料詳細を設定できません。'}
            )
        if (
            material_type in {
                Material.MaterialType.SEED,
                Material.MaterialType.SEEDLING,
                Material.MaterialType.OTHER,
            }
            and (fertilizer_profile or pesticide_profile)
        ):
            raise serializers.ValidationError(
                '種子・種苗・その他には詳細プロファイルを設定できません。'
            )
        return attrs

    @transaction.atomic
    def create(self, validated_data):
        fertilizer_profile_data = validated_data.pop('fertilizer_profile', None)
        pesticide_profile_data = validated_data.pop('pesticide_profile', None)

        material = Material.objects.create(**validated_data)
        self._save_profiles(material, fertilizer_profile_data, pesticide_profile_data)
        return material

    @transaction.atomic
    def update(self, instance, validated_data):
        fertilizer_profile_data = validated_data.pop('fertilizer_profile', None)
        pesticide_profile_data = validated_data.pop('pesticide_profile', None)

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

        self._save_profiles(instance, fertilizer_profile_data, pesticide_profile_data)
        return instance

    def to_representation(self, instance):
        return MaterialReadSerializer(instance, context=self.context).data

    def _save_profiles(self, material, fertilizer_profile_data, pesticide_profile_data):
        if material.material_type == Material.MaterialType.FERTILIZER:
            if fertilizer_profile_data is not None:
                profile, _ = FertilizerProfile.objects.get_or_create(material=material)
                for attr, value in fertilizer_profile_data.items():
                    setattr(profile, attr, value)
                profile.save()
            PesticideProfile.objects.filter(material=material).delete()
            return

        if material.material_type == Material.MaterialType.PESTICIDE:
            if pesticide_profile_data is not None:
                profile, _ = PesticideProfile.objects.get_or_create(material=material)
                for attr, value in pesticide_profile_data.items():
                    setattr(profile, attr, value)
                profile.save()
            FertilizerProfile.objects.filter(material=material).delete()
            return

        FertilizerProfile.objects.filter(material=material).delete()
        PesticideProfile.objects.filter(material=material).delete()


class StockTransactionSerializer(serializers.ModelSerializer):
    material_name = serializers.CharField(source='material.name', read_only=True)
    material_type = serializers.CharField(source='material.material_type', read_only=True)
    stock_unit = serializers.CharField(source='material.stock_unit', read_only=True)
    stock_unit_display = serializers.CharField(
        source='material.get_stock_unit_display',
        read_only=True,
    )
    transaction_type_display = serializers.CharField(
        source='get_transaction_type_display',
        read_only=True,
    )
    is_locked = serializers.SerializerMethodField()

    class Meta:
        model = StockTransaction
        fields = [
            'id',
            'material',
            'material_name',
            'material_type',
            'transaction_type',
            'transaction_type_display',
            'quantity',
            'stock_unit',
            'stock_unit_display',
            'occurred_on',
            'note',
            'fertilization_plan',
            'spreading_item',
            'is_locked',
            'created_at',
        ]
        read_only_fields = ['created_at']

    def get_is_locked(self, obj):
        return bool(obj.fertilization_plan_id or obj.spreading_item_id)


class StockSummarySerializer(serializers.Serializer):
    material_id = serializers.IntegerField()
    name = serializers.CharField()
    material_type = serializers.CharField()
    material_type_display = serializers.CharField()
    maker = serializers.CharField()
    stock_unit = serializers.CharField()
    stock_unit_display = serializers.CharField()
    is_active = serializers.BooleanField()
    current_stock = serializers.DecimalField(max_digits=10, decimal_places=3)
    reserved_stock = serializers.DecimalField(max_digits=10, decimal_places=3)
    available_stock = serializers.DecimalField(max_digits=10, decimal_places=3)
    last_transaction_date = serializers.DateField(allow_null=True)
