from rest_framework import generics, permissions, status, filters
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.exceptions import ValidationError
from django_filters.rest_framework import DjangoFilterBackend
from django.shortcuts import get_object_or_404

from datetime import datetime, timedelta
from decimal import Decimal
from django.db.models import Q
from .models import Restaurant, MenuItem, Booking, BookingItem, Table, AaharConfig, AaharOrderSync, DeliveryLocationLog, Offer, Combo, ComboItem, SyankoConfiguration, MenuCategory, ItemVariant, ItemFeature
from .serializers import (
    RestaurantSerializer, RestaurantListSerializer,
    MenuItemSerializer, GlobalMenuItemSerializer, BookingSerializer, BookingCreateSerializer,
    BookingStatusUpdateSerializer, DeliveryCheckSerializer,
    DeliveryCheckResponseSerializer, TableSerializer,
    AaharConfigSerializer, AaharOrderSyncSerializer, AaharLinkRequestSerializer,
    AaharPaymentUpdateSerializer, AaharFetchOrdersRequestSerializer,
    DeliveryLocationUpdateSerializer, DeliveryTrackingSerializer, OfferSerializer,
    ComboSerializer, MenuCategorySerializer, ItemVariantSerializer, ItemFeatureSerializer,
)

from .permissions import IsRestaurantOwner, IsOwnerOrAdminOrReadOnly, IsAdmin, IsCustomer, IsRestaurantOwnerOrAdmin
from .utils import check_delivery_availability, estimate_delivery_time, calculate_delivery_charge
from . import aahar_service
from .aahar_service import AaharServiceError


# ==================== Restaurant Views ====================

class RestaurantListCreateAPIView(generics.ListCreateAPIView):
    """
    GET: List all restaurants (public)
    POST: Create restaurant (Admin/Restaurant Owner only)
    """
    queryset = Restaurant.objects.filter(is_active=True).order_by('-created_at')
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['name', 'cuisine', 'location']
    ordering_fields = ['rating', 'created_at', 'name']
    filterset_fields = ['cuisine', 'is_delivery_available', 'is_dine_in_available', 'is_takeaway_available', 'owner']
    
    def get_serializer_class(self):
        if self.request.method == 'GET':
            return RestaurantListSerializer
        return RestaurantSerializer
    
    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated(), IsAdmin()]
        return [permissions.AllowAny()]

    def perform_create(self, serializer):
        serializer.save()


class RestaurantDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    GET: Get restaurant details (public)
    PUT/PATCH: Update restaurant (Owner/Admin only)
    DELETE: Delete restaurant (Admin only)
    """
    queryset = Restaurant.objects.all()
    serializer_class = RestaurantSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrAdminOrReadOnly]


class NearbyRestaurantsView(APIView):
    """
    GET: Get restaurants within a radius of customer location
    Query params: latitude, longitude, radius_km (default 5)
    """
    permission_classes = [permissions.AllowAny]
    
    def get(self, request):
        lat = request.query_params.get('latitude')
        lon = request.query_params.get('longitude')
        radius = float(request.query_params.get('radius_km', 5))
        
        if not lat or not lon:
            return Response(
                {'error': 'latitude and longitude are required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Get all restaurants with GPS coordinates
        restaurants = Restaurant.objects.filter(
            is_active=True,
            latitude__isnull=False,
            longitude__isnull=False
        )
        
        # Filter by distance
        nearby = []
        for restaurant in restaurants:
            delivery_info = check_delivery_availability(restaurant, lat, lon)
            if delivery_info['distance_km'] and delivery_info['distance_km'] <= radius:
                restaurant_data = RestaurantListSerializer(restaurant).data
                restaurant_data['distance_km'] = delivery_info['distance_km']
                nearby.append(restaurant_data)
        
        # Sort by distance
        nearby.sort(key=lambda x: x['distance_km'])
        
        return Response(nearby)


# ==================== Menu Views ====================

class MenuItemListCreateView(generics.ListCreateAPIView):
    """
    GET: List menu items for a restaurant (public)
    POST: Add menu item (Owner/Admin only)
    """
    serializer_class = MenuItemSerializer
    
    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        qs = MenuItem.objects.filter(restaurant_id=restaurant_id)
        
        restaurant = Restaurant.objects.filter(id=restaurant_id).first()
        if restaurant and (not hasattr(restaurant, 'aahar_config') or not restaurant.aahar_config.is_active):
            qs = qs.filter(aahar_item_id__isnull=True)
        
        # If user is the owner or admin, they can see all items (including unavailable ones)
        if self.request.user.is_authenticated:
            if self.request.user.is_superuser or self.request.user.role == 'ADMIN':
                return qs
            # Check if user is the restaurant owner
            if restaurant and restaurant.owner == self.request.user:
                return qs
                
        # Public users only see available items
        return qs.filter(is_available=True)
    
    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated(), IsOwnerOrAdminOrReadOnly()]
        return [permissions.AllowAny()]
    
    def perform_create(self, serializer):
        restaurant_id = self.kwargs.get('restaurant_id')
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)
        item = serializer.save(restaurant=restaurant)
        f = self.request.FILES.get('image')
        if f:
            content = _process_menu_image(f)
            if item.custom_image:
                item.custom_image.delete(save=False)
            item.custom_image.save(f'menu_{item.id}.jpg', content, save=True)


class MenuItemDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    GET: Get menu item details (public)
    PUT/PATCH: Update menu item (Owner/Admin only)
    DELETE: Delete menu item (Owner/Admin only)
    """
    serializer_class = MenuItemSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrAdminOrReadOnly]
    
    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        return MenuItem.objects.filter(restaurant_id=restaurant_id)

    def perform_update(self, serializer):
        item = serializer.save()
        f = self.request.FILES.get('image')
        if f:
            content = _process_menu_image(f)
            if item.custom_image:
                item.custom_image.delete(save=False)
            item.custom_image.save(f'menu_{item.id}.jpg', content, save=True)


class MenuCategoryListCreateView(generics.ListCreateAPIView):
    """
    GET: List menu categories for a restaurant (public)
    POST: Add menu category (Owner/Admin only)
    """
    serializer_class = MenuCategorySerializer

    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        return MenuCategory.objects.filter(restaurant_id=restaurant_id)

    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated(), IsOwnerOrAdminOrReadOnly()]
        return [permissions.AllowAny()]

    def perform_create(self, serializer):
        restaurant_id = self.kwargs.get('restaurant_id')
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)
        serializer.save(restaurant=restaurant)


class MenuCategoryDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    Retrieve, Update, Delete a menu category.
    """
    serializer_class = MenuCategorySerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrAdminOrReadOnly]

    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        return MenuCategory.objects.filter(restaurant_id=restaurant_id)


class ItemVariantListCreateView(generics.ListCreateAPIView):
    """
    List or create variants for a menu item.
    """
    serializer_class = ItemVariantSerializer

    def get_queryset(self):
        item_id = self.kwargs.get('item_id')
        return ItemVariant.objects.filter(menu_item_id=item_id)

    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated()]
        return [permissions.AllowAny()]

    def perform_create(self, serializer):
        item_id = self.kwargs.get('item_id')
        menu_item = get_object_or_404(MenuItem, pk=item_id)
        serializer.save(menu_item=menu_item)


class ItemVariantDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    Retrieve, update, delete an item variant.
    """
    serializer_class = ItemVariantSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def get_queryset(self):
        item_id = self.kwargs.get('item_id')
        return ItemVariant.objects.filter(menu_item_id=item_id)


class ItemFeatureListCreateView(generics.ListCreateAPIView):
    """
    List or create features/flavors for a menu item.
    """
    serializer_class = ItemFeatureSerializer

    def get_queryset(self):
        item_id = self.kwargs.get('item_id')
        return ItemFeature.objects.filter(menu_item_id=item_id)

    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated()]
        return [permissions.AllowAny()]

    def perform_create(self, serializer):
        item_id = self.kwargs.get('item_id')
        menu_item = get_object_or_404(MenuItem, pk=item_id)
        serializer.save(menu_item=menu_item)


class ItemFeatureDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    Retrieve, update, delete an item feature.
    """
    serializer_class = ItemFeatureSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def get_queryset(self):
        item_id = self.kwargs.get('item_id')
        return ItemFeature.objects.filter(menu_item_id=item_id)


def _process_menu_image(uploaded):
    """Validate an uploaded image with Pillow and downscale to <=1024px, re-encoded as JPEG."""
    # ponytail: validate + downscale to 1024px. Bump the cap if larger images are ever needed.
    from PIL import Image, UnidentifiedImageError
    from io import BytesIO
    from django.core.files.base import ContentFile
    try:
        Image.open(uploaded).verify()   # detect corrupt / non-image
        uploaded.seek(0)                # verify() exhausts the file; reopen to actually read it
        img = Image.open(uploaded).convert('RGB')
    except (UnidentifiedImageError, OSError):
        raise ValidationError('Uploaded file is not a valid image.')
    img.thumbnail((1024, 1024))
    buf = BytesIO()
    img.save(buf, format='JPEG', quality=85)
    return ContentFile(buf.getvalue())


class MenuItemImageView(APIView):
    """
    Single CRUD endpoint for a menu item's HotelFinder-owned image.
    POST/PUT: upload or replace it (multipart field "image"). Shown in preference
              to the Aahar-synced image and preserved across Aahar syncs.
    DELETE:   remove it (display falls back to the Aahar-synced image).
    Owner/Admin only.
    """
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdminOrReadOnly]
    parser_classes = [MultiPartParser, FormParser]

    def _get_item(self, restaurant_id, pk):
        item = get_object_or_404(MenuItem, pk=pk, restaurant_id=restaurant_id)
        self.check_object_permissions(self.request, item)
        return item

    def post(self, request, restaurant_id, pk):
        item = self._get_item(restaurant_id, pk)
        f = request.FILES.get('image')
        if not f:
            return Response({'error': 'No image file provided (field "image").'}, status=400)
        content = _process_menu_image(f)
        if item.custom_image:
            item.custom_image.delete(save=False)   # avoid orphan files on replace
        item.custom_image.save(f'menu_{item.id}.jpg', content, save=True)
        return Response(MenuItemSerializer(item, context={'request': request}).data)

    put = post

    def delete(self, request, restaurant_id, pk):
        item = self._get_item(restaurant_id, pk)
        if item.custom_image:
            item.custom_image.delete(save=True)
        return Response(MenuItemSerializer(item, context={'request': request}).data)


class GlobalMenuItemListView(generics.ListAPIView):
    """
    GET: List all menu items across restaurants (public)
    Used for "Popular Food Items" on main page.
    """
    serializer_class = GlobalMenuItemSerializer
    permission_classes = [permissions.AllowAny]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    search_fields = ['name', 'description', 'category']
    filterset_fields = ['is_vegetarian', 'category', 'is_vegan', 'spice_level']
    
    def get_queryset(self):
        # Return random items or just all items
        # Filter by active restaurants only, excluding Aahar items if restaurant is unlinked or inactive
        queryset = MenuItem.objects.filter(
            is_available=True, 
            restaurant__is_active=True
        ).exclude(
            aahar_item_id__isnull=False,
            restaurant__aahar_config__isnull=True
        ).exclude(
            aahar_item_id__isnull=False,
            restaurant__aahar_config__is_active=False
        ).select_related('restaurant')
        
        # Optional: Randomize order for variety on main page
        # Note: order_by('?') can be slow on large DBs, but fine for small scale
        return queryset.order_by('?')


# ==================== Booking Views ====================

class BookingListCreateView(generics.ListCreateAPIView):
    """
    GET: List customer's bookings
    POST: Create new booking
    """
    permission_classes = [permissions.IsAuthenticated]
    
    def get_serializer_class(self):
        if self.request.method == 'POST':
            return BookingCreateSerializer
        return BookingSerializer
    
    def get_queryset(self):
        user = self.request.user
        if user.role == 'ADMIN':
            return Booking.objects.all()
        elif user.role == 'RESTAURANT_OWNER':
            return Booking.objects.filter(restaurant__owner=user)
        return Booking.objects.filter(customer=user)


class BookingDetailView(generics.RetrieveAPIView):
    """
    GET: Get booking details
    """
    serializer_class = BookingSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        user = self.request.user
        if user.role == 'ADMIN':
            return Booking.objects.all()
        elif user.role == 'RESTAURANT_OWNER':
            return Booking.objects.filter(restaurant__owner=user)
        return Booking.objects.filter(customer=user)


class BookingStatusUpdateView(generics.UpdateAPIView):
    """
    PATCH: Update booking status (Owner/Admin only)
    """
    serializer_class = BookingStatusUpdateSerializer
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdminOrReadOnly]
    
    def get_queryset(self):
        user = self.request.user
        if user.role == 'ADMIN' or user.is_superuser:
            return Booking.objects.all()
        elif user.role == 'RESTAURANT_OWNER':
            return Booking.objects.filter(restaurant__owner=user)
        return Booking.objects.none()


class BookingCancelView(APIView):
    """
    POST: Cancel a booking
    """
    permission_classes = [permissions.IsAuthenticated]
    
    def post(self, request, pk):
        user = request.user
        
        # Get booking based on user role
        if user.role == 'ADMIN' or user.is_superuser:
            booking = get_object_or_404(Booking, pk=pk)
        elif user.role == 'RESTAURANT_OWNER':
            booking = get_object_or_404(Booking, pk=pk, restaurant__owner=user)
        else:
            booking = get_object_or_404(Booking, pk=pk, customer=user)
        
        if booking.status in ['COMPLETED', 'CANCELLED']:
            return Response(
                {'error': f'Cannot cancel a {booking.status.lower()} booking'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        booking.status = 'CANCELLED'
        booking.save()
        
        return Response(BookingSerializer(booking).data)


class RestaurantBookingsView(generics.ListAPIView):
    """
    GET: List all bookings for a specific restaurant (Owner/Admin only)
    """
    serializer_class = BookingSerializer
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdminOrReadOnly]
    
    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        return Booking.objects.filter(restaurant_id=restaurant_id)


# ==================== Delivery Check View ====================

class DeliveryCheckView(APIView):
    """
    POST: Check if delivery is available for a customer location
    """
    permission_classes = [permissions.AllowAny]
    
    def post(self, request):
        serializer = DeliveryCheckSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        restaurant = get_object_or_404(Restaurant, pk=serializer.validated_data['restaurant_id'])
        
        if not restaurant.is_delivery_available:
            return Response({
                'is_deliverable': False,
                'distance_km': None,
                'has_warning': True,
                'warning_message': 'Delivery is not available at this restaurant',
                'estimated_delivery_time': None,
                'delivery_charge': None
            })
        
        delivery_info = check_delivery_availability(
            restaurant,
            serializer.validated_data['latitude'],
            serializer.validated_data['longitude']
        )
        
        response_data = {
            'is_deliverable': delivery_info['is_deliverable'],
            'distance_km': delivery_info['distance_km'],
            'has_warning': delivery_info['has_warning'],
            'warning_message': delivery_info['warning_message'],
            'estimated_delivery_time': None,
            'delivery_charge': None
        }
        
        if delivery_info['distance_km']:
            response_data['estimated_delivery_time'] = estimate_delivery_time(delivery_info['distance_km'])
            response_data['delivery_charge'] = calculate_delivery_charge(delivery_info['distance_km'])
        
        return Response(response_data)



# ==================== Table Views ====================

class TableListCreateView(generics.ListCreateAPIView):
    """
    GET: List all tables for a restaurant
    POST: Create a table (Owner/Admin only)
    """
    serializer_class = TableSerializer
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdminOrReadOnly]
    
    def get_queryset(self):
        restaurant_id = self.request.query_params.get('restaurant')
        user = self.request.user
        
        if restaurant_id:
            return Table.objects.filter(restaurant_id=restaurant_id).prefetch_related('bookings')
        
        if user.role == 'RESTAURANT_OWNER':
            return Table.objects.filter(restaurant__owner=user).prefetch_related('bookings')
            
        return Table.objects.none()

    def perform_create(self, serializer):
        # Ensure restaurant belongs to owner
        restaurant = serializer.validated_data['restaurant']
        if self.request.user.role == 'RESTAURANT_OWNER' and restaurant.owner != self.request.user:
            raise permissions.PermissionDenied("You do not own this restaurant")
        serializer.save()


class TableDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    GET: Get table details
    PUT/PATCH: Update table
    DELETE: Delete table
    """
    serializer_class = TableSerializer
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdminOrReadOnly]

