from rest_framework import serializers
from django.db import transaction
from django.db.models import F
from datetime import datetime, timedelta
from django.utils import timezone
from payments.models import Payment
from django.contrib.contenttypes.models import ContentType
import uuid
from .models import (
    Hotel,
    RoomType,
    RoomTypeAvailability,
    HotelBooking,
    RoomTypeImage,
    SeasonalPricing
)


class RoomTypeAvailabilitySerializer(serializers.ModelSerializer):
    class Meta:
        model = RoomTypeAvailability
        fields = ["date", "available_rooms"]


class SeasonalPricingSerializer(serializers.ModelSerializer):
    class Meta:
        model = SeasonalPricing
        fields = "__all__"




class RoomTypeImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = RoomTypeImage
        fields = ["id", "image"]


class RoomTypeSerializer(serializers.ModelSerializer):
    availability = RoomTypeAvailabilitySerializer(many=True, read_only=True)
    images = RoomTypeImageSerializer(many=True, read_only=True)
    seasonal_prices = SeasonalPricingSerializer(many=True, read_only=True)

    class Meta:
        model = RoomType
        fields = "__all__"


from django.contrib.auth import get_user_model
User = get_user_model()

class HotelSerializer(serializers.ModelSerializer):
    room_types = RoomTypeSerializer(many=True, read_only=True)
    owner = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(),
        required=False,
        allow_null=True
    )

    class Meta:
        model = Hotel
        fields = "__all__"
        # owner is not read_only so Admin can set it
        read_only_fields = []


class HotelBookingSerializer(serializers.ModelSerializer):
    class Meta:
        model = HotelBooking
        fields = "__all__"
        read_only_fields = ["user", "status", "branch"] 

    hotel_name = serializers.ReadOnlyField(source='hotel.name')
    room_type_name = serializers.ReadOnlyField(source='room_type.name')
    total_amount = serializers.ReadOnlyField()
    guest_display_name = serializers.SerializerMethodField()

    def get_guest_display_name(self, obj):
        if obj.guest_name:
            return obj.guest_name
        return obj.user.username

    def validate(self, data):
        # Basic date validation
        if data["check_out"] <= data["check_in"]:
            raise serializers.ValidationError(
                "Check-out date must be after check-in date."
            )

        # Check if check-in date is not in the past
        if data["check_in"] < datetime.now().date():
            raise serializers.ValidationError(
                "Check-in date cannot be in the past."
            )

        room_type = data["room_type"]
        check_in = data["check_in"]
        check_out = data["check_out"]
        rooms_requested = data["rooms_booked"]

        # Check if room type belongs to the hotel
        if data.get("hotel") and room_type.hotel != data["hotel"]:
            raise serializers.ValidationError(
                "Selected room type does not belong to the specified hotel."
            )

        # Check room availability for each night
        current_date = check_in
        unavailable_dates = []
        
        while current_date < check_out:
            try:
                availability = RoomTypeAvailability.objects.get(
                    room_type=room_type,
                    date=current_date
                )
                if availability.available_rooms < rooms_requested:
                    unavailable_dates.append(current_date.strftime('%Y-%m-%d'))
            except RoomTypeAvailability.DoesNotExist:
                unavailable_dates.append(current_date.strftime('%Y-%m-%d'))
            
            current_date += timedelta(days=1)

        if unavailable_dates:
            raise serializers.ValidationError(
                f"Not enough rooms available on the following dates: {', '.join(unavailable_dates)}. "
                f"Requested: {rooms_requested} rooms."
            )

        return data

    def create(self, validated_data):
        # Use transaction to ensure atomicity
        with transaction.atomic():
            hotel = validated_data["hotel"]
            room_type = validated_data["room_type"]
            check_in = validated_data["check_in"]
            check_out = validated_data["check_out"]
            rooms_requested = validated_data["rooms_booked"]

            # Lock the availability rows and reduce availability date-wise
            current_date = check_in
            availability_objs = []

            while current_date < check_out:
                try:
                    # select_for_update() locks the row until the transaction completes
                    availability = RoomTypeAvailability.objects.select_for_update().get(
                        room_type=room_type,
                        date=current_date
                    )
                    if availability.available_rooms < rooms_requested:
                        raise serializers.ValidationError(
                            f"Not enough rooms available on {current_date}. Only {availability.available_rooms} left."
                        )
                    
                    availability.available_rooms -= rooms_requested
                    availability_objs.append(availability)
                except RoomTypeAvailability.DoesNotExist:
                    raise serializers.ValidationError(f"No availability set for {current_date}")
                
                current_date += timedelta(days=1)
            
            # Bulk update the availability records
            RoomTypeAvailability.objects.bulk_update(availability_objs, ['available_rooms'])

            # Create the booking - validation happens in validate() and model clean()
            booking = HotelBooking.objects.create(**validated_data,
            branch=hotel.branch)
            
            # Create Payment Record
            amount = booking.total_amount
            order_id = str(uuid.uuid4())
            Payment.objects.create(
                content_type=ContentType.objects.get_for_model(HotelBooking),
                object_id=booking.id,
                amount=amount,
                transaction_id=order_id,
                payment_method="Paytm-Mock",
                status="pending"
            )

            # Generate Invoice if confirmed
            if booking.status == "confirmed" and not booking.invoice_number:
                today = timezone.now().date()
                invoice_no = f"INV-{today.strftime('%Y%m%d')}-{booking.id}"
                booking.invoice_number = invoice_no
                booking.invoice_date = today
                booking.save(update_fields=['invoice_number', 'invoice_date'])

            return booking


class BookingInvoiceSerializer(serializers.ModelSerializer):
    hotel_name = serializers.ReadOnlyField(source='hotel.name')
    hotel_address = serializers.ReadOnlyField(source='hotel.address')
    hotel_city = serializers.ReadOnlyField(source='hotel.city')
    hotel_gstin = serializers.ReadOnlyField(source='hotel.gstin')
    hotel_pan = serializers.ReadOnlyField(source='hotel.pan')
    
    room_type_name = serializers.ReadOnlyField(source='room_type.name')
    total_amount = serializers.ReadOnlyField()
    
    class Meta:
        model = HotelBooking
        fields = [
            'id', 'invoice_number', 'invoice_date', 'created_at',
            'hotel_name', 'hotel_address', 'hotel_city', 'hotel_gstin', 'hotel_pan',
            'guest_name', 'email', 'mobile',
            'check_in', 'check_out', 'room_type_name', 'rooms_booked',
            'total_amount', 'status'
        ]
