from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework import status
from rest_framework.exceptions import PermissionDenied
from django.db import transaction
from django.db.models import F

from .models import Hotel, RoomType, RoomTypeAvailability, HotelBooking, SeasonalPricing
from .serializers import HotelSerializer, RoomTypeSerializer, HotelBookingSerializer, BookingInvoiceSerializer, SeasonalPricingSerializer
from .permissions import IsHotelOwner, IsOwnerOrAdminOrReadOnly, IsAdmin


class HotelViewSet(ModelViewSet):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def get_queryset(self):
        # Support filtering by owner for the dashboard
        owner_id = self.request.query_params.get('owner_id')
        if owner_id:
            # Check permissions? Or just filter? 
            # Ideally only admin or the simple owner should see their own, but public profile might need to see "hotels by this owner".
            # For now, allow it.
            return Hotel.objects.filter(owner_id=owner_id)
        
        # Default behavior: active hotels only, unless admin
        if self.request.user.is_superuser or getattr(self.request.user, 'role', '') == 'ADMIN':
             return Hotel.objects.all()
             
        return Hotel.objects.filter(is_active=True)

    def get_permissions(self):
        if self.action == 'create':
            return [IsAuthenticated(), IsAdmin()]
        elif self.action in ['update', 'partial_update', 'destroy', 'add_branch']:
            return [IsAuthenticated(), IsOwnerOrAdminOrReadOnly()]
        return [AllowAny()]

    def perform_create(self, serializer):
        # Admin can assign owner in payload, else default to self
        owner = serializer.validated_data.get('owner') or self.request.user
        serializer.save(owner=owner)

    @action(detail=False, methods=['get'], url_path='next-id')
    def next_id(self, request):
        from django.db.models import Max
        max_id = Hotel.objects.aggregate(max_id=Max('id'))['max_id']
        next_id = (max_id or 0) + 1
        return Response({'next_id': next_id})

    @action(detail=True, methods=['post'])
    def add_branch(self, request, pk=None):
        hotel = self.get_object()
        
        branch_name = request.data.get("branch")
        address = request.data.get("address")
        city = request.data.get("city")
        
        if not all([branch_name, address, city]):
            return Response(
                {"error": "branch, address, and city are required fields."}, 
                status=status.HTTP_400_BAD_REQUEST
            )
            
        new_branch = Hotel.objects.create(
            owner=hotel.owner,
            name=hotel.name,
            branch=branch_name,
            city=city,
            address=address,
            description=request.data.get("description", hotel.description),
            amenities=hotel.amenities,
            checkin_time=hotel.checkin_time,
            checkout_time=hotel.checkout_time,
            # Images are not automatically copied to avoid duplication issues or incorrect photos
        )
        
        serializer = self.get_serializer(new_branch)
        return Response(serializer.data, status=status.HTTP_201_CREATED)


class RoomTypeViewSet(ModelViewSet):
    queryset = RoomType.objects.all()
    serializer_class = RoomTypeSerializer
    
    def get_permissions(self):
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            # We don't have a specific object-level permission for RoomType that checks Hotel owner efficiently 
            # in standard classes without a query, but we can use IsHotelOwner (role check) | IsAdmin
            # And then validate object ownership in perform_create/update.
            return [IsAuthenticated()] # We will validate in logic
        return [AllowAny()]

    def perform_create(self, serializer):
        hotel = serializer.validated_data['hotel']
        user = self.request.user
        
        # Check permissions: Admin or Owner of the hotel
        if user.role != 'ADMIN' and not user.is_superuser:
            if user.role != 'HOTEL_OWNER':
                 raise PermissionDenied("Only Hotel Owners or Admins can create room types.")
            if hotel.owner != user:
                raise PermissionDenied("You can only add room types to hotels you own.")
                
        serializer.save()
        
    def perform_update(self, serializer):
        # Ensure user owns the hotel of this room type
        room_type = self.get_object()
        user = self.request.user
        
        if user.role != 'ADMIN' and not user.is_superuser:
            if room_type.hotel.owner != user:
                raise PermissionDenied("You can only edit room types for hotels you own.")
        
        serializer.save()

    def perform_destroy(self, instance):
        user = self.request.user
        if user.role != 'ADMIN' and not user.is_superuser:
             if instance.hotel.owner != user:
                raise PermissionDenied("You can only delete room types for hotels you own.")
        instance.delete()