class AvailableTablesView(APIView):
    """
    GET: Get available tables for a specific date, time, and party size
    Query params: restaurant_id, date (YYYY-MM-DD), time (HH:MM), party_size
    """
    permission_classes = [permissions.AllowAny]

    def get(self, request, restaurant_id):
        date_str = request.query_params.get('date')
        time_str = request.query_params.get('time')
        party_size = request.query_params.get('party_size')

        if not date_str or not time_str:
            return Response(
                {'error': 'date and time are required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            booking_date = datetime.strptime(date_str, '%Y-%m-%d').date()
            booking_time = datetime.strptime(time_str, '%H:%M').time()
            
            # Combine to datetime for easier comparison if needed, though we store separately
            booking_datetime = datetime.combine(booking_date, booking_time)
            
            # Assuming standard 2 hour slot
            end_time = (booking_datetime + timedelta(hours=2)).time()
            
            # Handle midnight crossing if needed (simplified here for same-day)
        except ValueError:
            return Response(
                {'error': 'Invalid date or time format'},
                status=status.HTTP_400_BAD_REQUEST
            )

        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)
        
        # 1. Get all active tables for the restaurant
        tables = Table.objects.filter(restaurant=restaurant, is_active=True)
        
        if party_size:
            try:
                size = int(party_size)
                tables = tables.filter(capacity__gte=size)
            except ValueError:
                pass # Ignore invalid party size

        # 2. Find conflicting bookings
        # A booking conflicts if it is on the same date AND times overlap
        # Overlap logic: (StartA < EndB) and (EndA > StartB)
        
        # Existing bookings for this restaurant on this date
        conflicting_bookings = Booking.objects.filter(
            restaurant=restaurant,
            reservation_date=booking_date,
            status__in=['CONFIRMED', 'PREPARING', 'READY'], # Exclude CANCELLED, COMPLETED, PENDING(maybe?)
            booking_type='DINE_IN'
        )
        
        # Filter strictly by time overlap
        # We need to calculate end time for each existing booking. 
        # Since we don't store duration, we assume 2 hours for all.
        
        occupied_table_ids = set()
        
        # Simple overlap check: 
        # Requested: S1 to E1
        # Existing: S2 to E2
        # Overlap if S1 < E2 and E1 > S2
        
        # Since we can't easily do time math in DB query without duration field,
        # we might need to fetch and filter in python or use specific logic.
        # For simplicity, let's exclude tables booked within +/- 2 hours of requested time.
        
        # Logic: 
        # If existing booking time is 7PM. It occupies 7PM - 9PM.
        # Request at 8PM (8-10) -> Overlaps
        # Request at 5:30PM (5:30-7:30) -> Overlaps
        
        req_start_dt = booking_datetime
        req_end_dt = req_start_dt + timedelta(hours=2)
        
        for booking in conflicting_bookings:
            if not booking.reservation_time:
                continue
                
            book_start_dt = datetime.combine(booking_date, booking.reservation_time)
            book_end_dt = book_start_dt + timedelta(hours=2)
            
            if req_start_dt < book_end_dt and req_end_dt > book_start_dt:
                # Time overlaps, mark its tables as occupied
                for table in booking.allocated_tables.all():
                    occupied_table_ids.add(table.id)
        
        # 3. Exclude occupied tables
        available_tables = tables.exclude(id__in=occupied_table_ids)
        
        # Serialize
        serializer = TableSerializer(available_tables, many=True)
        
        return Response({
            'restaurant_id': restaurant.id,
            'table_booking_fee': restaurant.table_booking_fee,
            'available_tables': serializer.data
        })


# ==================== Aahar Integration Views ====================

class AaharLinkView(APIView):
    """
    POST: Link a restaurant with Aahar.
    Creates AaharConfig and optionally calls Aahar's restaurant_service_mapping API.
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        serializer = AaharLinkRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        restaurant_id = serializer.validated_data['restaurant_id']
        aahar_service_id = serializer.validated_data['aahar_restaurant_service_id']
        aahar_domain = serializer.validated_data['aahar_domain']
        is_multitenant = serializer.validated_data.get('is_multitenant', True)
        aahar_api_token = serializer.validated_data.get('aahar_api_token', '')

        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)

        # Check ownership for non-admin
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and restaurant.owner != request.user:
            return Response(
                {'error': 'You do not own this restaurant'},
                status=status.HTTP_403_FORBIDDEN
            )

        # Check if already linked
        if hasattr(restaurant, 'aahar_config'):
            return Response(
                {'error': 'This restaurant is already linked with Aahar. Unlink first.'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Try to register with Aahar (API #1)
        aahar_response = None
        try:
            aahar_response = aahar_service.register_restaurant_mapping(aahar_service_id, aahar_domain)
        except AaharServiceError as e:
            # Even if Aahar is unreachable, save the config locally
            aahar_response = {'warning': str(e)}

        # Create local config
        config = AaharConfig.objects.create(
            restaurant=restaurant,
            aahar_restaurant_service_id=aahar_service_id,
            aahar_domain=aahar_domain,
            is_multitenant=is_multitenant,
            aahar_api_token=aahar_api_token,
        )

        return Response({
            'message': 'Restaurant linked with Aahar successfully',
            'config': AaharConfigSerializer(config).data,
            'aahar_response': aahar_response,
        }, status=status.HTTP_201_CREATED)

    def get(self, request):
        """GET: List all Aahar configurations"""
        user = request.user
        if user.role == 'ADMIN' or user.is_superuser:
            configs = AaharConfig.objects.all()
        elif user.role == 'RESTAURANT_OWNER':
            configs = AaharConfig.objects.filter(restaurant__owner=user)
        else:
            configs = AaharConfig.objects.none()

        serializer = AaharConfigSerializer(configs, many=True)
        return Response(serializer.data)


class AaharConfigDetailView(generics.RetrieveDestroyAPIView):
    """
    GET: View Aahar config details
    DELETE: Unlink restaurant from Aahar
    """
    serializer_class = AaharConfigSerializer
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'ADMIN' or user.is_superuser:
            return AaharConfig.objects.all()
        return AaharConfig.objects.filter(restaurant__owner=user)

    def perform_destroy(self, instance):
        restaurant = instance.restaurant
        # Clean up Aahar-synced menu items when unlinking so they don't remain visible
        synced_items = MenuItem.objects.filter(restaurant=restaurant, aahar_item_id__isnull=False)
        for item in synced_items:
            # Check if item is tied to any past orders (BookingItem) or Combos to avoid breaking POS/order history
            if not item.bookingitem_set.exists() and not item.comboitem_set.exists():
                item.delete()
            else:
                item.is_available = False
                item.save(update_fields=['is_available'])
        instance.delete()


class AaharFetchDetailsView(APIView):
    """
    GET: Fetch details of a specific restaurant from an Aahar domain.
    Query parameters: aahar_restaurant_service_id, aahar_domain
    """
    permission_classes = [permissions.IsAuthenticated, IsAdmin]

    def get(self, request):
        service_id = request.query_params.get('aahar_restaurant_service_id')
        domain = request.query_params.get('aahar_domain')

        if not service_id or not domain:
            return Response({'error': 'aahar_restaurant_service_id and aahar_domain are required'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            details = aahar_service.fetch_restaurant_details_from_domain(service_id, domain)
            return Response(details)
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_502_BAD_GATEWAY)


class AaharFetchOrdersView(APIView):
    """
    POST: Fetch orders from Aahar and store locally.
    Calls Aahar's get-hotel-order API.
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        serializer = AaharFetchOrdersRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        config_id = serializer.validated_data['aahar_config_id']
        room_number = serializer.validated_data.get('room_number', '')
        booking_id = serializer.validated_data.get('booking_id', '')

        config = get_object_or_404(AaharConfig, pk=config_id)

        # Check ownership
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and config.restaurant.owner != request.user:
            return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)

        try:
            orders_data = aahar_service.fetch_hotel_orders(
                config.aahar_restaurant_service_id,
                room_number,
                booking_id,
                base_url=config.aahar_domain,
            )
        except AaharServiceError as e:
            return Response({
                'error': str(e),
                'details': e.response_data,
            }, status=status.HTTP_502_BAD_GATEWAY)

        # Sync orders to local database
        synced_orders = []
        if isinstance(orders_data, list):
            for order in orders_data:
                # Determine payment status based on paid_amount vs grand_total
                grand_total = float(order.get('grand_total', 0))
                paid_amount = float(order.get('paid_amount', 0))
                payment_status = 'UNPAID'
                if grand_total > 0 and paid_amount >= (grand_total - 0.01):
                    payment_status = 'PAID'

                # Determine order status from payload or default to PLACED
                raw_status = order.get('status') or 'PLACED'
                status_val = raw_status.upper()

                order_obj, created = AaharOrderSync.objects.update_or_create(
                    aahar_config=config,
                    aahar_order_id=order.get('order_id', 0),
                    defaults={
                        'aahar_order_number': order.get('order_number', ''),
                        'invoice': order.get('invoice', ''),
                        'room_number': str(order.get('room_id', order.get('room_number', ''))),
                        'booking_id': order.get('booking_id', ''),
                        'order_data': order,
                        'grand_total': order.get('grand_total', 0),
                        'payment_status': payment_status,
                        'status': status_val,
                    }
                )

                # Sync payment status and order status to local Booking
                booking_id_str = order.get('booking_id')
                if booking_id_str:
                    try:
                        if '_' in booking_id_str:
                            booking_id_int = int(booking_id_str.split('_')[-1])
                        else:
                            booking_id_int = int(booking_id_str)
                        booking = Booking.objects.get(id=booking_id_int)
                        
                        # Sync payment status
                        if payment_status == 'PAID' and booking.payment_status != 'PAID':
                            booking.payment_status = 'PAID'

                        # Sync order status
                        status_lower = status_val.lower()
                        if status_lower == 'preparing':
                            booking.status = 'PREPARING'
                        elif status_lower == 'ready':
                            booking.status = 'READY'
                        elif status_lower in ['handed-over', 'handed_over', 'delivered', 'completed']:
                            if booking.booking_type == 'TAKEAWAY':
                                booking.status = 'COMPLETED'
                            elif booking.booking_type == 'DELIVERY':
                                booking.status = 'DELIVERED'
                            else:
                                booking.status = 'COMPLETED'
                        elif status_lower == 'cancelled':
                            booking.status = 'CANCELLED'

                        booking.save()

                        # Send websocket notification
                        try:
                            from channels.layers import get_channel_layer
                            from asgiref.sync import async_to_sync
                            channel_layer = get_channel_layer()
                            if channel_layer:
                                async_to_sync(channel_layer.group_send)(
                                    f"delivery_tracking_{booking.id}",
                                    {
                                        "type": "delivery_message",
                                        "message": {
                                            "type": "order_status_update",
                                            "status": booking.status,
                                            "booking_id": booking.id
                                        }
                                    }
                                )
                        except Exception:
                            pass

                    except (ValueError, TypeError, Booking.DoesNotExist):
                        pass

                synced_orders.append(AaharOrderSyncSerializer(order_obj).data)

        return Response({
            'message': f'Synced {len(synced_orders)} orders from Aahar',
            'orders': synced_orders,
        })


