from decimal import Decimal

from rest_framework import serializers
from apps.fields.models import Field
from apps.materials.models import StockTransaction
from .models import Crop, Variety, Plan
from .models import RiceTransplantEntry, RiceTransplantPlan
from .services import NO_CHANGE, update_plan_with_variety_tracking


class VarietySerializer(serializers.ModelSerializer):
    seed_material_name = serializers.CharField(source='seed_material.name', read_only=True)

    class Meta:
        model = Variety
        fields = [
            'id',
            'crop',
            'name',
            'default_seedling_boxes_per_tan',
            'seed_material',
            'seed_material_name',
        ]


class CropSerializer(serializers.ModelSerializer):
    varieties = VarietySerializer(many=True, read_only=True)

    class Meta:
        model = Crop
        fields = '__all__'


class PlanSerializer(serializers.ModelSerializer):
    crop_name = serializers.ReadOnlyField(source='crop.name')
    variety_name = serializers.ReadOnlyField(source='variety.name')
    field_name = serializers.ReadOnlyField(source='field.name')
    variety_change_count = serializers.SerializerMethodField()
    latest_variety_change = serializers.SerializerMethodField()

    class Meta:
        model = Plan
        fields = '__all__'
        read_only_fields = ('id', 'created_at', 'updated_at')

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

    def update(self, instance, validated_data):
        return update_plan_with_variety_tracking(
            instance,
            crop=validated_data.get('crop', NO_CHANGE),
            variety=validated_data.get('variety', NO_CHANGE),
            notes=validated_data.get('notes', NO_CHANGE),
        )

    def get_variety_change_count(self, obj):
        prefetched = getattr(obj, '_prefetched_objects_cache', {})
        changes = prefetched.get('variety_changes')
        if changes is not None:
            return len(changes)
        return obj.variety_changes.count()

    def get_latest_variety_change(self, obj):
        prefetched = getattr(obj, '_prefetched_objects_cache', {})
        changes = prefetched.get('variety_changes')
        if changes is not None:
            latest = changes[0] if changes else None
        else:
            latest = obj.variety_changes.select_related('old_variety', 'new_variety').first()
        if latest is None:
            return None
        return {
            'id': latest.id,
            'changed_at': latest.changed_at,
            'old_variety_id': latest.old_variety_id,
            'old_variety_name': latest.old_variety.name if latest.old_variety else None,
            'new_variety_id': latest.new_variety_id,
            'new_variety_name': latest.new_variety.name if latest.new_variety else None,
            'fertilizer_moved_entry_count': latest.fertilizer_moved_entry_count,
        }


class RiceTransplantEntrySerializer(serializers.ModelSerializer):
    field_name = serializers.CharField(source='field.name', read_only=True)
    field_area_tan = serializers.DecimalField(
        source='field.area_tan',
        max_digits=6,
        decimal_places=4,
        read_only=True,
    )
    planned_boxes = serializers.SerializerMethodField()
    default_seedling_boxes = serializers.SerializerMethodField()

    class Meta:
        model = RiceTransplantEntry
        fields = [
            'id',
            'field',
            'field_name',
            'field_area_tan',
            'installed_seedling_boxes',
            'default_seedling_boxes',
            'planned_boxes',
        ]

    def get_default_seedling_boxes(self, obj):
        area = Decimal(str(obj.field.area_tan))
        default_boxes_per_tan = obj.plan.seedling_boxes_per_tan
        return str((area * default_boxes_per_tan).quantize(Decimal('0.01')))

    def get_planned_boxes(self, obj):
        return str(obj.installed_seedling_boxes.quantize(Decimal('0.01')))