class SeasonalPricingViewSet(ModelViewSet):
    queryset = SeasonalPricing.objects.all()
    serializer_class = SeasonalPricingSerializer
    
    def filter_queryset(self, queryset):
        room_type_id = self.request.query_params.get('room_type')
        if room_type_id:
            return queryset.filter(room_type_id=room_type_id)
        return super().filter_queryset(queryset)
        
    def get_permissions(self):
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            return [IsAuthenticated()]
        return [AllowAny()]

    def perform_create(self, serializer):
        room_type = serializer.validated_data['room_type']
        user = self.request.user
        
        if user.role != 'ADMIN' and not user.is_superuser:
            if room_type.hotel.owner != user:
                raise PermissionDenied("You can only add seasonal pricing to hotels you own.")
                
        serializer.save()

    def perform_update(self, serializer):
        seasonal_pricing = self.get_object()
        user = self.request.user
        
        if user.role != 'ADMIN' and not user.is_superuser:
            if seasonal_pricing.room_type.hotel.owner != user:
                raise PermissionDenied("You can only edit seasonal pricing for hotels you own.")
                
        serializer.save()

    def perform_destroy(self, instance):
        user = self.request.user
        if user.role != 'ADMIN' and not user.is_superuser:
             if instance.room_type.hotel.owner != user:
                raise PermissionDenied("You can only delete seasonal pricing for hotels you own.")
        instance.delete()


class HotelBookingViewSet(ModelViewSet):
    queryset = HotelBooking.objects.all()
    serializer_class = HotelBookingSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        if user.is_superuser or getattr(user, 'role', '') == 'ADMIN':
            return HotelBooking.objects.all()
        
        # If owner, return bookings for their hotels OR their own personal bookings?
        # Usually owner dashboard needs bookings for their hotels.
        if getattr(user, 'role', '') == 'HOTEL_OWNER':
            return HotelBooking.objects.filter(hotel__owner=user)
            
        return HotelBooking.objects.filter(user=user)

    def perform_create(self, serializer):
        # Set the hotel from room_type if not provided
        room_type = serializer.validated_data["room_type"]
        serializer.save(
            user=self.request.user,
            hotel=room_type.hotel,
            status="confirmed"
        )


from rest_framework.generics import RetrieveAPIView

class BookingInvoiceView(RetrieveAPIView):
    """
    Retrieve Invoice details for a specific booking.
    Only the User who made the booking or the Hotel Owner can view it.
    """
    queryset = HotelBooking.objects.all()
    serializer_class = BookingInvoiceSerializer
    permission_classes = [IsAuthenticated]

    def get_object(self):
        booking = super().get_object()
        user = self.request.user
        
        # Permission Check
        is_booker = booking.user == user
        is_owner = booking.hotel.owner == user
        is_admin = user.is_superuser or getattr(user, 'role', '') == 'ADMIN'
        
        if not (is_booker or is_owner or is_admin):
            raise PermissionDenied("You do not have permission to view this invoice.")
            
        return booking

from rest_framework.views import APIView