class AaharUpdatePaymentView(APIView):
    """
    POST: Send payment status to Aahar for an order.
    Calls Aahar's restaurant_order_payment_status API.
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        serializer = AaharPaymentUpdateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        order_sync_id = serializer.validated_data['aahar_order_sync_id']
        payment = serializer.validated_data['payment']
        mode = serializer.validated_data['mode']
        pay_status = serializer.validated_data['status']

        order_sync = get_object_or_404(AaharOrderSync, pk=order_sync_id)
        config = order_sync.aahar_config

        # Check ownership
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and config.restaurant.owner != request.user:
            return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)

        try:
            aahar_response = aahar_service.send_payment_status(
                config.aahar_restaurant_service_id,
                order_sync.invoice,
                str(payment),
                mode,
                pay_status,
                base_url=config.aahar_domain,
            )
        except AaharServiceError as e:
            return Response({
                'error': str(e),
                'details': e.response_data,
            }, status=status.HTTP_502_BAD_GATEWAY)

        # Update local record
        order_sync.payment_status = pay_status.upper()
        order_sync.payment_mode = mode
        order_sync.save()

        return Response({
            'message': 'Payment status sent to Aahar successfully',
            'order': AaharOrderSyncSerializer(order_sync).data,
            'aahar_response': aahar_response,
        })


class AaharOrdersListView(generics.ListAPIView):
    """
    GET: List all synced Aahar orders for the user's restaurants.
    Query params: ?restaurant=<id> to filter by restaurant
    """
    serializer_class = AaharOrderSyncSerializer
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def get_queryset(self):
        user = self.request.user
        restaurant_id = self.request.query_params.get('restaurant')

        if user.role == 'ADMIN' or user.is_superuser:
            qs = AaharOrderSync.objects.all()
        else:
            qs = AaharOrderSync.objects.filter(aahar_config__restaurant__owner=user)

        if restaurant_id:
            qs = qs.filter(aahar_config__restaurant_id=restaurant_id)

        return qs.select_related('aahar_config__restaurant')


class AaharOrderWebhookView(APIView):
    """
    POST: Receive order data pushed from Aahar.
    This endpoint is called BY Aahar when a new order is placed.
    Uses API key authentication instead of JWT.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        # Validate API key (bypassed per user request: no api key restriction for the time being)
        api_key = request.headers.get('X-Aahar-Api-Key', '')
        expected_key = getattr(settings, 'AAHAR_API_KEY', '')

        # if not expected_key or api_key != expected_key:
        #     return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)

        data = request.data
        service_id = data.get('restaurant_service_id', '')

        if not service_id:
            return Response({'error': 'restaurant_service_id is required'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            config = AaharConfig.objects.get(aahar_restaurant_service_id=service_id, is_active=True)
        except AaharConfig.DoesNotExist:
            return Response({'error': 'No active Aahar config found for this service ID'}, status=status.HTTP_404_NOT_FOUND)

        orders_list = data.get('orders', [])
        if not isinstance(orders_list, list):
            orders_list = []

        if not orders_list:
            orders_list = [data]

        results = []
        for order_data in orders_list:
            order_id = order_data.get('order_id', 0)
            if not order_id:
                continue

            # Determine payment status based on paid_amount vs grand_total
            grand_total = float(order_data.get('grand_total', 0))
            paid_amount = float(order_data.get('paid_amount', 0))
            payment_status = 'UNPAID'
            if grand_total > 0 and paid_amount >= (grand_total - 0.01):
                payment_status = 'PAID'

            # Determine order status from payload or default to PLACED
            raw_status = order_data.get('status') or 'PLACED'
            status_val = raw_status.upper()

            order_obj, created = AaharOrderSync.objects.update_or_create(
                aahar_config=config,
                aahar_order_id=order_id,
                defaults={
                    'aahar_order_number': order_data.get('order_number', ''),
                    'invoice': order_data.get('invoice', ''),
                    'is_transfer': order_data.get('is_transfer', 'others'),
                    'room_number': str(order_data.get('room_number', order_data.get('booking_number', ''))),
                    'booking_id': order_data.get('booking_id', ''),
                    'order_data': order_data,
                    'grand_total': order_data.get('grand_total', 0),
                    'payment_status': payment_status,
                    'status': status_val,
                }
            )

            # Update the linked local Booking payment status & order status if applicable
            booking_id_str = order_data.get('booking_id')
            if booking_id_str:
                try:
                    if '_' in booking_id_str:
                        booking_id_int = int(booking_id_str.split('_')[-1])
                    else:
                        booking_id_int = int(booking_id_str)
                    booking = Booking.objects.get(id=booking_id_int)
                    
                    # Sync payment status
                    if payment_status == 'PAID' and booking.payment_status != 'PAID':
                        booking.payment_status = 'PAID'

                    # Sync order status
                    status_lower = status_val.lower()
                    if status_lower == 'preparing':
                        booking.status = 'PREPARING'
                    elif status_lower == 'ready':
                        booking.status = 'READY'
                    elif status_lower in ['handed-over', 'handed_over', 'delivered', 'completed']:
                        if booking.booking_type == 'TAKEAWAY':
                            booking.status = 'COMPLETED'
                        elif booking.booking_type == 'DELIVERY':
                            booking.status = 'DELIVERED'
                        else:
                            booking.status = 'COMPLETED'
                    elif status_lower == 'cancelled':
                        booking.status = 'CANCELLED'

                    booking.save()

                    # Send websocket notification
                    try:
                        from channels.layers import get_channel_layer
                        from asgiref.sync import async_to_sync
                        channel_layer = get_channel_layer()
                        if channel_layer:
                            async_to_sync(channel_layer.group_send)(
                                f"delivery_tracking_{booking.id}",
                                {
                                    "type": "delivery_message",
                                    "message": {
                                        "type": "order_status_update",
                                        "status": booking.status,
                                        "booking_id": booking.id
                                    }
                                }
                            )
                    except Exception:
                        pass

                except (ValueError, TypeError, Booking.DoesNotExist):
                    pass

            results.append({
                'order_id': order_obj.id,
                'aahar_order_id': order_id,
                'created': created
            })

        return Response({
            'code': 200,
            'message': 'Orders processed successfully',
            'results': results
        })


class AaharSyncHotelsView(APIView):
    """
    POST: Fetch all active hotels from Ahaar and sync to local database.
    Admin only.
    """
    permission_classes = [permissions.IsAuthenticated, IsAdmin]

    def post(self, request):
        aahar_domain = request.data.get('aahar_domain') or request.data.get('domain')
        aahar_token = request.data.get('aahar_token') or request.data.get('token') or request.data.get('api_token') or request.data.get('access_token') or 'your_master_token_here'
        try:
            hotels_data = aahar_service.fetch_active_hotels(base_url=aahar_domain, api_token=aahar_token)
        except AaharServiceError as e:
            return Response({
                'error': str(e),
                'details': getattr(e, 'response_data', None)
            }, status=status.HTTP_502_BAD_GATEWAY)

        if not isinstance(hotels_data, list):
            return Response({'error': 'Unexpected response format from Aahar'}, status=status.HTTP_502_BAD_GATEWAY)

        synced_count = 0
        for hotel in hotels_data:
            # Assuming hotel has id, restaurant_name, restaurant_address, etc. based on YAML
            aahar_id = str(hotel.get('id') or hotel.get('restaurant_service_id') or '')
            if not aahar_id:
                continue

            name = hotel.get('restaurant_name') or hotel.get('name') or f"Aahar Hotel {aahar_id}"
            address = hotel.get('restaurant_address') or hotel.get('address') or ''
            location = hotel.get('city') or hotel.get('location') or 'Unknown City'

            # Solve image
            image_name = hotel.get('restaurant_img') or hotel.get('logo') or ''
            image_url = ''
            if image_name:
                if image_name.startswith('http'):
                    image_url = image_name
                else:
                    domain_to_use = aahar_domain or 'http://localhost:8125'
                    if not domain_to_use.startswith('http'):
                        domain_to_use = f"http://{domain_to_use}"
                    domain_to_use = domain_to_use.rstrip('/')
                    image_url = f"{domain_to_use}/backend/web/uploads/restaurant/{image_name}"

            # Check if this Aahar config already exists
            config = AaharConfig.objects.filter(aahar_restaurant_service_id=aahar_id).first()
            if config:
                restaurant = config.restaurant
                updated = False
                if restaurant.location == 'Unknown API City' or restaurant.location == 'Unknown City' or not restaurant.location:
                    restaurant.location = location
                    updated = True
                if not restaurant.address and address:
                    restaurant.address = address
                    updated = True
                if (not restaurant.image or 'picsum' in str(restaurant.image)) and image_url:
                    restaurant.image = image_url
                    updated = True
                if updated:
                    restaurant.save()

                if aahar_domain and config.aahar_domain != aahar_domain:
                    config.aahar_domain = aahar_domain
                    config.save()
                continue

            # Create a new local Restaurant and link it
            new_restaurant = Restaurant.objects.create(
                name=name,
                address=address,
                location=location,
                cuisine='Various',  # Placeholder
                image=image_url,
                is_delivery_available=True,
                is_takeaway_available=True,
                is_dine_in_available=False, # Assuming online portal focuses on delivery
                owner=request.user,  # Always assign requesting user as owner
            )

            # Create Config
            AaharConfig.objects.create(
                restaurant=new_restaurant,
                aahar_restaurant_service_id=aahar_id,
                aahar_domain=aahar_domain or 'aahar.vaiditech.in', # Default domain
            )
            synced_count += 1

        return Response({'message': f'Synced {synced_count} new hotels from Aahar.'})


def sync_menu_from_aahar(restaurant):
    """
    Helper to fetch and sync Aahar menu items for a restaurant to the local DB.
    """
    if not hasattr(restaurant, 'aahar_config') or not restaurant.aahar_config.is_active:
        return 0, 'Restaurant is not linked to Aahar or integration is inactive.'

    aahar_service_id = restaurant.aahar_config.aahar_restaurant_service_id
    aahar_domain = restaurant.aahar_config.aahar_domain

    try:
        raw_response = aahar_service.fetch_hotel_menu(aahar_service_id, base_url=aahar_domain)
    except AaharServiceError as e:
        return 0, str(e)

    # Unwrap the outer {code, data} envelope
    if isinstance(raw_response, dict):
        if raw_response.get('code') and raw_response.get('code') != 200:
            return 0, raw_response.get('error_message', 'Aahar menu sync error')
        inner_data = raw_response.get('data', raw_response)
    else:
        inner_data = raw_response

    # Build a flat list of items from the categories structure
    items_list = []

    if isinstance(inner_data, dict) and 'categories' in inner_data:
        # Aahar standard format: {restaurant_name, categories: [{category_name, items:[...]}]}
        for cat in inner_data.get('categories', []):
            cat_name = cat.get('category_name', 'MAIN_COURSE')
            for item in cat.get('items', []):
                item['_category_name'] = cat_name
                items_list.append(item)
    elif isinstance(inner_data, dict) and 'data' in inner_data:
        # Fallback: nested data wrapper
        nested = inner_data['data']
        if isinstance(nested, list):
            items_list = nested
        elif isinstance(nested, dict) and 'categories' in nested:
            for cat in nested.get('categories', []):
                cat_name = cat.get('category_name', 'MAIN_COURSE')
                for item in cat.get('items', []):
                    item['_category_name'] = cat_name
                    items_list.append(item)
    elif isinstance(inner_data, list):
        items_list = inner_data
    elif isinstance(inner_data, dict):
        # Dict keyed by category name: {"Starters": [{item}, ...]}
        for category_name, items in inner_data.items():
            if isinstance(items, list):
                for item in items:
                    item['_category_name'] = category_name
                    items_list.append(item)

    menu_data = items_list

    # Build base URL for item images from the Aahar domain
    _aahar_base = (aahar_domain or 'http://localhost:8125').rstrip('/')
    if not _aahar_base.startswith('http'):
        _aahar_base = f"http://{_aahar_base}"

    import requests as _requests
    import os as _os
    from io import BytesIO
    from django.core.files.base import ContentFile
    from django.conf import settings as _settings

    def _download_and_save_image(image_url, filename_hint):
        """Download image from Aahar and return a ContentFile, or None on failure."""
        try:
            resp = _requests.get(image_url, timeout=10, stream=True)
            if resp.status_code == 200:
                ext = _os.path.splitext(filename_hint)[-1] or '.jpg'
                fname = f"aahar_{_os.path.basename(filename_hint)}"
                return ContentFile(resp.content), fname
        except Exception as exc:
            logger.warning("Failed to download item image %s: %s", image_url, exc)
        return None, None

    synced_count = 0
    for item in menu_data:
        # Extract fields based on suspected Aahar structure
        name = item.get('item_name') or item.get('name')
        if not name:
            continue

        price = item.get('item_price') or item.get('price', 0)
        category = item.get('_category_name') or item.get('category', 'MAIN_COURSE')
        item_type = item.get('item_type', 'Veg')

        # Use the original category name
        db_category = str(category).strip()
        if not db_category:
            db_category = 'Uncategorized'

        is_veg = (item_type == 'Veg')

        # Build item image URL from Aahar uploads path
        item_img_filename = item.get('item_img') or item.get('image') or ''
        item_image_url = ''
        if item_img_filename:
            if item_img_filename.startswith('http'):
                item_image_url = item_img_filename
            else:
                item_image_url = f"{_aahar_base}/backend/web/uploads/items/{item_img_filename}"

        defaults = {
            'aahar_item_id': item.get('item_id'),
            'price': price,
            'category': db_category,
            'description': item.get('item_short_description') or item.get('description', ''),
            'is_available': item.get('status', 'Active') == 'Active',
            'is_vegetarian': is_veg,
        }

        menu_item, created = MenuItem.objects.update_or_create(
            restaurant=restaurant,
            name=name,
            defaults=defaults,
        )

        # Download & save image locally so Django ImageField serves it cleanly
        needs_image_download = False
        if item_image_url:
            if not menu_item.image or str(menu_item.image.name).startswith('http'):
                needs_image_download = True
            elif item_img_filename:
                # Check if the existing image filename matches the new one from Aahar
                current_img_basename = _os.path.basename(menu_item.image.name)
                new_img_basename = f"aahar_{_os.path.basename(item_img_filename)}"
                if current_img_basename != new_img_basename:
                    needs_image_download = True

        if needs_image_download:
            img_content, img_name = _download_and_save_image(item_image_url, item_img_filename or 'item.jpg')
            if img_content:
                # Remove the old image file if it exists to save space
                if menu_item.image:
                    menu_item.image.delete(save=False)
                menu_item.image.save(img_name, img_content, save=True)

        synced_count += 1

    return synced_count, None


class AaharSyncMenuView(APIView):
    """
    POST: Fetch menu for a specific Aahar-linked restaurant and sync to local DB.
    Admin or Owner.
    Payload: {"restaurant_id": 1} # Local restaurant ID
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        restaurant_id = request.data.get('restaurant_id')
        if not restaurant_id:
            return Response({'error': 'restaurant_id is required'}, status=status.HTTP_400_BAD_REQUEST)

        restaurant = get_object_or_404(Restaurant, id=restaurant_id)
        
        # Check permissions
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and restaurant.owner != request.user:
            return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)

        synced_count, error_msg = sync_menu_from_aahar(restaurant)
        if error_msg:
            return Response({'error': error_msg}, status=status.HTTP_502_BAD_GATEWAY)

        return Response({'message': f'Synced {synced_count} menu items from Aahar.'})


class AaharMapCustomItemView(APIView):
    """
    POST: Manually bind a custom HotelFinder MenuItem to an existing Aahar item_id.
    Payload: {"menu_item_id": 1, "aahar_item_id": 105}
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        menu_item_id = request.data.get('menu_item_id')
        aahar_item_id = request.data.get('aahar_item_id')
        
        if not menu_item_id or not aahar_item_id:
            return Response({'error': 'menu_item_id and aahar_item_id are required'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            aahar_item_id = int(aahar_item_id)
        except ValueError:
            return Response({'error': 'aahar_item_id must be a valid integer'}, status=status.HTTP_400_BAD_REQUEST)

        menu_item = get_object_or_404(MenuItem, id=menu_item_id)
        
        # Check permissions
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and menu_item.restaurant.owner != request.user:
            return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)

        menu_item.aahar_item_id = aahar_item_id
        menu_item.save(update_fields=['aahar_item_id'])

        return Response({'message': f'Successfully mapped {menu_item.name} to Aahar ID {aahar_item_id}.'})


class AaharServiceMappingWebhookView(APIView):
    """
    POST: Webhook called by Aahar when a restaurant/service mapping is created/updated.
    Authenticates using local database settings, creates or updates AaharConfig,
    and automatically pulls the latest menu items.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        data = request.data
        aahar_service_id = data.get('restaurant_service_id')
        local_restaurant_id = data.get('hotal_id') or data.get('branch_id')
        aahar_domain = data.get('restaurant_domain') or data.get('domain')
        status_val = data.get('status')
        is_active = (status_val == '1' or status_val == 1 or status_val == True or status_val == 'true')

        if not aahar_service_id:
            return Response({'error': 'restaurant_service_id is required'}, status=status.HTTP_400_BAD_REQUEST)

        if not local_restaurant_id:
            return Response({'error': 'hotal_id is required'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            restaurant = Restaurant.objects.get(id=local_restaurant_id)
        except Restaurant.DoesNotExist:
            return Response({'error': f'Local restaurant with ID {local_restaurant_id} not found'}, status=status.HTTP_404_NOT_FOUND)

        # Update or create the AaharConfig linking this restaurant
        config, created = AaharConfig.objects.update_or_create(
            restaurant=restaurant,
            defaults={
                'aahar_restaurant_service_id': str(aahar_service_id),
                'aahar_domain': aahar_domain or '',
                'is_active': is_active,
            }
        )

        synced_count = 0
        sync_error = None
        if is_active:
            try:
                synced_count, sync_error = sync_menu_from_aahar(restaurant)
            except Exception as e:
                sync_error = str(e)

        return Response({
            'code': 200,
            'message': 'Mapping configuration processed successfully',
            'config_created': created,
            'is_active': is_active,
            'menu_items_synced': synced_count,
            'menu_sync_error': sync_error
        })


# ==================== Restaurant Finder Integration Views ====================

from .models import RestaurantFinderConfig, RestaurantFinderOrderSync
from .serializers import RestaurantFinderConfigSerializer, RestaurantFinderOrderSyncSerializer, RestaurantFinderLinkRequestSerializer
from . import restaurant_finder_service

class RestaurantFinderLinkView(APIView):
    """
    POST: Link a restaurant with Restaurant Finder.
    Creates RestaurantFinderConfig and tests validation.
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        serializer = RestaurantFinderLinkRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        aahar_config_id = serializer.validated_data['aahar_config_id']

        from .models import AaharConfig
        aahar_config = get_object_or_404(AaharConfig, pk=aahar_config_id)
        restaurant = aahar_config.restaurant

        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and restaurant.owner != request.user:
            return Response({'error': 'You do not own the restaurant linked to this Aahar config'}, status=status.HTTP_403_FORBIDDEN)

        if hasattr(aahar_config, 'restaurant_finder_config'):
            return Response({'error': 'This Aahar config is already linked to Restaurant Finder.'}, status=status.HTTP_400_BAD_REQUEST)

        # Auto-generate credentials for Aahar POS
        import secrets
        api_key = secrets.token_hex(32)
        outlet_id = f"RF-{aahar_config.id}-{secrets.token_hex(4)}"
        api_url = request.build_absolute_uri('/api/restaurants/restaurant-finder/webhook/order/')
        brand_name = restaurant.name

        config = RestaurantFinderConfig.objects.create(
            aahar_config=aahar_config,
            api_url=api_url,
            brand_name=brand_name,
            outlet_id=outlet_id,
            api_key=api_key
        )

        return Response({
            'message': 'Restaurant linked with Restaurant Finder successfully',
            'config': RestaurantFinderConfigSerializer(config).data
        }, status=status.HTTP_201_CREATED)

    def get(self, request):
        """GET: List all Restaurant Finder configurations"""
        user = request.user
        if user.role == 'ADMIN' or user.is_superuser:
            configs = RestaurantFinderConfig.objects.all()
        elif user.role == 'RESTAURANT_OWNER':
            configs = RestaurantFinderConfig.objects.filter(aahar_config__restaurant__owner=user)
        else:
            configs = RestaurantFinderConfig.objects.none()

        serializer = RestaurantFinderConfigSerializer(configs, many=True)
        return Response(serializer.data)


class RestaurantFinderConfigDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    GET: View config details
    PUT/PATCH: Update config details
    DELETE: Unlink from Restaurant Finder
    """
    serializer_class = RestaurantFinderConfigSerializer
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'ADMIN' or user.is_superuser:
            return RestaurantFinderConfig.objects.all()
        return RestaurantFinderConfig.objects.filter(aahar_config__restaurant__owner=user)


class RestaurantFinderSyncMenuView(APIView):
    """
    POST: Push local menu to Restaurant Finder.
    Triggered by the green 'Menu' button.
    Payload: {"config_id": 1}
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def post(self, request):
        config_id = request.data.get('config_id')
        if not config_id:
            return Response({'error': 'config_id is required'}, status=status.HTTP_400_BAD_REQUEST)

        config = get_object_or_404(RestaurantFinderConfig, id=config_id)
        
        if request.user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER'] and config.aahar_config.restaurant.owner != request.user:
            return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)

        # Gather menu items
        items = MenuItem.objects.filter(restaurant=config.aahar_config.restaurant, is_available=True)
        menu_payload = [
            {
                "name": item.name,
                "description": item.description,
                "price": str(item.price),
                "category": item.category,
                "is_veg": item.is_vegetarian
            }
            for item in items
        ]

        try:
            # Push to external service
            restaurant_finder_service.push_menu(config.api_key, config.outlet_id, menu_payload)
            return Response({'message': f'Successfully synced {items.count()} items.'})
        except restaurant_finder_service.RestaurantFinderError as e:
            return Response({'error': str(e)}, status=status.HTTP_502_BAD_GATEWAY)


class RestaurantFinderOrderWebhookView(APIView):
    """
    POST: Receive order data from Restaurant Finder Webhook.
    Called externally.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        # The external service would pass the Outlet-Id in headers or body
        outlet_id = request.headers.get('Outlet-Id') or request.data.get('outlet_id')
        
        if not outlet_id:
            return Response({'error': 'Outlet-Id is required'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            config = RestaurantFinderConfig.objects.get(outlet_id=outlet_id, is_active=True)
        except RestaurantFinderConfig.DoesNotExist:
            return Response({'error': 'No active configuration found for this outlet'}, status=status.HTTP_404_NOT_FOUND)

        # Authenticate using the API Key we gave to the webhook
        auth_header = request.headers.get('Authorization', '')
        if not auth_header or auth_header != f"Bearer {config.api_key}":
            return Response({'error': 'Unauthorized Webhook Call'}, status=status.HTTP_401_UNAUTHORIZED)

        # Process the incoming webhook data
        order_data = request.data
        rf_order_id = order_data.get('order_id')
        
        if not rf_order_id:
            return Response({'error': 'order_id is required in payload'}, status=status.HTTP_400_BAD_REQUEST)

        grand_total = order_data.get('grand_total', 0)

        order_sync, created = RestaurantFinderOrderSync.objects.update_or_create(
            config=config,
            rf_order_id=rf_order_id,
            defaults={
                'order_data': order_data,
                'grand_total': grand_total,
                'status': 'RECEIVED'
            }
        )

# ==================== Delivery Partner Views ====================

from .models import DeliveryContract, DeliveryCommissionLedger
from .serializers import DeliveryContractSerializer, DeliveryCommissionLedgerSerializer

class DeliveryContractRequestView(generics.CreateAPIView):
    """
    POST: Delivery partner initiates a contract request with a restaurant.
    """
    serializer_class = DeliveryContractSerializer
    permission_classes = [permissions.IsAuthenticated]

    def perform_create(self, serializer):
        user = self.request.user
        if user.role != 'DELIVERY_PARTNER':
            raise permissions.PermissionDenied("Only Delivery Partners can send connection requests.")
        serializer.save(delivery_partner=user, status='PENDING')

class DeliveryContractManageView(generics.RetrieveUpdateAPIView):
    """
    GET: View a contract.
    PATCH: Owner accepts/rejects a contract.
    """
    queryset = DeliveryContract.objects.all()
    serializer_class = DeliveryContractSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'DELIVERY_PARTNER':
            return self.queryset.filter(delivery_partner=user)
        elif user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
            return self.queryset.filter(restaurant__owner=user)
        return self.queryset.none()
        
    def perform_update(self, serializer):
        # Only owners can update the status (e.g. from PENDING to ACTIVE)
        if self.request.user.role not in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
            raise permissions.PermissionDenied("Only the restaurant owner can manage contract statuses.")
        serializer.save()

class DeliveryPartnerContractsView(generics.ListAPIView):
    """
    GET: List all contracts for the current delivery partner or owner.
    """
    serializer_class = DeliveryContractSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        user = self.request.user
        if user.role == 'DELIVERY_PARTNER':
            return DeliveryContract.objects.filter(delivery_partner=user)
        elif user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
            return DeliveryContract.objects.filter(restaurant__owner=user)
        return DeliveryContract.objects.none()

class DeliveryAvailableOrdersView(generics.ListAPIView):
    """
    GET: List all preparing/ready orders from restaurants the partner is contracted with.
    """
    serializer_class = BookingSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        if user.role != 'DELIVERY_PARTNER':
            return Booking.objects.none()
            
        active_contracts = DeliveryContract.objects.filter(delivery_partner=user, status='ACTIVE')
        restaurant_ids = active_contracts.values_list('restaurant_id', flat=True)
        
        return Booking.objects.filter(
            restaurant_id__in=restaurant_ids,
            booking_type='DELIVERY',
            status__in=['PREPARING', 'READY'],
            delivery_partner__isnull=True
        ).order_by('created_at')

class DeliveryActiveOrdersView(generics.ListAPIView):
    """
    GET: List active orders accepted by the delivery partner.
    """
    serializer_class = BookingSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        if user.role != 'DELIVERY_PARTNER':
            return Booking.objects.none()
        return Booking.objects.filter(
            delivery_partner=user,
            status__in=['PARTNER_ASSIGNED', 'OUT_FOR_DELIVERY', 'PICKED_UP']
        )
        
class DeliveryAcceptOrderView(APIView):
    """
    POST: Delivery partner accepts an order.
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        user = request.user
        if user.role != 'DELIVERY_PARTNER':
            return Response({'error': 'Only Delivery Partners can accept orders.'}, status=status.HTTP_403_FORBIDDEN)
            
        booking = get_object_or_404(Booking, pk=pk)
        
        # Verify contract validity
        is_contracted = DeliveryContract.objects.filter(
            restaurant=booking.restaurant,
            delivery_partner=user,
            status='ACTIVE'
        ).exists()
        
        if not is_contracted:
            return Response({'error': 'You do not have an active contract with this restaurant.'}, status=status.HTTP_403_FORBIDDEN)
            
        if booking.delivery_partner is not None:
             return Response({'error': 'Order is already assigned to another partner.'}, status=status.HTTP_400_BAD_REQUEST)
             
        booking.delivery_partner = user
        booking.status = 'PARTNER_ASSIGNED'
        booking.save()
        
        return Response({'message': 'Order successfully assigned.', 'booking': BookingSerializer(booking).data})

class DeliveryCompleteOrderView(APIView):
    """
    POST: Delivery partner marks an order as DELIVERED, and commission is captured.
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        user = request.user
        if user.role != 'DELIVERY_PARTNER':
             return Response({'error': 'Permission denied.'}, status=status.HTTP_403_FORBIDDEN)
             
        booking = get_object_or_404(Booking, pk=pk, delivery_partner=user)
        
        if booking.status in ['DELIVERED', 'COMPLETED', 'CANCELLED']:
             return Response({'error': 'Order is already finalized or cancelled.'}, status=status.HTTP_400_BAD_REQUEST)
             
        contract = get_object_or_404(DeliveryContract, restaurant=booking.restaurant, delivery_partner=user, status='ACTIVE')
        
        # Mark delivered
        booking.status = 'DELIVERED'
        booking.save()
        
        # Calculate commission
        commission_amt = Decimal('0.00')
        if contract.commission_type == 'FLAT_FEE':
            commission_amt = contract.commission_value
        elif contract.commission_type == 'PERCENTAGE':
            # Percentage based on subtotal (excluding tax and delivery charge usually)
            booking_subtotal = booking.subtotal
            if not booking_subtotal:
                booking.calculate_total()
                booking_subtotal = booking.subtotal
            commission_amt = (booking_subtotal * contract.commission_value) / Decimal('100.00')
            
        # Record into ledger
        ledger = DeliveryCommissionLedger.objects.create(
            booking=booking,
            delivery_partner=user,
            restaurant=booking.restaurant,
            commission_amount=commission_amt,
            is_settled=False
        )
        
        return Response({
            'message': 'Order delivered successfully.',
            'commission_earned': commission_amt,
            'booking_status': booking.status
        })

class DeliveryCommissionLedgerView(generics.ListAPIView):
    """
    GET: Ledger of commissions. 
    Partners see their earnings.
    Owners see what they owe.
    """
    serializer_class = DeliveryCommissionLedgerSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        user = self.request.user
        if user.role == 'DELIVERY_PARTNER':
            return DeliveryCommissionLedger.objects.filter(delivery_partner=user)
        elif user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
            return DeliveryCommissionLedger.objects.filter(restaurant__owner=user)
        return DeliveryCommissionLedger.objects.none()

class DeliveryCommissionSettleView(APIView):
    """
    POST: Owner marks a set of ledgers or a single ledger as paid/settled.
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        user = request.user
        if user.role not in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
             return Response({'error': 'Only owners can settle accounts.'}, status=status.HTTP_403_FORBIDDEN)
             
        ledger_ids = request.data.get('ledger_ids', [])
        if not ledger_ids:
             return Response({'error': 'No ledger IDs provided.'}, status=status.HTTP_400_BAD_REQUEST)
             
        # Filter ledgers belonging to the owner's restaurants that are unsettled
        ledgers = DeliveryCommissionLedger.objects.filter(
            id__in=ledger_ids, 
            restaurant__owner=user,
            is_settled=False
        )
        
        count = ledgers.count()
        ledgers.update(is_settled=True, settled_at=datetime.now())
        
        return Response({'message': f'{count} commission records marked as settled.'})


import requests
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from django.conf import settings
from .models import AaharConfig

class DirectAaharRestaurantListView(APIView):
    permission_classes = [AllowAny]
    """
    Returns the list of restaurants available on Aahar.
    In Multi-Tenant mode, fetches dynamically from Aahar.
    In Single-Tenant mode, returns the single configured restaurant.
    """
    def get(self, request):
        config = AaharConfig.objects.filter(is_active=True).first()
        if not config:
            return Response({"error": "Aahar not configured"}, status=status.HTTP_404_NOT_FOUND)
            
        if not config.is_multitenant:
            return Response({"data": [{"restaurant_service_id": config.aahar_restaurant_service_id, "name": config.restaurant.name}]})
            
        headers = {"Access-Token": config.aahar_api_token}
        url = f"{config.aahar_domain.rstrip('/')}/apis/backend/web/get-all-restaurants"
        
        try:
            resp = requests.get(url, headers=headers, timeout=5)
            return Response(resp.json())
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class DirectAaharMenuView(APIView):
    permission_classes = [AllowAny]
    """Proxies menu fetch to Aahar"""
    def get(self, request, restaurant_id):
        config = AaharConfig.objects.filter(is_active=True).first()
        if not config:
            return Response({"error": "Aahar not configured"}, status=status.HTTP_404_NOT_FOUND)
            
        url = f"{config.aahar_domain.rstrip('/')}/apis/backend/web/export-menu?restaurant_id={restaurant_id}"
        headers = {"Access-Token": config.aahar_api_token} if config.is_multitenant else {}
        
        try:
            resp = requests.get(url, headers=headers, timeout=5)
            return Response(resp.json())
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class DirectAaharServicesView(APIView):
    permission_classes = [AllowAny]
    """
    Returns one Aahar restaurant's service categories grouped into the three
    service tabs (Dine-in / Room / Parcel). Proxies Aahar's get-restaurant-services.
    """
    def get(self, request, restaurant_id):
        config = AaharConfig.objects.filter(is_active=True).first()
        if not config:
            return Response({"error": "Aahar not configured"}, status=status.HTTP_404_NOT_FOUND)

        url = f"{config.aahar_domain.rstrip('/')}/apis/backend/web/get-restaurant-services?restaurant_id={restaurant_id}"
        headers = {"Access-Token": config.aahar_api_token} if config.is_multitenant else {}
        try:
            resp = requests.get(url, headers=headers, timeout=5)
            return Response(resp.json(), status=resp.status_code)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class DirectAaharOrderView(APIView):
    permission_classes = [AllowAny]
    """Proxies order placement to Aahar"""
    def post(self, request, restaurant_id):
        config = AaharConfig.objects.filter(is_active=True).first()
        if not config:
            return Response({"error": "Aahar not configured"}, status=status.HTTP_404_NOT_FOUND)
            
        url = f"{config.aahar_domain.rstrip('/')}/apis/backend/web/receive-external-order"
        headers = {"Access-Token": config.aahar_api_token} if config.is_multitenant else {}
        
        payload = request.data
        payload['restaurant_id'] = restaurant_id
        payload['is_transfer'] = "online_hf"
        
        try:
            resp = requests.post(url, json=payload, headers=headers, timeout=10)
            return Response(resp.json(), status=resp.status_code)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

def apply_aahar_order_status(order_id, new_status):
    """
    Shared logic for an Aahar order-status event (used by both the REST webhook
    and the LiveOrders WebSocket consumer): updates AaharOrderSync + the linked
    Booking, notifies the guest's delivery-tracking socket, and queues an
    event_outbox row. Returns the resolved status string.
    """
    sync_record = AaharOrderSync.objects.filter(aahar_order_id=order_id).first()
    if sync_record:
        sync_record.status = new_status.upper()
        sync_record.save()

        if sync_record.booking_id:
            try:
                booking_id_str = sync_record.booking_id
                if '_' in booking_id_str:
                    booking_id_int = int(booking_id_str.split('_')[-1])
                else:
                    booking_id_int = int(booking_id_str)
                booking = Booking.objects.get(id=booking_id_int)
                status_lower = new_status.lower()
                if status_lower == 'preparing':
                    booking.status = 'PREPARING'
                elif status_lower == 'ready':
                    booking.status = 'READY'
                elif status_lower in ['handed-over', 'handed_over', 'delivered', 'completed']:
                    if booking.booking_type == 'TAKEAWAY':
                        booking.status = 'COMPLETED'
                    elif booking.booking_type == 'DELIVERY':
                        booking.status = 'DELIVERED'
                    else:
                        booking.status = 'COMPLETED'
                elif status_lower in ['cancelled', 'rejected']:
                    booking.status = 'CANCELLED'
                booking.save()

                try:
                    from channels.layers import get_channel_layer
                    from asgiref.sync import async_to_sync
                    channel_layer = get_channel_layer()
                    if channel_layer:
                        async_to_sync(channel_layer.group_send)(
                            f"delivery_tracking_{booking.id}",
                            {
                                "type": "delivery_message",
                                "message": {
                                    "type": "order_status_update",
                                    "status": booking.status,
                                    "booking_id": booking.id
                                }
                            }
                        )
                except Exception:
                    pass

                new_status = booking.status
            except (ValueError, TypeError, Booking.DoesNotExist):
                pass

    # Queue an event_outbox row -> HotelFinder's zero-latency broadcast to the guest's phone
    from django.db import connection
    try:
        with connection.cursor() as cursor:
            import json
            engine = connection.settings_dict.get('ENGINE', '')
            if 'mysql' in engine:
                cursor.execute("""
                    CREATE TABLE IF NOT EXISTS event_outbox (
                        id INT AUTO_INCREMENT PRIMARY KEY,
                        topic VARCHAR(255),
                        payload TEXT,
                        status VARCHAR(20) DEFAULT 'PENDING',
                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                    )
                """)
            else:
                cursor.execute("""
                    CREATE TABLE IF NOT EXISTS event_outbox (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        topic VARCHAR(255),
                        payload TEXT,
                        status VARCHAR(20) DEFAULT 'PENDING',
                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                    )
                """)
            cursor.execute("""
                INSERT INTO event_outbox (topic, payload)
                VALUES (%s, %s)
            """, ['food_order_status_updated', json.dumps({"order_id": order_id, "status": new_status})])
    except Exception as e:
        print("DB ERROR:", e)
    return new_status


class DirectAaharOrderStatusWebhookView(APIView):
    permission_classes = [AllowAny]
    """
    Webhook receiver for Aahar to push live Kitchen Order Ticket status updates.
    Receives status changes (e.g. 'Preparing', 'Out for Delivery') directly from the Aahar POS.
    """
    def post(self, request):
        payload = request.data
        order_id = payload.get('order_id')
        new_status = payload.get('status')

        if not order_id or not new_status:
            return Response({"error": "order_id and status required"}, status=status.HTTP_400_BAD_REQUEST)

        apply_aahar_order_status(order_id, new_status)
        return Response({"status": "acknowledged"})


class DeliveryLocationUpdateView(APIView):
    """
    POST: Delivery partner sends GPS coordinates for an active order.
    Called every ~10 seconds from the delivery partner's browser.
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        user = request.user
        if user.role != 'DELIVERY_PARTNER':
            return Response({'error': 'Only delivery partners can share location.'},
                            status=status.HTTP_403_FORBIDDEN)

        booking = get_object_or_404(Booking, pk=pk, delivery_partner=user)

        if booking.status not in ['PARTNER_ASSIGNED', 'OUT_FOR_DELIVERY', 'PICKED_UP']:
            return Response({'error': 'Location sharing is only active during delivery.'},
                            status=status.HTTP_400_BAD_REQUEST)

        serializer = DeliveryLocationUpdateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        DeliveryLocationLog.objects.create(
            booking=booking,
            delivery_partner=user,
            latitude=serializer.validated_data['latitude'],
            longitude=serializer.validated_data['longitude'],
        )

        return Response({'message': 'Location updated.'})


class DeliveryTrackingView(APIView):
    """
    GET: Restaurant owner fetches live tracking data for a delivery order.
    Returns: current partner location, restaurant coords, destination coords, and route history.
    """
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, pk):
        user = request.user

        # Restaurant owner, admin, or customer can track
        if user.role in ['RESTAURANT_OWNER', 'HOTEL_OWNER']:
            booking = get_object_or_404(Booking, pk=pk, restaurant__owner=user)
        elif user.role == 'ADMIN' or user.is_superuser:
            booking = get_object_or_404(Booking, pk=pk)
        elif user.role == 'CUSTOMER':
            booking = get_object_or_404(Booking, pk=pk, customer=user)
        else:
            return Response({'error': 'Permission denied.'},
                            status=status.HTTP_403_FORBIDDEN)

        if not booking.delivery_partner:
            return Response({'error': 'No delivery partner assigned to this order.'},
                            status=status.HTTP_400_BAD_REQUEST)

        # Get latest location
        latest_log = booking.location_logs.first()  # ordered by -timestamp

        # Get route history (last 100 breadcrumbs, ordered oldest→newest for drawing)
        route_history = list(booking.location_logs.all()[:100])
        route_history.reverse()

        tracking_data = {
            'booking_id': booking.id,
            'status': booking.status,
            'delivery_partner_name': booking.delivery_partner.username,

            'current_latitude': latest_log.latitude if latest_log else None,
            'current_longitude': latest_log.longitude if latest_log else None,
            'last_updated': latest_log.timestamp if latest_log else None,

            'restaurant_name': booking.restaurant.name,
            'restaurant_latitude': booking.restaurant.latitude,
            'restaurant_longitude': booking.restaurant.longitude,

            'delivery_address': booking.delivery_address,
            'destination_latitude': booking.delivery_latitude,
            'destination_longitude': booking.delivery_longitude,

            'route_history': route_history,
        }

        serializer = DeliveryTrackingSerializer(tracking_data)
        return Response(serializer.data)

class LandingPageDataView(APIView):
    """
    Returns real-time data for the landing page:
    - Stats (total dishes, total happy customers)
    - Top 3 most ordered menu items
    - Ongoing offers
    """
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        from django.db.models import Sum

        # 1. Stats
        total_dishes = MenuItem.objects.filter(is_available=True).count()
        total_customers = Booking.objects.values('customer').distinct().count()

        # 2. Most Ordered Menu Items
        # Aggregate quantity from BookingItem, sort descending, limit 3
        top_items_qs = BookingItem.objects.values('menu_item').annotate(total_quantity=Sum('quantity')).order_by('-total_quantity')[:3]
        top_menu_item_ids = [item['menu_item'] for item in top_items_qs]
        
        # If no orders exist yet, just get 3 random available items as fallback
        if not top_menu_item_ids:
            top_menu_items = MenuItem.objects.filter(is_available=True)[:3]
        else:
            top_menu_items = MenuItem.objects.filter(id__in=top_menu_item_ids)

        menu_serializer = MenuItemSerializer(top_menu_items, many=True)

        # 3. Active Offers
        offers = Offer.objects.filter(is_active=True).order_by('-created_at')
        offer_serializer = OfferSerializer(offers, many=True)

        return Response({
            "stats": {
                "total_dishes": total_dishes,
                "total_customers": total_customers
            },
            "most_ordered": menu_serializer.data,
            "offers": offer_serializer.data
        })

class SyankoConfigurationUpdateView(APIView):
    """
    POST: Save configuration overrides from Syanko Flare (Admin UI) globally across the server.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        key = request.data.get('key')
        value = request.data.get('value')
        if not key:
            return Response({'error': 'Configuration key is required'}, status=status.HTTP_400_BAD_REQUEST)
        config, created = SyankoConfiguration.objects.update_or_create(
            key=key,
            defaults={'value': value}
        )
        return Response({'status': 'success', 'key': config.key, 'value': config.value})


class SyankoCommerceDataView(APIView):
    """
    Returns dynamic data for the SyankoCommerce landing page.
    Prioritizes global overrides stored in SyankoConfiguration.
    """
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        from django.db.models import Sum

        # Fetch all stored configuration overrides from Syanko Flare
        stored_configs = {c.key: c.value for c in SyankoConfiguration.objects.all()}

        # Fetch all available menu items (excluding Aahar items from unlinked/inactive restaurants)
        available_items = MenuItem.objects.filter(
            is_available=True,
            restaurant__is_active=True
        ).exclude(
            aahar_item_id__isnull=False,
            restaurant__aahar_config__isnull=True
        ).exclude(
            aahar_item_id__isnull=False,
            restaurant__aahar_config__is_active=False
        )

        # 1. Hero Product: Use global override if set, otherwise most ordered item
        if 'HERO_PRODUCT' in stored_configs and stored_configs['HERO_PRODUCT']:
            hero_product = stored_configs['HERO_PRODUCT']
        else:
            top_item_qs = BookingItem.objects.values('menu_item').annotate(total_quantity=Sum('quantity')).order_by('-total_quantity').first()
            if top_item_qs:
                hero_product_obj = available_items.filter(id=top_item_qs['menu_item']).first()
            else:
                hero_product_obj = available_items.first()
            
            hero_product = None
            if hero_product_obj:
                hero_product = {
                    "id": hero_product_obj.id,
                    "name": hero_product_obj.name,
                    "subtitle": hero_product_obj.description or "Your Himalayan Comfort.",
                    "price": hero_product_obj.price,
                    "prepTime": f"{hero_product_obj.preparation_time_minutes} mins",
                    "serves": "1 Person",
                    "tag": "Student Favourite",
                    "tagNote": "Recommended for first-time customers.",
                    "show_detail_screen": hero_product_obj.show_detail_screen,
                    "image": request.build_absolute_uri(hero_product_obj.image.url) if hero_product_obj.image else "/images/hero_roll.jpg"
                }

        # 2. Recommended Items: Use global override if set, otherwise Top 4 most ordered
        if 'RECOMMENDED_ITEMS' in stored_configs and stored_configs['RECOMMENDED_ITEMS']:
            recommended_items = stored_configs['RECOMMENDED_ITEMS']
        else:
            top_4_qs = BookingItem.objects.values('menu_item').annotate(total_quantity=Sum('quantity')).order_by('-total_quantity')[:4]
            top_4_ids = [item['menu_item'] for item in top_4_qs]
            if not top_4_ids:
                recommended_qs = available_items[:4]
            else:
                recommended_qs = available_items.filter(id__in=top_4_ids)
            
            recommended_items = [
                {
                    "id": item.id,
                    "name": item.name,
                    "price": item.price,
                    "isVeg": item.is_vegetarian,
                    "show_detail_screen": item.show_detail_screen,
                    "tag": "Student Favourite" if i == 0 else None,
                    "image": request.build_absolute_uri(item.image.url) if item.image else "/images/chicken_roll.jpg"
                }
                for i, item in enumerate(recommended_qs)
            ]

        # 3. Meals: Use global override if set
        if 'MEALS' in stored_configs and stored_configs['MEALS']:
            meals = stored_configs['MEALS']
        else:
            meals_qs = available_items.filter(category__in=['THALI', 'MAIN_COURSE', 'BIRYANI'])[:3]
            if not meals_qs.exists():
                meals_qs = available_items[:3]
                
            meals = [
                {
                    "id": item.id,
                    "name": item.name,
                    "price": item.price,
                    "desc": "Feeds 1–2 Persons",
                    "show_detail_screen": item.show_detail_screen,
                    "badge": "MOST ORDERED MEAL" if i == 0 else None,
                    "image": request.build_absolute_uri(item.image.url) if item.image else "/images/meal.jpg"
                }
                for i, item in enumerate(meals_qs)
            ]

        # 4. Today's Pick (Random or specific)
        todays_pick_obj = available_items.order_by('?').first()
        todays_pick = None
        if todays_pick_obj:
            todays_pick = {
                "name": todays_pick_obj.name,
                "by": "Team SYANKO",
                "id": todays_pick_obj.id,
                "price": todays_pick_obj.price,
                "show_detail_screen": todays_pick_obj.show_detail_screen,
                "image": request.build_absolute_uri(todays_pick_obj.image.url) if todays_pick_obj.image else "/images/momo.jpg"
            }

        # 5. Menu Categories
        if 'MENU_CATEGORIES' in stored_configs and stored_configs['MENU_CATEGORIES']:
            menu_categories = stored_configs['MENU_CATEGORIES']
        else:
            active_custom_cats = MenuCategory.objects.filter(is_active=True, restaurant__is_active=True).distinct()
            if active_custom_cats.exists():
                menu_categories = [
                    {
                        "id": str(cat.id),
                        "icon": cat.icon or '🍽️',
                        "name": cat.name,
                        "description": cat.description or "",
                        "parent": str(cat.parent.id) if cat.parent else None,
                        "parent_name": cat.parent.name if cat.parent else None
                    }
                    for cat in active_custom_cats
                ]
            else:
                cat_dict = dict(MenuItem.CATEGORY_CHOICES)
                distinct_cats = available_items.order_by().values_list('category', flat=True).distinct()
                
                icon_map = {
                    'STARTER': '🥟', 'MAIN_COURSE': '🍽️', 'DESSERT': '🍰', 'BEVERAGE': '🥤',
                    'SNACKS': '🌯', 'BIRYANI': '🍛', 'THALI': '🍱', 'CHINESE': '🍜',
                    'SOUTH_INDIAN': '🥥', 'NORTH_INDIAN': '🥘',
                    'COFFEE SHOP': '☕', 'Cold Drinks': '🥤', 'Juice & Shakes': '🥤',
                    'Non Veg Items': '🍗', 'South Indian': '🥥', 'Sweets & Desert': '🍰',
                    'Thali Combo': '🍱', 'Veg Items': '🥗', 'VEG SOUP': '🍲',
                    'BAR ITEMS': '🍻', 'BREADS & ROTI': '🫓', 'CHINESE ITEMS': '🍜'
                }

                menu_categories = [
                    {
                        "id": cat,
                        "icon": icon_map.get(cat, icon_map.get(cat.upper().replace(' ', '_'), '🍽️')),
                        "name": cat_dict.get(cat, cat)
                    }
                    for cat in distinct_cats
                ]

        # 6. All items
        all_items_serializer = MenuItemSerializer(available_items, many=True, context={'request': request})

        return Response({
            "HERO_PRODUCT": hero_product,
            "RECOMMENDED_ITEMS": recommended_items,
            "MEALS": meals,
            "TODAYS_PICK": todays_pick,
            "MENU_CATEGORIES": menu_categories,
            "ALL_ITEMS": all_items_serializer.data,
            "BRAND": stored_configs.get('BRAND'),
            "FEATURES": stored_configs.get('FEATURES'),
            "ITEM_ADDONS": stored_configs.get('ITEM_ADDONS')
        })


class ComboListCreateView(generics.ListCreateAPIView):
    """
    GET: List combos for a restaurant (public)
    POST: Add combo (Owner/Admin only)
    """
    serializer_class = ComboSerializer

    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        qs = Combo.objects.filter(restaurant_id=restaurant_id)

        # If user is owner or admin, show all combos (active/inactive)
        if self.request.user.is_authenticated:
            if self.request.user.is_superuser or self.request.user.role == 'ADMIN':
                return qs
            restaurant = Restaurant.objects.filter(id=restaurant_id).first()
            if restaurant and restaurant.owner == self.request.user:
                return qs

        # Public users only see active combos
        return qs.filter(is_active=True)

    def get_permissions(self):
        if self.request.method == 'POST':
            return [permissions.IsAuthenticated(), IsOwnerOrAdminOrReadOnly()]
        return [permissions.AllowAny()]

    def perform_create(self, serializer):
        restaurant_id = self.kwargs.get('restaurant_id')
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id)
        # Check permission explicitly
        if not (self.request.user.is_superuser or self.request.user.role == 'ADMIN' or restaurant.owner == self.request.user):
            from rest_framework.exceptions import PermissionDenied
            raise PermissionDenied("You do not have permission to add combos to this restaurant.")
        serializer.save(restaurant=restaurant)


class ComboDetailView(generics.RetrieveUpdateDestroyAPIView):
    """
    GET: Get combo details (public)
    PUT/PATCH: Update combo (Owner/Admin only)
    DELETE: Delete combo (Owner/Admin only)
    """
    serializer_class = ComboSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrAdminOrReadOnly]

    def get_queryset(self):
        restaurant_id = self.kwargs.get('restaurant_id')
        return Combo.objects.filter(restaurant_id=restaurant_id)

