from django.db import transaction
from decimal import Decimal
from rest_framework import serializers

from apps.plans.models import Plan
from apps.workrecords.services import sync_levee_work_record
from .models import LeveeWorkSession, LeveeWorkSessionItem


class LeveeWorkSessionItemReadSerializer(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,
    )
    group_name = serializers.CharField(source='field.group_name', read_only=True, allow_null=True)

    class Meta:
        model = LeveeWorkSessionItem
        fields = [
            'id',
            'field',
            'field_name',
            'field_area_tan',
            'group_name',
            'plan',
            'crop_name_snapshot',
            'variety_name_snapshot',
        ]


class LeveeWorkSessionSerializer(serializers.ModelSerializer):
    items = LeveeWorkSessionItemReadSerializer(many=True, read_only=True)
    work_record_id = serializers.IntegerField(source='work_record.id', read_only=True)
    item_count = serializers.SerializerMethodField()
    total_area_tan = serializers.SerializerMethodField()

    class Meta:
        model = LeveeWorkSession
        fields = [
            'id',
            'year',
            'date',
            'title',
            'notes',
            'work_record_id',
            'item_count',
            'total_area_tan',
            'items',
            'created_at',
            'updated_at',
        ]

    def get_item_count(self, obj):
        return len(obj.items.all())

    def get_total_area_tan(self, obj):
        total = sum((item.field.area_tan or Decimal('0')) for item in obj.items.all())
        return str(total)


class LeveeWorkSessionItemWriteInputSerializer(serializers.Serializer):
    field = serializers.IntegerField()
    plan = serializers.IntegerField(required=False, allow_null=True)


class LeveeWorkSessionWriteSerializer(serializers.ModelSerializer):
    items = LeveeWorkSessionItemWriteInputSerializer(many=True, write_only=True)

    class Meta:
        model = LeveeWorkSession
        fields = ['id', 'year', 'date', 'title', 'notes', 'items']

    def validate(self, attrs):
        year = attrs.get('year', getattr(self.instance, 'year', None))
        date = attrs.get('date', getattr(self.instance, 'date', None))
        if year is not None and date is not None and year != date.year:
            raise serializers.ValidationError({'year': 'year は date.year と一致させてください。'})
        return attrs

    def validate_items(self, value):
        if not value:
            raise serializers.ValidationError('items を1件以上指定してください。')
        seen = set()
        for item in value:
            key = item['field']
            if key in seen:
                raise serializers.ValidationError('同一 session 内で同じ圃場を重複登録できません。')
            seen.add(key)
        return value

    @transaction.atomic
    def create(self, validated_data):
        items_data = validated_data.pop('items', [])
        validated_data['title'] = (validated_data.get('title') or '').strip() or '水稲畔塗'
        session = LeveeWorkSession.objects.create(**validated_data)
        self._replace_items(session, items_data)
        sync_levee_work_record(session)
        return session

    @transaction.atomic
    def update(self, instance, validated_data):
        items_data = validated_data.pop('items', None)
        for attr, value in validated_data.items():
            if attr == 'title':
                value = (value or '').strip() or '水稲畔塗'
            setattr(instance, attr, value)
        if 'title' not in validated_data:
            instance.title = (instance.title or '').strip() or '水稲畔塗'
        instance.save()
        if items_data is not None:
            self._replace_items(instance, items_data)
        sync_levee_work_record(instance)
        return instance

    def _replace_items(self, session, items_data):
        session.items.all().delete()
        for item in items_data:
            plan = self._resolve_plan(session.year, item['field'], item.get('plan'))
            LeveeWorkSessionItem.objects.create(
                session=session,
                field_id=item['field'],
                plan=plan,
                crop_name_snapshot=plan.crop.name,
                variety_name_snapshot=plan.variety.name if plan.variety else '',
            )

    def _resolve_plan(self, year, field_id, plan_id):
        queryset = Plan.objects.select_related('crop', 'variety').filter(
            year=year,
            field_id=field_id,
            crop__name='水稲',
        )
        if plan_id is not None:
            try:
                return queryset.get(id=plan_id)
            except Plan.DoesNotExist as exc:
                raise serializers.ValidationError(
                    {'items': f'field={field_id} に対応する水稲作付け計画(plan={plan_id})が見つかりません。'}
                ) from exc

        plan = queryset.first()
        if plan is None:
            raise serializers.ValidationError(
                {'items': f'field={field_id} は当年の水稲作付け圃場ではありません。'}
            )
        return plan