class SyncPMSHotelView(APIView):
    """
    API to manually trigger PMS Sync for a Hotel.
    """
    permission_classes = [IsAuthenticated, IsAdmin] # Restrict to Admins

    def post(self, request):
        is_aatithya = request.data.get("is_aatithya", False)
        pms_url = request.data.get("pms_sync_url")
        token = request.data.get("pms_sync_token", "")
        pms_branch_id = request.data.get("pms_branch_id")
        pms_hotel_id = request.data.get("pms_hotel_id")
        
        if not pms_url:
            return Response(
                {"error": "pms_sync_url is required."}, 
                status=status.HTTP_400_BAD_REQUEST
            )
            
        base_aatithya_url = None
        if is_aatithya:
            if not pms_branch_id:
                return Response(
                    {"error": "pms_branch_id is required for Aatithya integration."}, 
                    status=status.HTTP_400_BAD_REQUEST
                )
            base_aatithya_url = pms_url.rstrip('/')
            sync_url = f"{base_aatithya_url}/channel-manager/api/v1/hotelfinder/hotels/{pms_branch_id}/"
            token = ""
            pms_hotel_id = pms_branch_id
        else:
            sync_url = pms_url

        hotel_id = request.data.get("hotel_id")
        created = False
        
        if hotel_id:
            try:
                hotel = Hotel.objects.get(id=hotel_id)
                # Persist credentials to DB
                hotel.pms_sync_url = pms_url
                hotel.pms_sync_token = token
                if pms_hotel_id:
                    hotel.pms_hotel_id = pms_hotel_id
                if pms_branch_id:
                    hotel.pms_branch_id = pms_branch_id
                hotel.save()
            except Hotel.DoesNotExist:
                return Response({"error": "Hotel not found"}, status=status.HTTP_404_NOT_FOUND)
        else:
            # Create a shell hotel
            hotel = Hotel.objects.create(
                owner=request.user,
                name="New PMS Hotel",
                pms_sync_url=pms_url,
                pms_sync_token=token,
                pms_hotel_id=pms_hotel_id,
                pms_branch_id=pms_branch_id
            )
            created = True
            
        from .pms_sync import sync_one_hotel
        success, msg = sync_one_hotel(hotel, sync_url, token, pms_hotel_id=pms_hotel_id, pms_branch_id=pms_branch_id)
        
        if success:
            # Refresh to get updated data
            hotel.refresh_from_db()
            
            # If Aatithya mode, register the connection mapping back to Aatithya
            if is_aatithya and base_aatithya_url:
                mapping_url = f"{base_aatithya_url}/channel-manager/api/v1/hotelfinder/connection-mapping/"
                mapping_payload = {
                    "property_id": hotel.id,
                    "property_name": hotel.name,
                    "branch_id": int(pms_branch_id),
                    "hotelfinder_url": "http://localhost:8000/api/hotel",
                    "status": "ACTIVE"
                }
                try:
                    import requests
                    requests.post(mapping_url, json=mapping_payload, timeout=5)
                except Exception as ex:
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.warning(f"Failed to register connection mapping in Aatithya: {ex}")

            serializer = HotelSerializer(hotel)
            return Response(
                {
                    "message": f"Successfully synced. {msg}",
                    "hotel": serializer.data,
                    "created": created
                },
                status=status.HTTP_201_CREATED if created else status.HTTP_200_OK
            )
        else:
            if created:
                hotel.delete() # Cleanup if failed on create
            return Response({"error": msg}, status=status.HTTP_400_BAD_REQUEST)

class AatithyaWebhookReceiverView(APIView):
    """
    Receives webhooks from AAthitya.
    Specifically listens for booking.checked_out events to mark
    local HotelBooking as completed and notify clients via WebSockets.
    """
    permission_classes = [AllowAny] # In production, verify HMAC signature

    def post(self, request):
        event_type = request.headers.get('X-Webhook-Event') or request.data.get('event')
        
        if event_type == 'booking.checked_out':
            data = request.data.get('data', {})
            booking_id = data.get('id')
            
            if not booking_id:
                return Response({"error": "No booking ID provided"}, status=status.HTTP_400_BAD_REQUEST)
                
            try:
                # Assuming booking_id is stringified UUID or int matching local HotelBooking ID.
                # If they don't match directly, one might need to map by booking_number or pms_id.
                booking = HotelBooking.objects.get(id=booking_id)
                booking.status = 'completed'
                booking.save(update_fields=['status'])
                
                # Broadcast WebSocket update
                from asgiref.sync import async_to_sync
                from channels.layers import get_channel_layer
                channel_layer = get_channel_layer()
                
                async_to_sync(channel_layer.group_send)(
                    f'hotel_booking_{booking.id}',
                    {
                        'type': 'status_update',
                        'status': 'completed',
                        'booking_id': booking.id,
                        'message': 'Your booking has been checked out successfully.'
                    }
                )
                
                return Response({"message": "Booking updated to completed"}, status=status.HTTP_200_OK)
            except HotelBooking.DoesNotExist:
                return Response({"error": f"Booking {booking_id} not found"}, status=status.HTTP_404_NOT_FOUND)
                
        return Response({"message": "Event ignored"}, status=status.HTTP_200_OK)