from users.models import User

class SyankoOrderView(APIView):
    """
    POST: Create a new SyankoCommerce order and register the user via phone.
    GET: Fetch the most recent order for a returning user by phone.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        phone = request.data.get('phone')
        cart = request.data.get('cart', [])
        order_type = request.data.get('orderType', 'TAKEAWAY')
        total = request.data.get('total', 0)
        payment_method = request.data.get('paymentMethod', 'UPI')
        restaurant_id = request.data.get('restaurant_id')

        if not phone:
            return Response({'error': 'Phone number is required'}, status=status.HTTP_400_BAD_REQUEST)

        # 1. Get or create the User based on phone
        user, created = User.objects.get_or_create(
            username=phone,
            defaults={
                'phone': phone,
                'role': 'CUSTOMER',
                'first_name': 'Guest'
            }
        )

        # 2. Get Restaurant (use provided or first active one)
        if restaurant_id:
            restaurant = Restaurant.objects.filter(id=restaurant_id, is_active=True).first()
        else:
            restaurant = Restaurant.objects.filter(is_active=True).first()

        if not restaurant:
            return Response({'error': 'No active restaurant found'}, status=status.HTTP_400_BAD_REQUEST)

        # 3. Create Booking
        booking = Booking.objects.create(
            restaurant=restaurant,
            customer=user,
            contact_number=phone,
            customer_name=user.first_name or phone,
            booking_type=order_type,
            total_amount=Decimal(str(total)),
            status='CONFIRMED',
            payment_status='PAID' # Assuming paid via frontend simulation
        )

        # 4. Create Booking Items
        aahar_items = []
        for item in cart:
            try:
                raw_id = str(item.get('id', ''))
                menu_item = None
                flavor_text = item.get('flavor', '')
                # Try integer ID lookup first
                if raw_id.isdigit():
                    menu_item = MenuItem.objects.filter(id=int(raw_id)).first()
                if not menu_item and '-' in raw_id:
                    parts = raw_id.split('-')
                    for p in parts:
                        if p.isdigit():
                            menu_item = MenuItem.objects.filter(id=int(p)).first()
                            if menu_item:
                                break

                if menu_item:
                    BookingItem.objects.create(
                        booking=booking,
                        menu_item=menu_item,
                        quantity=item.get('qty', 1),
                        unit_price=Decimal(str(item.get('price', 0))),
                        total_price=Decimal(str(item.get('price', 0))) * item.get('qty', 1),
                        special_instructions=flavor_text
                    )
                    if menu_item.aahar_item_id:
                        item_id_to_send = menu_item.aahar_item_id
                    else:
                        # No Aahar mapping for this item (e.g. an addon/drink that was
                        # never synced to Aahar's catalog). Sending HotelFinder's own
                        # MenuItem.id here would risk colliding with an unrelated
                        # restaurant's real item id in Aahar (item ids aren't
                        # restaurant-scoped). Use Aahar's ad-hoc custom-item convention
                        # (negative id + item_name in payload) instead.
                        item_id_to_send = -abs(menu_item.id)
                    item_name_to_send = menu_item.name
                else:
                    # id has no real MenuItem (e.g. a UI-only slug like
                    # "gd-chicken-roll" from a discovery/demo screen that never got
                    # wired to the real catalog). We deliberately do NOT fall back to
                    # fuzzy name matching here anymore — searching for a substring
                    # like "Chicken Roll" can match several unrelated real items
                    # (e.g. "DOUBLE CHICKEN ROLL"), silently substituting the wrong
                    # dish and price into the order. Safer to send exactly what the
                    # customer was shown as an ad-hoc item instead of guessing.
                    cart_name = (item.get('name') or '').strip()
                    if not cart_name:
                        continue  # nothing usable to send at all
                    item_id_to_send = -(abs(hash(raw_id or cart_name)) % 1000000 + 1)
                    item_name_to_send = cart_name

                aahar_items.append({
                    "item_id": item_id_to_send,
                    "item_name": item_name_to_send,
                    "quantity": item.get('qty', 1),
                    "price": str(item.get('price', 0)),
                    "remarks": flavor_text
                })
            except Exception as e:
                import logging
                logging.getLogger(__name__).warning(f"Error processing cart item for Syanko order: {e}")
                continue

        # 5. Aahar Online Order Integration
        if hasattr(restaurant, 'aahar_config') and restaurant.aahar_config.is_active:
            from . import aahar_service
            aahar_config = restaurant.aahar_config
            
            is_transfer_type = "takeaway_hf" if booking.booking_type == 'TAKEAWAY' else "orders_hf"
            
            from django.utils import timezone
            booking.refresh_from_db()
            booking_time = booking.created_at or timezone.now()
            clean_name = "".join(c for c in booking.customer_name if c.isalnum() or c == '_').replace(' ', '_')
            booking_time_str = booking_time.strftime('%Y%m%d_%H%M%S')
            generated_booking_id = f"{clean_name}_{booking.contact_number}_{booking_time_str}_{booking.id}"[:100]

            order_payload = {
                "restaurant_id": int(aahar_config.aahar_restaurant_service_id),
                "customer_name": f"{booking.customer_name} (#{booking.id})",
                "customer_mobile": booking.contact_number,
                "customer_phone": booking.contact_number,
                "booking_id": generated_booking_id,
                "payment_status": "Paid", # We set payment_status='PAID' above
                "payment_mode": payment_method,
                "total_amount": str(total),
                "is_transfer": is_transfer_type,
                "items": aahar_items
            }
            
            try:
                response = aahar_service.submit_online_order(
                    int(aahar_config.aahar_restaurant_service_id), 
                    order_payload,
                    base_url=aahar_config.aahar_domain
                )
                if response and (response.get('code') == 200 or response.get('code') == 201):
                    data = response.get('data', {})
                    aahar_order_id = data.get('order_id')
                    if aahar_order_id:
                        AaharOrderSync.objects.create(
                            aahar_config=aahar_config,
                            aahar_order_id=aahar_order_id,
                            aahar_order_number=data.get('parcel_number', ''),
                            invoice=f"HF-{booking.id}",
                            booking_id=generated_booking_id,
                            is_transfer=is_transfer_type,
                            grand_total=booking.total_amount,
                            payment_status='PAID',
                            payment_mode='UPI',
                            order_data=order_payload
                        )
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.error(f"Failed to submit Syanko order to Aahar: {e}")

        # Send order confirmation
        from .whatsapp_service import send_order_confirmation, send_reply
        send_order_confirmation(booking)

        if created:
            import string
            import random
            # Generate a 8-character random alphanumeric password
            characters = string.ascii_letters + string.digits
            generated_password = ''.join(random.choice(characters) for i in range(8))
            
            user.set_password(generated_password)
            user.save()
            
            creds_msg = (
                "🎉 Welcome to Syanko!\n"
                "Your account has been automatically created.\n"
                f"👤 Username: {phone}\n"
                f"🔑 Password: {generated_password}\n\n"
                "You can use these credentials to log in later."
            )
            send_reply(phone, creds_msg)

        order_number = f"#{booking.id}"
        # Check if synced to Aahar and has parcel number
        aahar_sync = AaharOrderSync.objects.filter(booking_id__endswith=f"_{booking.id}").first()
        if aahar_sync and aahar_sync.aahar_order_number:
            order_number = f"#{aahar_sync.aahar_order_number}"

        return Response({
            'message': 'Order placed successfully',
            'booking_id': booking.id,
            'order_number': order_number,
            'status': booking.status
        }, status=status.HTTP_201_CREATED)

    def get(self, request):
        phone = request.query_params.get('phone')
        if not phone:
            return Response({'error': 'Phone parameter is required'}, status=status.HTTP_400_BAD_REQUEST)

        # Find latest booking for this phone
        latest_booking = Booking.objects.filter(
            contact_number=phone
        ).order_by('-created_at').first()

        if not latest_booking:
            return Response({'message': 'No previous orders found'}, status=status.HTTP_404_NOT_FOUND)

        # Serialize items manually to match frontend cart structure
        items = []
        for b_item in latest_booking.items.all():
            items.append({
                'id': b_item.menu_item.id,
                'name': b_item.menu_item.name,
                'price': float(b_item.unit_price),
                'qty': b_item.quantity,
                'flavor': b_item.special_instructions or 'Classic',
                'image': b_item.menu_item.image.url if b_item.menu_item.image else None
            })

        order_num = f"#{latest_booking.id}"
        aahar_sync = AaharOrderSync.objects.filter(booking_id__endswith=f"_{latest_booking.id}").first()
        if aahar_sync and aahar_sync.aahar_order_number:
            order_num = f"#{aahar_sync.aahar_order_number}"

        return Response({
            'booking_id': latest_booking.id,
            'order_number': order_num,
            'status': latest_booking.status,
            'items': items,
            'created_at': latest_booking.created_at
        })

