"""
Room Assignment Optimization Service
Provides intelligent room assignment based on guest preferences, room attributes, and availability
"""
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func
from datetime import datetime
from typing import List, Optional, Dict
from ..models.room import Room, RoomStatus
from ...bookings.models.booking import Booking, BookingStatus
from ..models.room_attribute import RoomAttribute
from ...auth.models.user import User


class RoomAssignmentService:
    """Service for optimizing room assignments"""
    
    @staticmethod
    def find_best_room(
        db: Session,
        room_type_id: int,
        check_in: datetime,
        check_out: datetime,
        num_guests: int,
        guest_preferences: Optional[Dict] = None,
        exclude_room_ids: Optional[List[int]] = None
    ) -> Optional[Room]:
        """
        Find the best available room for a booking based on multiple criteria
        
        Args:
            db: Database session
            room_type_id: Required room type ID
            check_in: Check-in date
            check_out: Check-out date
            num_guests: Number of guests
            guest_preferences: Optional dict with preferences like {'view': 'ocean', 'floor': 'high', 'quiet': True}
            exclude_room_ids: List of room IDs to exclude from consideration
            
        Returns:
            Best matching Room or None if no room available
        """
        # Base query: available rooms of the correct type
        query = db.query(Room).filter(
            Room.room_type_id == room_type_id,
            Room.status == RoomStatus.available
        )
        
        # Exclude specific rooms if provided
        if exclude_room_ids:
            query = query.filter(~Room.id.in_(exclude_room_ids))
        
        # Exclude rooms with overlapping bookings
        overlapping_rooms = db.query(Booking.room_id).filter(
            and_(
                Booking.status != BookingStatus.cancelled,
                Booking.check_in_date < check_out,
                Booking.check_out_date > check_in
            )
        ).subquery()
        query = query.filter(~Room.id.in_(db.query(overlapping_rooms.c.room_id)))
        
        # Exclude rooms blocked by maintenance
        from ..models.room_maintenance import RoomMaintenance, MaintenanceStatus
        blocked_rooms = db.query(RoomMaintenance.room_id).filter(
            and_(
                RoomMaintenance.blocks_room == True,
                RoomMaintenance.status.in_([MaintenanceStatus.scheduled, MaintenanceStatus.in_progress]),
                or_(
                    and_(
                        RoomMaintenance.block_start.isnot(None),
                        RoomMaintenance.block_end.isnot(None),
                        RoomMaintenance.block_start < check_out,
                        RoomMaintenance.block_end > check_in
                    ),
                    and_(
                        RoomMaintenance.scheduled_start < check_out,
                        RoomMaintenance.scheduled_end.isnot(None),
                        RoomMaintenance.scheduled_end > check_in
                    )
                )
            )
        ).subquery()
        query = query.filter(~Room.id.in_(db.query(blocked_rooms.c.room_id)))
        
        available_rooms = query.all()
        
        if not available_rooms:
            return None
        
        # Score rooms based on preferences
        if guest_preferences:
            scored_rooms = []
            for room in available_rooms:
                score = RoomAssignmentService._calculate_room_score(room, guest_preferences, db)
                scored_rooms.append((score, room))
            
            # Sort by score (highest first) and return best match
            scored_rooms.sort(key=lambda x: x[0], reverse=True)
            return scored_rooms[0][1] if scored_rooms else None
        
        # If no preferences, return first available room
        return available_rooms[0] if available_rooms else None
    
    @staticmethod
    def _calculate_room_score(room: Room, preferences: Dict, db: Session) -> float:
        """
        Calculate a score for a room based on guest preferences
        Higher score = better match
        """
        score = 0.0
        
        # View preference
        if 'view' in preferences and room.view:
            if preferences['view'].lower() in room.view.lower():
                score += 10.0
        
        # Floor preference
        if 'floor' in preferences:
            preferred_floor = preferences['floor']
            if preferred_floor == 'high' and room.floor >= 3:
                score += 5.0
            elif preferred_floor == 'low' and room.floor <= 2:
                score += 5.0
            elif isinstance(preferred_floor, int) and room.floor == preferred_floor:
                score += 10.0
        
        # Quiet preference
        if preferences.get('quiet'):
            # Check room attributes for noise level
            quiet_attr = db.query(RoomAttribute).filter(
                and_(
                    RoomAttribute.room_id == room.id,
                    RoomAttribute.attribute_name == 'noise_level',
                    RoomAttribute.is_active == True
                )
            ).first()
            if quiet_attr and 'quiet' in quiet_attr.attribute_value.lower():
                score += 8.0
        
        # Accessibility preference
        if preferences.get('accessible'):
            accessible_attr = db.query(RoomAttribute).filter(
                and_(
                    RoomAttribute.room_id == room.id,
                    RoomAttribute.attribute_name == 'accessibility',
                    RoomAttribute.is_active == True
                )
            ).first()
            if accessible_attr and 'accessible' in accessible_attr.attribute_value.lower():
                score += 15.0  # High priority for accessibility
        
        # Featured rooms get a small boost
        if room.featured:
            score += 2.0
        
        # Rooms with higher ratings get a boost
        from ...reviews.models.review import Review, ReviewStatus
        avg_rating = db.query(func.avg(Review.rating)).filter(
            and_(
                Review.room_id == room.id,
                Review.status == ReviewStatus.approved
            )
        ).scalar()
        if avg_rating:
            score += float(avg_rating) * 2.0
        
        return score
    
    @staticmethod
    def get_room_availability_calendar(
        db: Session,
        room_id: int,
        start_date: datetime,
        end_date: datetime
    ) -> Dict:
        """
        Get detailed availability information for a room over a date range
        """
        room = db.query(Room).filter(Room.id == room_id).first()
        if not room:
            return None
        
        # Get bookings
        bookings = db.query(Booking).filter(
            and_(
                Booking.room_id == room_id,
                Booking.status != BookingStatus.cancelled,
                Booking.check_in_date < end_date,
                Booking.check_out_date > start_date
            )
        ).all()
        
        # Get maintenance blocks
        from ..models.room_maintenance import RoomMaintenance, MaintenanceStatus
        maintenance_blocks = db.query(RoomMaintenance).filter(
            and_(
                RoomMaintenance.room_id == room_id,
                RoomMaintenance.blocks_room == True,
                RoomMaintenance.status.in_([MaintenanceStatus.scheduled, MaintenanceStatus.in_progress]),
                or_(
                    and_(
                        RoomMaintenance.block_start.isnot(None),
                        RoomMaintenance.block_end.isnot(None),
                        RoomMaintenance.block_start < end_date,
                        RoomMaintenance.block_end > start_date
                    ),
                    and_(
                        RoomMaintenance.scheduled_start < end_date,
                        RoomMaintenance.scheduled_end.isnot(None),
                        RoomMaintenance.scheduled_end > start_date
                    )
                )
            )
        ).all()
        
        return {
            'room_id': room_id,
            'room_number': room.room_number,
            'status': room.status.value,
            'bookings': [
                {
                    'id': b.id,
                    'check_in': b.check_in_date.isoformat(),
                    'check_out': b.check_out_date.isoformat(),
                    'status': b.status.value
                }
                for b in bookings
            ],
            'maintenance_blocks': [
                {
                    'id': m.id,
                    'title': m.title,
                    'start': (m.block_start or m.scheduled_start).isoformat(),
                    'end': (m.block_end or m.scheduled_end).isoformat() if (m.block_end or m.scheduled_end) else None,
                    'type': m.maintenance_type.value
                }
                for m in maintenance_blocks
            ]
        }

