"""
Overview service for Aimantis dashboard.

Calculates today's operational metrics:
- Check-ins and check-outs
- Guests currently in structure
- Room occupancy status
"""

from datetime import date
from typing import Dict, Any, Optional

from django.db.models import Count, Q

from bookings.models import Booking
from guests.models import Guest
from properties.models import Property, PropertyType
from structures.models import Structure

from dashboard.services.utils import get_today


class OverviewService:
    """
    Service for calculating dashboard overview metrics.
    
    All calculations are optimized to minimize database queries
    and avoid N+1 problems.
    """
    
    def __init__(self, structure_id: Optional[int] = None):
        """
        Initialize the overview service.
        
        Args:
            structure_id: Optional structure ID for multi-tenant filtering
        """
        self.structure_id = structure_id
        self.today = get_today()
    
    def _get_structure_filter(self) -> Dict[str, Any]:
        """Get structure filter dict for queries."""
        return {"structure_id": self.structure_id} if self.structure_id else {}
    
    def _get_property_queryset(self):
        """Get filtered property queryset."""
        qs = Property.objects.all()
        if self.structure_id:
            qs = qs.filter(structure_id=self.structure_id)
        return qs
    
    def get_checkins_today(self) -> int:
        """
        Count bookings checking in today.
        
        Uses check_in_date == today.
        Counts all arrivals scheduled for today regardless of check-in status.
        """
        return Booking.objects.filter(
            check_in_date=self.today,
            **self._get_structure_filter()
        ).count()
    
    def get_checkouts_today(self) -> int:
        """
        Count bookings checking out today.
        
        Uses check_out_date == today.
        """
        return Booking.objects.filter(
            check_out_date=self.today,
            **self._get_structure_filter()
        ).count()
    
    def get_guests_in_structure(self) -> int:
        """
        Count guests currently staying in the structure.
        
        Uses ACTIVE booking logic (date-based, not check-in status):
        - check_in_date <= today
        - check_out_date > today (exclusive)
        
        Counts all guests from active reservations for today.
        """
        current_bookings = Booking.objects.filter(
            check_in_date__lte=self.today,
            check_out_date__gt=self.today,
            **self._get_structure_filter()
        ).only('id')  # Only need booking IDs for the subquery, not full booking objects
        
        return Guest.objects.filter(
            booking__in=current_bookings
        ).count()
    
    def get_total_rooms(self) -> int:
        """Count total rooms in the structure."""
        return self._get_property_queryset().count()
    
    def get_occupied_rooms(self) -> int:
        """
        Count physically occupied rooms.
        
        Uses ACTIVE booking logic (date-based, not check-in status):
        - check_in_date <= today
        - check_out_date > today (exclusive)
        
        Counts all rooms with active reservations for today.
        Returns DISTINCT property count to avoid double-counting.
        """
        # OPTIMIZATION: Combine occupied rooms and available rooms calculation
        # to avoid duplicate queries (was querying twice before)
        occupied_property_ids = Booking.objects.filter(
            check_in_date__lte=self.today,
            check_out_date__gt=self.today,
            **self._get_structure_filter()
        ).values_list('property_id', flat=True).distinct()
        
        # Count occupied rooms
        occupied_count = Property.objects.filter(
            id__in=occupied_property_ids
        ).count()
        
        return occupied_count
    
    def get_available_rooms(self) -> int:
        """
        Count available rooms.
        
        Available = Total - Occupied
        """
        total = self.get_total_rooms()
        occupied = self.get_occupied_rooms()
        return max(0, total - occupied)
    
    def get_total_beds(self) -> int:
        """
        Count total beds across all property types in the structure.
        
        Optimized to use prefetch_related to avoid N+1 queries.
        """
        property_types = PropertyType.objects.prefetch_related("beds")
        
        if self.structure_id:
            property_types = property_types.filter(structure_id=self.structure_id)
        
        total_beds = 0
        for pt in property_types:
            # beds is already prefetched
            total_beds += sum(bed.quantity for bed in pt.beds.all())
        
        return total_beds
    
    def get_overview(self) -> Dict[str, Any]:
        """
        Get complete overview metrics in a single optimized call.
        
        Returns:
            Dict with all overview metrics
        """
        # OPTIMIZATION: Calculate occupied_rooms first, then derive available_rooms
        # This avoids duplicate queries in get_available_rooms()
        occupied_rooms = self.get_occupied_rooms()
        total_rooms = self.get_total_rooms()
        available_rooms = max(0, total_rooms - occupied_rooms)
        
        return {
            "checkins_today": self.get_checkins_today(),
            "checkouts_today": self.get_checkouts_today(),
            "guests_in_structure": self.get_guests_in_structure(),
            "available_rooms": available_rooms,
            "occupied_rooms": occupied_rooms,
            "total_rooms": total_rooms,
            "total_beds": self.get_total_beds(),
        }
