from rest_framework import serializers
from .models import Booking
from django.db import transaction
from decimal import Decimal, InvalidOperation


class BookingSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta:
        model = Booking
        fields = (
            'id', 'booking_id', 'user', 'quick_schedule', 'booking_status',
            'pickup_type',
            'schedule_pickup_date', 'schedule_pickup_time',
            'contact_name', 'contact_mobile', 'pickup_instructions',
            'pickup_address', 'geo_location', 
            'order_details', 'premium_details', 'luxury_details',
            'total_quantity', 'total_final_price', 'currency_code',
            'trash', 'created_at', 'updated_at'
        )
        read_only_fields = ('id', 'booking_id', 'user', 'created_at', 'updated_at')

    def _validate_details(self, details, field_name):
        quantity = 0
        price = Decimal('0.00')
        found_item = False
        
        if not details:
            return 0, Decimal('0.00'), False

        if not isinstance(details, dict):
            raise serializers.ValidationError({field_name: 'Must be an object.'})

        for service_key, service_val in details.items():
            if not isinstance(service_val, dict):
                raise serializers.ValidationError({field_name: f'Service "{service_key}" must map to an object of categories.'})
            for category_key, items in service_val.items():
                if not isinstance(items, list):
                    raise serializers.ValidationError({field_name: f'Category "{category_key}" under service "{service_key}" must be a list of items.'})
                for idx, item in enumerate(items):
                    found_item = True
                    if not isinstance(item, dict):
                        raise serializers.ValidationError({field_name: f'Item at index {idx} in "{service_key}.{category_key}" must be an object.'})
                    # required fields per item
                    required_fields = ['item_id', 'item_name', 'item_service', 'item_quantity']
                    # For standard order_details, price is required. For premium/luxury, it is optional (staff adds it).
                    if field_name == 'order_details':
                        required_fields.extend(['unit_price', 'total_item_price'])

                    for field in required_fields:
                        if field not in item:
                            raise serializers.ValidationError({field_name: f'Item in "{service_key}.{category_key}" missing required field: {field}.'})

                    # accumulate totals
                    try:
                        qty = int(item.get('item_quantity', 0))
                    except (ValueError, TypeError):
                        raise serializers.ValidationError({field_name: f'Invalid item_quantity for item {item.get("item_id")}'})
                    try:
                        p = Decimal(str(item.get('total_item_price', '0')))
                    except (InvalidOperation, TypeError):
                        raise serializers.ValidationError({field_name: f'Invalid total_item_price for item {item.get("item_id")}'})

                    quantity += qty
                    price += p
        
        return quantity, price, found_item

    def validate(self, attrs):
        # Basic validation: require at least one item across all detail fields
        order_details = attrs.get('order_details') or (self.instance and self.instance.order_details)
        premium_details = attrs.get('premium_details') or (self.instance and self.instance.premium_details)
        luxury_details = attrs.get('luxury_details') or (self.instance and self.instance.luxury_details)

        q1, p1, f1 = self._validate_details(order_details, 'order_details')
        q2, p2, f2 = self._validate_details(premium_details, 'premium_details')
        q3, p3, f3 = self._validate_details(luxury_details, 'luxury_details')

        # Removed validation that required at least one item to be present.
        # All 3 detail fields are now optional.

        total_quantity = q1 + q2 + q3
        total_price = p1 + p2 + p3

        # If totals provided in payload, validate they match computed totals
        payload_total_qty = attrs.get('total_quantity')
        payload_total_price = attrs.get('total_final_price')

        if payload_total_qty is not None:
            try:
                if int(payload_total_qty) != total_quantity:
                    raise serializers.ValidationError({'total_quantity': 'Does not match sum of item quantities in details.'})
            except (ValueError, TypeError):
                raise serializers.ValidationError({'total_quantity': 'Invalid total_quantity provided.'})

        if payload_total_price is not None:
            try:
                if Decimal(str(payload_total_price)) != total_price:
                    raise serializers.ValidationError({'total_final_price': 'Does not match sum of item total_item_price in details.'})
            except (InvalidOperation, TypeError):
                raise serializers.ValidationError({'total_final_price': 'Invalid total_final_price provided.'})

        # If not provided, set computed totals into attrs so create() will save them
        if attrs.get('total_quantity') is None:
            attrs['total_quantity'] = total_quantity
        if attrs.get('total_final_price') is None:
            attrs['total_final_price'] = total_price

        return attrs

    @transaction.atomic
    def create(self, validated_data):
        request = self.context.get('request')
        user = getattr(request, 'user', None)
        if user is None or user.is_anonymous:
            raise serializers.ValidationError('Authentication required to create booking')

        validated_data['user'] = user
        # Ensure total_final_price is a Decimal instance
        tfp = validated_data.get('total_final_price')
        if tfp is not None and not isinstance(tfp, Decimal):
            try:
                validated_data['total_final_price'] = Decimal(str(tfp))
            except (InvalidOperation, TypeError):
                raise serializers.ValidationError({'total_final_price': 'Invalid total_final_price value.'})

        booking = Booking.objects.create(**validated_data)
        
        # Automatically generate tags for items
        booking.generate_item_tags()
        
        return booking

    @transaction.atomic
    def update(self, instance, validated_data):
        # For now, allow default ModelSerializer update behavior
        return super().update(instance, validated_data)