class RiceTransplantPlanSerializer(serializers.ModelSerializer):
    variety_name = serializers.CharField(source='variety.name', read_only=True)
    crop_name = serializers.CharField(source='variety.crop.name', read_only=True)
    seed_material_name = serializers.CharField(source='variety.seed_material.name', read_only=True)
    entries = RiceTransplantEntrySerializer(many=True, read_only=True)
    field_count = serializers.SerializerMethodField()
    total_seedling_boxes = serializers.SerializerMethodField()
    total_seed_kg = serializers.SerializerMethodField()
    variety_seed_inventory_kg = serializers.SerializerMethodField()
    remaining_seed_kg = serializers.SerializerMethodField()

    class Meta:
        model = RiceTransplantPlan
        fields = [
            'id',
            'name',
            'year',
            'variety',
            'variety_name',
            'crop_name',
            'default_seed_grams_per_box',
            'seedling_boxes_per_tan',
            'notes',
            'seed_material_name',
            'entries',
            'field_count',
            'total_seedling_boxes',
            'total_seed_kg',
            'variety_seed_inventory_kg',
            'remaining_seed_kg',
            'created_at',
            'updated_at',
        ]

    def get_field_count(self, obj):
        return obj.entries.count()

    def get_total_seedling_boxes(self, obj):
        total = sum(
            (
                entry.installed_seedling_boxes
                for entry in obj.entries.all()
            ),
            Decimal('0'),
        )
        return str(total.quantize(Decimal('0.01')))

    def get_total_seed_kg(self, obj):
        total = sum(
            (
                (
                    entry.installed_seedling_boxes
                    * obj.default_seed_grams_per_box
                    / Decimal('1000')
                )
                for entry in obj.entries.all()
            ),
            Decimal('0'),
        )
        return str(total.quantize(Decimal('0.001')))

    def get_variety_seed_inventory_kg(self, obj):
        return str(self._get_seed_inventory_kg(obj).quantize(Decimal('0.001')))

    def get_remaining_seed_kg(self, obj):
        total_seed = Decimal(self.get_total_seed_kg(obj))
        return str((self._get_seed_inventory_kg(obj) - total_seed).quantize(Decimal('0.001')))

    def _get_seed_inventory_kg(self, obj):
        material = obj.variety.seed_material
        if material is None:
            return Decimal('0')

        transactions = list(material.stock_transactions.all())
        increase = sum(
            (
                txn.quantity
                for txn in transactions
                if txn.transaction_type in StockTransaction.INCREASE_TYPES
            ),
            Decimal('0'),
        )
        decrease = sum(
            (
                txn.quantity
                for txn in transactions
                if txn.transaction_type in StockTransaction.DECREASE_TYPES
            ),
            Decimal('0'),
        )
        return increase - decrease


class RiceTransplantPlanWriteSerializer(serializers.ModelSerializer):
    entries = serializers.ListField(child=serializers.DictField(), write_only=True, required=False)

    class Meta:
        model = RiceTransplantPlan
        fields = [
            'id',
            'name',
            'year',
            'variety',
            'default_seed_grams_per_box',
            'seedling_boxes_per_tan',
            'notes',
            'entries',
        ]

    def create(self, validated_data):
        entries_data = validated_data.pop('entries', [])
        plan = RiceTransplantPlan.objects.create(**validated_data)
        self._save_entries(plan, entries_data)
        return plan

    def update(self, instance, validated_data):
        entries_data = validated_data.pop('entries', None)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        if entries_data is not None:
            instance.entries.all().delete()
            self._save_entries(instance, entries_data)
        return instance

    def validate(self, attrs):
        entries_data = attrs.get('entries')
        if entries_data is None:
            return attrs

        field_ids = [entry.get('field_id') for entry in entries_data if entry.get('field_id') is not None]
        existing_ids = set(Field.objects.filter(id__in=field_ids).values_list('id', flat=True))
        missing_ids = sorted(set(field_ids) - existing_ids)
        if missing_ids:
            raise serializers.ValidationError({
                'entries': f'存在しない圃場IDが含まれています: {", ".join(str(field_id) for field_id in missing_ids)}'
            })
        return attrs

    def _save_entries(self, plan, entries_data):
        for entry in entries_data:
            RiceTransplantEntry.objects.create(
                plan=plan,
                field_id=entry['field_id'],
                installed_seedling_boxes=entry['installed_seedling_boxes'],
                seed_grams_per_box=plan.default_seed_grams_per_box,
            )
