from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Request, Query
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func
from sqlalchemy.exc import IntegrityError
from typing import List, Optional
from datetime import datetime
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ..models.room import Room, RoomStatus
from ...analytics.services.audit_service import audit_service
from ..models.room_type import RoomType
from ..schemas.room import CreateRoomRequest, UpdateRoomRequest, BulkDeleteRoomsRequest, UpdateAmenityRequest, CreateRoomTypeRequest, UpdateRoomTypeRequest
from ...shared.utils.response_helpers import success_response
from ...reviews.models.review import Review, ReviewStatus
from ...bookings.models.booking import Booking, BookingStatus
from ...hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType
from ..services.room_service import get_rooms_with_ratings, get_amenities_list, normalize_images, get_base_url
import os
import aiofiles
from pathlib import Path

logger = get_logger(__name__)
router = APIRouter(prefix='/rooms', tags=['rooms'])

@router.get('/')
async def get_rooms(request: Request, type: Optional[str]=Query(None), minPrice: Optional[float]=Query(None), maxPrice: Optional[float]=Query(None), capacity: Optional[int]=Query(None), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=1000), sort: Optional[str]=Query(None), featured: Optional[bool]=Query(None), db: Session=Depends(get_db)):
    try:
        where_clause = {}
        room_type_where = {}
        if featured is not None:
            where_clause['featured'] = featured
        if type:
            room_type_where['name'] = f'%{type}%'
        if capacity:
            room_type_where['capacity'] = capacity
        if minPrice or maxPrice:
            if minPrice:
                room_type_where['base_price_min'] = minPrice
            if maxPrice:
                room_type_where['base_price_max'] = maxPrice
        query = db.query(Room).join(RoomType)
        if where_clause.get('featured') is not None:
            query = query.filter(Room.featured == where_clause['featured'])
        if room_type_where.get('name'):
            query = query.filter(RoomType.name.like(room_type_where['name']))
        if room_type_where.get('capacity'):
            query = query.filter(RoomType.capacity >= room_type_where['capacity'])
        if room_type_where.get('base_price_min'):
            query = query.filter(RoomType.base_price >= room_type_where['base_price_min'])
        if room_type_where.get('base_price_max'):
            query = query.filter(RoomType.base_price <= room_type_where['base_price_max'])
        total = query.count()
        if sort == 'newest' or sort == 'created_at':
            query = query.order_by(Room.created_at.desc())
        else:
            query = query.order_by(Room.featured.desc(), Room.created_at.desc())
        offset = (page - 1) * limit
        rooms = query.offset(offset).limit(limit).all()
        base_url = get_base_url(request)
        rooms_with_ratings = await get_rooms_with_ratings(db, rooms, base_url)
        return {'status': 'success', 'data': {'rooms': rooms_with_ratings, 'pagination': {'total': total, 'page': page, 'limit': limit, 'totalPages': (total + limit - 1) // limit}}}
    except Exception as e:
        logger.error(f'Error fetching rooms: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/amenities')
async def get_amenities(db: Session=Depends(get_db)):
    try:
        amenities = await get_amenities_list(db)
        return {'status': 'success', 'data': {'amenities': amenities}}
    except Exception as e:
        logger.error(f'Error fetching amenities: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/amenities/{old_name}', dependencies=[Depends(authorize_roles('admin'))])
async def update_amenity(old_name: str, request: UpdateAmenityRequest, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    """Update/rename an amenity across all rooms and room types."""
    try:
        import json
        updated_count = 0
        
        # Update in room types
        room_types = db.query(RoomType).all()
        for rt in room_types:
            if rt.amenities:
                amenities_list = []
                if isinstance(rt.amenities, list):
                    amenities_list = rt.amenities
                elif isinstance(rt.amenities, str):
                    try:
                        amenities_list = json.loads(rt.amenities)
                    except:
                        amenities_list = [s.strip() for s in rt.amenities.split(',') if s.strip()]
                
                if old_name in amenities_list:
                    amenities_list = [request.new_name if a == old_name else a for a in amenities_list]
                    rt.amenities = amenities_list
                    updated_count += 1
        
        # Update in rooms
        rooms = db.query(Room).all()
        for room in rooms:
            if room.amenities:
                amenities_list = []
                if isinstance(room.amenities, list):
                    amenities_list = room.amenities
                elif isinstance(room.amenities, str):
                    try:
                        amenities_list = json.loads(room.amenities)
                    except:
                        amenities_list = [s.strip() for s in room.amenities.split(',') if s.strip()]
                
                if old_name in amenities_list:
                    amenities_list = [request.new_name if a == old_name else a for a in amenities_list]
                    room.amenities = amenities_list
                    updated_count += 1
        
        db.commit()
        return success_response(
            data={'updated_count': updated_count, 'old_name': old_name, 'new_name': request.new_name},
            message=f'Amenity "{old_name}" updated to "{request.new_name}" in {updated_count} location(s)'
        )
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating amenity: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/amenities/{amenity_name}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_amenity(amenity_name: str, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    """Remove an amenity from all rooms and room types."""
    try:
        import json
        updated_count = 0
        
        # Remove from room types
        room_types = db.query(RoomType).all()
        for rt in room_types:
            if rt.amenities:
                amenities_list = []
                if isinstance(rt.amenities, list):
                    amenities_list = rt.amenities
                elif isinstance(rt.amenities, str):
                    try:
                        amenities_list = json.loads(rt.amenities)
                    except:
                        amenities_list = [s.strip() for s in rt.amenities.split(',') if s.strip()]
                
                if amenity_name in amenities_list:
                    amenities_list = [a for a in amenities_list if a != amenity_name]
                    rt.amenities = amenities_list if amenities_list else []
                    updated_count += 1
        
        # Remove from rooms
        rooms = db.query(Room).all()
        for room in rooms:
            if room.amenities:
                amenities_list = []
                if isinstance(room.amenities, list):
                    amenities_list = room.amenities
                elif isinstance(room.amenities, str):
                    try:
                        amenities_list = json.loads(room.amenities)
                    except:
                        amenities_list = [s.strip() for s in room.amenities.split(',') if s.strip()]
                
                if amenity_name in amenities_list:
                    amenities_list = [a for a in amenities_list if a != amenity_name]
                    room.amenities = amenities_list if amenities_list else []
                    updated_count += 1
        
        db.commit()
        return success_response(
            data={'updated_count': updated_count, 'amenity_name': amenity_name},
            message=f'Amenity "{amenity_name}" removed from {updated_count} location(s)'
        )
    except Exception as e:
        db.rollback()
        logger.error(f'Error deleting amenity: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/room-types')
async def get_room_types(db: Session=Depends(get_db)):
    """Get all room types for dropdowns and forms."""
    try:
        room_types = db.query(RoomType).order_by(RoomType.name).all()
        room_types_list = [
            {
                'id': rt.id,
                'name': rt.name,
                'description': rt.description,
                'base_price': float(rt.base_price) if rt.base_price else 0.0,
                'capacity': rt.capacity,
                'amenities': rt.amenities if rt.amenities else [],
            }
            for rt in room_types
        ]
        return {'status': 'success', 'data': {'room_types': room_types_list}}
    except Exception as e:
        logger.error(f'Error fetching room types: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/room-types/{id}')
async def get_room_type(id: int, db: Session=Depends(get_db)):
    """Get a specific room type by ID."""
    try:
        room_type = db.query(RoomType).filter(RoomType.id == id).first()
        if not room_type:
            raise HTTPException(status_code=404, detail='Room type not found')
        
        room_type_dict = {
            'id': room_type.id,
            'name': room_type.name,
            'description': room_type.description,
            'base_price': float(room_type.base_price) if room_type.base_price else 0.0,
            'capacity': room_type.capacity,
            'amenities': room_type.amenities if room_type.amenities else [],
            'created_at': room_type.created_at.isoformat() if room_type.created_at else None,
            'updated_at': room_type.updated_at.isoformat() if room_type.updated_at else None,
        }
        return success_response(data={'room_type': room_type_dict}, message='Room type retrieved successfully')
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching room type: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/room-types', dependencies=[Depends(authorize_roles('admin'))])
async def create_room_type(
    room_type_data: CreateRoomTypeRequest,
    request: Request,
    current_user: User=Depends(authorize_roles('admin')),
    db: Session=Depends(get_db)
):
    """Create a new room type."""
    try:
        # Check for duplicate name
        existing = db.query(RoomType).filter(RoomType.name == room_type_data.name).first()
        if existing:
            raise HTTPException(status_code=400, detail='Room type with this name already exists')
        
        room_type = RoomType(
            name=room_type_data.name,
            description=room_type_data.description,
            base_price=room_type_data.base_price,
            capacity=room_type_data.capacity,
            amenities=room_type_data.amenities or []
        )
        
        db.add(room_type)
        db.commit()
        db.refresh(room_type)
        
        # Audit log
        audit_service.log_action(
            db=db,
            user_id=current_user.id,
            action='create',
            resource_type='room_type',
            resource_id=room_type.id,
            details={'name': room_type.name, 'base_price': float(room_type.base_price)}
        )
        
        room_type_dict = {
            'id': room_type.id,
            'name': room_type.name,
            'description': room_type.description,
            'base_price': float(room_type.base_price) if room_type.base_price else 0.0,
            'capacity': room_type.capacity,
            'amenities': room_type.amenities if room_type.amenities else [],
            'created_at': room_type.created_at.isoformat() if room_type.created_at else None,
            'updated_at': room_type.updated_at.isoformat() if room_type.updated_at else None,
        }
        
        return success_response(data={'room_type': room_type_dict}, message='Room type created successfully')
    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        logger.error(f'Database integrity error during room type creation: {str(e)}')
        raise HTTPException(status_code=409, detail='Room type conflict detected. Please check room type name.')
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating room type: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while creating the room type')


@router.put('/room-types/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def update_room_type(
    id: int,
    room_type_data: UpdateRoomTypeRequest,
    request: Request,
    current_user: User=Depends(authorize_roles('admin')),
    db: Session=Depends(get_db)
):
    """Update a room type."""
    try:
        room_type = db.query(RoomType).filter(RoomType.id == id).first()
        if not room_type:
            raise HTTPException(status_code=404, detail='Room type not found')
        
        # Check for duplicate name if name is being updated
        if room_type_data.name and room_type_data.name != room_type.name:
            existing = db.query(RoomType).filter(RoomType.name == room_type_data.name).first()
            if existing:
                raise HTTPException(status_code=400, detail='Room type with this name already exists')
        
        # Update fields
        update_data = room_type_data.model_dump(exclude_unset=True)
        for key, value in update_data.items():
            setattr(room_type, key, value)
        
        db.commit()
        db.refresh(room_type)
        
        # Audit log
        audit_service.log_action(
            db=db,
            user_id=current_user.id,
            action='update',
            resource_type='room_type',
            resource_id=room_type.id,
            details=update_data
        )
        
        room_type_dict = {
            'id': room_type.id,
            'name': room_type.name,
            'description': room_type.description,
            'base_price': float(room_type.base_price) if room_type.base_price else 0.0,
            'capacity': room_type.capacity,
            'amenities': room_type.amenities if room_type.amenities else [],
            'created_at': room_type.created_at.isoformat() if room_type.created_at else None,
            'updated_at': room_type.updated_at.isoformat() if room_type.updated_at else None,
        }
        
        return success_response(data={'room_type': room_type_dict}, message='Room type updated successfully')
    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        logger.error(f'Database integrity error during room type update: {str(e)}')
        raise HTTPException(status_code=409, detail='Room type conflict detected. Please check room type name.')
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating room type: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while updating the room type')


@router.delete('/room-types/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_room_type(
    id: int,
    request: Request,
    current_user: User=Depends(authorize_roles('admin')),
    db: Session=Depends(get_db)
):
    """Delete a room type."""
    try:
        room_type = db.query(RoomType).filter(RoomType.id == id).first()
        if not room_type:
            raise HTTPException(status_code=404, detail='Room type not found')
        
        # Check if room type is being used by any rooms
        rooms_count = db.query(Room).filter(Room.room_type_id == id).count()
        if rooms_count > 0:
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete room type. It is being used by {rooms_count} room(s). Please remove or reassign rooms first.'
            )
        
        # Check if room type is being used by any rate plans
        from ..models.rate_plan import RatePlan
        rate_plans_count = db.query(RatePlan).filter(RatePlan.room_type_id == id).count()
        if rate_plans_count > 0:
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete room type. It is being used by {rate_plans_count} rate plan(s). Please remove or reassign rate plans first.'
            )
        
        # Check if room type is being used by any packages
        from ...loyalty.models.package import Package
        packages_count = db.query(Package).filter(Package.room_type_id == id).count()
        if packages_count > 0:
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete room type. It is being used by {packages_count} package(s). Please remove or reassign packages first.'
            )
        
        # Store name for audit log
        room_type_name = room_type.name
        
        db.delete(room_type)
        db.commit()
        
        # Audit log
        audit_service.log_action(
            db=db,
            user_id=current_user.id,
            action='delete',
            resource_type='room_type',
            resource_id=id,
            details={'name': room_type_name}
        )
        
        return success_response(message='Room type deleted successfully')
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error deleting room type: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while deleting the room type')

@router.get('/available')
async def search_available_rooms(request: Request, from_date: str=Query(..., alias='from'), to_date: str=Query(..., alias='to'), roomId: Optional[int]=Query(None, alias='roomId'), type: Optional[str]=Query(None), capacity: Optional[int]=Query(None), page: int=Query(1, ge=1), limit: int=Query(12, ge=1, le=100), db: Session=Depends(get_db)):
    try:
        try:
            if 'T' in from_date or 'Z' in from_date or '+' in from_date:
                check_in = datetime.fromisoformat(from_date.replace('Z', '+00:00'))
            else:
                check_in = datetime.strptime(from_date, '%Y-%m-%d')
        except ValueError:
            raise HTTPException(status_code=400, detail=f'Invalid from date format: {from_date}')
        try:
            if 'T' in to_date or 'Z' in to_date or '+' in to_date:
                check_out = datetime.fromisoformat(to_date.replace('Z', '+00:00'))
            else:
                check_out = datetime.strptime(to_date, '%Y-%m-%d')
        except ValueError:
            raise HTTPException(status_code=400, detail=f'Invalid to date format: {to_date}')
        if roomId:
            room = db.query(Room).filter(Room.id == roomId).first()
            if not room:
                raise HTTPException(status_code=404, detail='Room not found')
            if room.status != RoomStatus.available:
                return {'status': 'success', 'data': {'available': False, 'message': 'Room is not available', 'room_id': roomId}}
            overlapping = db.query(Booking).filter(and_(Booking.room_id == roomId, Booking.status != BookingStatus.cancelled, Booking.check_in_date < check_out, Booking.check_out_date > check_in)).first()
            if overlapping:
                return {'status': 'success', 'data': {'available': False, 'message': 'Room is already booked for the selected dates', 'room_id': roomId}}
            
            # Check for maintenance blocks
            from ..models.room_maintenance import RoomMaintenance, MaintenanceStatus
            maintenance_block = db.query(RoomMaintenance).filter(
                and_(
                    RoomMaintenance.room_id == roomId,
                    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
                        )
                    )
                )
            ).first()
            if maintenance_block:
                return {'status': 'success', 'data': {'available': False, 'message': f'Room is blocked for maintenance: {maintenance_block.title}', 'room_id': roomId}}
            
            return {'status': 'success', 'data': {'available': True, 'message': 'Room is available', 'room_id': roomId}}
        if check_in >= check_out:
            raise HTTPException(status_code=400, detail='Check-out date must be after check-in date')
        query = db.query(Room).join(RoomType).filter(Room.status == RoomStatus.available)
        if type:
            query = query.filter(RoomType.name.like(f'%{type}%'))
        if capacity:
            query = query.filter(RoomType.capacity >= capacity)
        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)))
        total = query.count()
        query = query.order_by(Room.featured.desc(), Room.created_at.desc())
        offset = (page - 1) * limit
        rooms = query.offset(offset).limit(limit).all()
        base_url = get_base_url(request)
        rooms_with_ratings = await get_rooms_with_ratings(db, rooms, base_url)
        return {'status': 'success', 'data': {'rooms': rooms_with_ratings, 'search': {'from': from_date, 'to': to_date, 'type': type, 'capacity': capacity}, 'pagination': {'total': total, 'page': page, 'limit': limit, 'totalPages': (total + limit - 1) // limit}}}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f'Error searching available rooms: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/id/{id}')
async def get_room_by_id(id: int, request: Request, db: Session=Depends(get_db)):
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        review_stats = db.query(func.avg(Review.rating).label('average_rating'), func.count(Review.id).label('total_reviews')).filter(and_(Review.room_id == room.id, Review.status == ReviewStatus.approved)).first()
        base_url = get_base_url(request)
        room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities, 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None, 'average_rating': round(float(review_stats.average_rating or 0), 1) if review_stats and review_stats.average_rating else None, 'total_reviews': review_stats.total_reviews or 0 if review_stats else 0}
        try:
            room_dict['images'] = normalize_images(room.images, base_url)
        except:
            room_dict['images'] = []
        if room.room_type:
            room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities, 'images': []}
        return {'status': 'success', 'data': {'room': room_dict}}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{room_number}')
async def get_room_by_number(room_number: str, request: Request, db: Session=Depends(get_db)):
    try:
        room = db.query(Room).filter(Room.room_number == room_number).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        review_stats = db.query(func.avg(Review.rating).label('average_rating'), func.count(Review.id).label('total_reviews')).filter(and_(Review.room_id == room.id, Review.status == ReviewStatus.approved)).first()
        base_url = get_base_url(request)
        room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities, 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None, 'average_rating': round(float(review_stats.average_rating or 0), 1) if review_stats and review_stats.average_rating else None, 'total_reviews': review_stats.total_reviews or 0 if review_stats else 0}
        try:
            room_dict['images'] = normalize_images(room.images, base_url)
        except:
            room_dict['images'] = []
        if room.room_type:
            room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities, 'images': []}
        return {'status': 'success', 'data': {'room': room_dict}}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/', dependencies=[Depends(authorize_roles('admin'))])
async def create_room(room_data: CreateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
    """Create a new room with validated input using Pydantic schema."""
    try:
        # Lock room type to prevent race conditions
        room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).with_for_update().first()
        if not room_type:
            db.rollback()
            raise HTTPException(status_code=404, detail='Room type not found')
        
        # Check for duplicate room number with locking
        existing = db.query(Room).filter(Room.room_number == room_data.room_number).with_for_update().first()
        if existing:
            db.rollback()
            raise HTTPException(status_code=400, detail='Room number already exists')
        
        # Use price from request or default to room type base price
        room_price = room_data.price if room_data.price is not None else float(room_type.base_price) if room_type.base_price else 0.0
        
        # Inherit amenities from room type if not provided or empty
        room_amenities = room_data.amenities if room_data.amenities else []
        if not room_amenities and room_type.amenities:
            # Convert room type amenities to list if needed
            if isinstance(room_type.amenities, list):
                room_amenities = room_type.amenities
            elif isinstance(room_type.amenities, str):
                try:
                    import json
                    room_amenities = json.loads(room_type.amenities)
                except:
                    room_amenities = [s.strip() for s in room_type.amenities.split(',') if s.strip()]
        
        room = Room(
            room_type_id=room_data.room_type_id,
            room_number=room_data.room_number,
            floor=room_data.floor,
            status=RoomStatus(room_data.status),
            featured=room_data.featured,
            price=room_price,
            description=room_data.description,
            capacity=room_data.capacity,
            room_size=room_data.room_size,
            view=room_data.view,
            amenities=room_amenities
        )
        db.add(room)
        db.flush()
        
        # Commit transaction
        db.commit()
        db.refresh(room)
        base_url = get_base_url(request)
        room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities if room.amenities else [], 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None}
        try:
            room_dict['images'] = normalize_images(room.images, base_url)
        except:
            room_dict['images'] = []
        if room.room_type:
            room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities if room.room_type.amenities else [], 'images': []}
        return success_response(data={'room': room_dict}, message='Room created successfully')
    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        logger.error(f'Database integrity error during room creation: {str(e)}')
        raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating room: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while creating the room')

@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    """Update a room with validated input using Pydantic schema."""
    try:
        # Lock room row to prevent race conditions
        room = db.query(Room).filter(Room.id == id).with_for_update().first()
        if not room:
            db.rollback()
            raise HTTPException(status_code=404, detail='Room not found')
        
        if room_data.room_type_id:
            room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).first()
            if not room_type:
                db.rollback()
                raise HTTPException(status_code=404, detail='Room type not found')
            room.room_type_id = room_data.room_type_id
            
            # Inherit amenities from room type if amenities not provided in update
            if not room_data.amenities and room_type.amenities:
                # Convert room type amenities to list if needed
                if isinstance(room_type.amenities, list):
                    room.amenities = room_type.amenities
                elif isinstance(room_type.amenities, str):
                    try:
                        import json
                        room.amenities = json.loads(room_type.amenities)
                    except:
                        room.amenities = [s.strip() for s in room_type.amenities.split(',') if s.strip()]
        elif not room_data.amenities:
            # If room type not changed but amenities not provided, inherit from current room type
            if room.room_type and room.room_type.amenities:
                if isinstance(room.room_type.amenities, list):
                    room.amenities = room.room_type.amenities
                elif isinstance(room.room_type.amenities, str):
                    try:
                        import json
                        room.amenities = json.loads(room.room_type.amenities)
                    except:
                        room.amenities = [s.strip() for s in room.room_type.amenities.split(',') if s.strip()]
        
        if room_data.room_number is not None:
            # Check for duplicate room number
            existing = db.query(Room).filter(Room.room_number == room_data.room_number, Room.id != id).first()
            if existing:
                db.rollback()
                raise HTTPException(status_code=400, detail='Room number already exists')
            room.room_number = room_data.room_number
        
        if room_data.floor is not None:
            room.floor = room_data.floor
        old_status = room.status
        if room_data.status is not None:
            new_status = RoomStatus(room_data.status)
            room.status = new_status
            
            # If room status is changed to cleaning, create a housekeeping task if one doesn't exist
            if new_status == RoomStatus.cleaning and old_status != RoomStatus.cleaning:
                # Check if there's already a pending housekeeping task for this room
                existing_task = db.query(HousekeepingTask).filter(
                    and_(
                        HousekeepingTask.room_id == room.id,
                        HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
                    )
                ).first()
                
                if not existing_task:
                    # Create a new housekeeping task for the cleaning room
                    cleaning_task = HousekeepingTask(
                        room_id=room.id,
                        task_type=HousekeepingType.vacant,
                        status=HousekeepingStatus.pending,
                        scheduled_time=datetime.utcnow(),
                        created_by=current_user.id,
                        checklist_items=[
                            {'item': 'Deep clean bathroom', 'completed': False, 'notes': ''},
                            {'item': 'Change linens', 'completed': False, 'notes': ''},
                            {'item': 'Vacuum and mop', 'completed': False, 'notes': ''},
                            {'item': 'Dust surfaces', 'completed': False, 'notes': ''},
                            {'item': 'Check amenities', 'completed': False, 'notes': ''}
                        ],
                        notes='Room set to cleaning mode'
                    )
                    db.add(cleaning_task)
        if room_data.featured is not None:
            room.featured = room_data.featured
        if room_data.price is not None:
            room.price = room_data.price
        if room_data.description is not None:
            room.description = room_data.description
        if room_data.capacity is not None:
            room.capacity = room_data.capacity
        if room_data.room_size is not None:
            room.room_size = room_data.room_size
        if room_data.view is not None:
            room.view = room_data.view
        if room_data.amenities is not None:
            room.amenities = room_data.amenities or []
        
        # Commit transaction
        db.commit()
        db.refresh(room)
        
        base_url = get_base_url(request)
        room_dict = {
            'id': room.id,
            'room_type_id': room.room_type_id,
            'room_number': room.room_number,
            'floor': room.floor,
            'status': room.status.value if isinstance(room.status, RoomStatus) else room.status,
            'price': float(room.price) if room.price is not None and room.price > 0 else None,
            'featured': room.featured,
            'description': room.description,
            'capacity': room.capacity,
            'room_size': room.room_size,
            'view': room.view,
            'amenities': room.amenities if room.amenities else [],
            'created_at': room.created_at.isoformat() if room.created_at else None,
            'updated_at': room.updated_at.isoformat() if room.updated_at else None
        }
        try:
            room_dict['images'] = normalize_images(room.images, base_url)
        except:
            room_dict['images'] = []
        if room.room_type:
            room_dict['room_type'] = {
                'id': room.room_type.id,
                'name': room.room_type.name,
                'description': room.room_type.description,
                'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0,
                'capacity': room.room_type.capacity,
                'amenities': room.room_type.amenities if room.room_type.amenities else [],
                'images': []
            }
        return success_response(data={'room': room_dict}, message='Room updated successfully')
    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        logger.error(f'Database integrity error during room update: {str(e)}')
        raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating room: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while updating the room')

@router.delete('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_room(id: int, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    client_ip = request.client.host if request.client else None
    user_agent = request.headers.get('User-Agent')
    request_id = getattr(request.state, 'request_id', None)
    
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        
        # SECURITY: Check for active bookings before deletion
        from ...bookings.models.booking import Booking, BookingStatus
        active_bookings = db.query(Booking).filter(
            Booking.room_id == id,
            Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])
        ).count()
        
        if active_bookings > 0:
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete room with {active_bookings} active booking(s). Please cancel or complete bookings first.'
            )
        
        # Check for historical bookings - prevent deletion to maintain data integrity
        historical_bookings = db.query(Booking).filter(Booking.room_id == id).count()
        if historical_bookings > 0:
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete room with {historical_bookings} historical booking(s). Consider marking the room as inactive or under maintenance instead.'
            )
        
        # Capture room info before deletion for audit
        deleted_room_info = {
            'room_id': room.id,
            'room_number': room.room_number,
            'floor': room.floor,
            'room_type_id': room.room_type_id,
            'status': room.status.value if isinstance(room.status, RoomStatus) else str(room.status),
            'price': float(room.price) if room.price else None,
        }
        
        db.delete(room)
        db.commit()
        
        # SECURITY: Log room deletion for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='room_deleted',
                resource_type='room',
                user_id=current_user.id,
                resource_id=id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details=deleted_room_info,
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log room deletion audit: {e}')
        
        return {'status': 'success', 'message': 'Room deleted successfully'}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/bulk-delete', dependencies=[Depends(authorize_roles('admin'))])
async def bulk_delete_rooms(room_ids: BulkDeleteRoomsRequest, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    """Bulk delete rooms with validated input using Pydantic schema."""
    client_ip = request.client.host if request.client else None
    user_agent = request.headers.get('User-Agent')
    request_id = getattr(request.state, 'request_id', None)
    try:
        ids = room_ids.room_ids
        
        # Check if rooms exist
        rooms = db.query(Room).filter(Room.id.in_(ids)).all()
        found_ids = [room.id for room in rooms]
        not_found_ids = [id for id in ids if id not in found_ids]
        if not_found_ids:
            db.rollback()
            raise HTTPException(status_code=404, detail=f'Rooms with IDs {not_found_ids} not found')
        
        # SECURITY: Check for active bookings before deletion
        from ...bookings.models.booking import Booking, BookingStatus
        rooms_with_active_bookings = db.query(Booking.room_id).filter(
            Booking.room_id.in_(ids),
            Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])
        ).distinct().all()
        
        if rooms_with_active_bookings:
            room_ids_with_bookings = [r[0] for r in rooms_with_active_bookings]
            db.rollback()
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete rooms with IDs {room_ids_with_bookings} - they have active bookings. Please cancel or complete bookings first.'
            )
        
        # Check for historical bookings - prevent deletion to maintain data integrity
        rooms_with_historical_bookings = db.query(Booking.room_id).filter(
            Booking.room_id.in_(ids)
        ).distinct().all()
        
        if rooms_with_historical_bookings:
            room_ids_with_bookings = [r[0] for r in rooms_with_historical_bookings]
            db.rollback()
            raise HTTPException(
                status_code=400,
                detail=f'Cannot delete rooms with IDs {room_ids_with_bookings} - they have historical bookings. Consider marking them as inactive or under maintenance instead.'
            )
        
        # Capture room info before deletion for audit
        deleted_rooms_info = []
        for room in rooms:
            deleted_rooms_info.append({
                'room_id': room.id,
                'room_number': room.room_number,
                'floor': room.floor,
                'room_type_id': room.room_type_id,
                'status': room.status.value if isinstance(room.status, RoomStatus) else str(room.status),
            })
        
        # Delete rooms
        deleted_count = db.query(Room).filter(Room.id.in_(ids)).delete(synchronize_session=False)
        
        # Commit transaction
        db.commit()
        
        # SECURITY: Log bulk room deletion for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='rooms_bulk_deleted',
                resource_type='room',
                user_id=current_user.id,
                resource_id=None,  # Multiple resources
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'deleted_count': deleted_count,
                    'deleted_room_ids': ids,
                    'deleted_rooms': deleted_rooms_info
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log bulk room deletion audit: {e}')
        
        return success_response(
            data={'deleted_count': deleted_count, 'deleted_ids': ids},
            message=f'Successfully deleted {deleted_count} room(s)'
        )
    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        logger.error(f'Database integrity error during bulk room deletion: {str(e)}')
        raise HTTPException(status_code=409, detail='Cannot delete rooms due to existing relationships (bookings, etc.)')
    except Exception as e:
        db.rollback()
        logger.error(f'Error bulk deleting rooms: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while deleting rooms')

@router.post('/{id}/images', dependencies=[Depends(authorize_roles('admin', 'staff'))])
async def upload_room_images(id: int, images: List[UploadFile]=File(...), current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        # Calculate upload directory to match main.py (Backend/uploads/rooms)
        # From Backend/src/rooms/routes/room_routes.py -> Backend/
        upload_dir = Path(__file__).parent.parent.parent.parent / 'uploads' / 'rooms'
        upload_dir.mkdir(parents=True, exist_ok=True)
        
        # Import validation and optimization utilities
        from ...shared.config.settings import settings
        from ...shared.utils.file_validation import validate_uploaded_image
        from ...shared.utils.image_optimization import optimize_image_async, ImageType
        
        image_urls = []
        for image in images:
            if not image.content_type or not image.content_type.startswith('image/'):
                continue
            if not image.filename:
                continue
            
            try:
                # Validate the image
                content = await validate_uploaded_image(image, settings.MAX_UPLOAD_SIZE)
                
                # Optimize image before saving
                optimized_content, optimized_ext = await optimize_image_async(content, ImageType.ROOM)
                
                import uuid
                ext = Path(image.filename).suffix or '.jpg'
                # Update extension if format changed
                if optimized_ext:
                    ext = optimized_ext
                filename = f'room-{uuid.uuid4()}{ext}'
                file_path = upload_dir / filename
                
                async with aiofiles.open(file_path, 'wb') as f:
                    await f.write(optimized_content)
                
                # Verify file was saved
                if not file_path.exists():
                    logger.error(f'File was not saved: {file_path}')
                    continue
                
                logger.info(f'Successfully uploaded and optimized image: {filename} ({len(optimized_content)} bytes)')
                image_urls.append(f'/uploads/rooms/{filename}')
            except HTTPException:
                # Skip invalid images and continue with others
                logger.warning(f'Skipping invalid image: {image.filename}')
                continue
            except Exception as e:
                logger.error(f'Error processing image {image.filename}: {str(e)}', exc_info=True)
                continue
        
        # Handle existing_images - it might be a list, a JSON string, or None
        existing_images = room.images or []
        if isinstance(existing_images, str):
            # If it's a string, try to parse it as JSON
            import json
            try:
                existing_images = json.loads(existing_images)
            except (json.JSONDecodeError, TypeError):
                # If parsing fails, treat as empty list
                existing_images = []
        # Ensure it's a list
        if not isinstance(existing_images, list):
            existing_images = []
        
        updated_images = existing_images + image_urls
        room.images = updated_images
        db.commit()
        return {'success': True, 'status': 'success', 'message': 'Images uploaded successfully', 'data': {'images': updated_images}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error uploading room images: {str(e)}', exc_info=True, extra={'room_id': id})
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/{id}/images', dependencies=[Depends(authorize_roles('admin', 'staff'))])
async def delete_room_images(id: int, image_url: str=Query(..., description='Image URL or path to delete'), current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        
        # Normalize the input URL to extract the path part
        # The frontend may send a full URL like "http://localhost:8000/uploads/rooms/image.webp"
        # but the database stores relative paths like "/uploads/rooms/image.webp"
        is_external_url = image_url.startswith('http://') or image_url.startswith('https://')
        normalized_path = image_url
        filename = None
        
        if is_external_url:
            # Extract the path from the full URL
            try:
                from urllib.parse import urlparse
                parsed_url = urlparse(image_url)
                normalized_path = parsed_url.path  # Extract path like "/uploads/rooms/image.webp"
                
                # Check if it's a local uploads path (not an external image service)
                if normalized_path.startswith('/uploads/'):
                    is_external_url = False  # It's a local file with full URL
                    filename = Path(normalized_path).name
                else:
                    # Truly external URL (like Unsplash)
                    normalized_path = image_url
            except Exception as e:
                logger.warning(f'Error parsing URL {image_url}: {str(e)}')
                # Fallback: try to extract path manually
                if '/uploads/' in image_url:
                    match = image_url.split('/uploads/', 1)
                    if len(match) == 2:
                        normalized_path = f'/uploads/{match[1]}'
                        is_external_url = False
                        filename = Path(normalized_path).name
        else:
            # Local file path - normalize it
            if not normalized_path.startswith('/'):
                normalized_path = f'/{normalized_path}'
            filename = Path(normalized_path).name
        
        logger.info(f'Deleting image: original={image_url}, normalized_path={normalized_path}, filename={filename}, is_external={is_external_url}')
        
        # Handle existing_images - it might be a list, a JSON string, or None
        existing_images = room.images or []
        if isinstance(existing_images, str):
            # If it's a string, try to parse it as JSON
            import json
            try:
                existing_images = json.loads(existing_images)
            except (json.JSONDecodeError, TypeError):
                # If parsing fails, treat as empty list
                existing_images = []
        # Ensure it's a list
        if not isinstance(existing_images, list):
            existing_images = []
        
        updated_images = []
        for img in existing_images:
            # Convert stored image to string for comparison
            img_str = str(img).strip()
            if not img_str:
                continue
            
            # Normalize stored image path
            stored_path = img_str
            stored_is_external = stored_path.startswith('http://') or stored_path.startswith('https://')
            
            if stored_is_external and is_external_url:
                # Both are external URLs - match exactly
                if img_str != image_url:
                    updated_images.append(img)
            elif stored_is_external and not is_external_url:
                # Stored is external, deleting is local - keep it
                updated_images.append(img)
            elif not stored_is_external and is_external_url:
                # Stored is local, deleting is external - keep it
                updated_images.append(img)
            else:
                # Both are local paths - normalize both for comparison
                stored_normalized = stored_path if stored_path.startswith('/') else f'/{stored_path}'
                stored_filename = Path(stored_normalized).name if '/' in stored_normalized else stored_path
                
                # Match by full path or by filename
                path_matches = (stored_normalized == normalized_path or stored_path == normalized_path)
                filename_matches = (filename and stored_filename == filename)
                
                if not (path_matches or filename_matches):
                    # Keep images that don't match
                    updated_images.append(img)
        
        logger.info(f'Images before: {len(existing_images)}, after: {len(updated_images)}')
        
        # Only try to delete the physical file if it's a local file (filename exists)
        if filename:
            file_path = Path(__file__).parent.parent.parent.parent / 'uploads' / 'rooms' / filename
            if file_path.exists():
                try:
                    file_path.unlink()
                    logger.info(f'Deleted file: {file_path}')
                except Exception as e:
                    logger.warning(f'Could not delete file {file_path}: {str(e)}')
            else:
                logger.warning(f'File does not exist: {file_path}')
        
        room.images = updated_images
        db.commit()
        return {'status': 'success', 'message': 'Image deleted successfully', 'data': {'images': updated_images}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error deleting room image: {str(e)}', exc_info=True, extra={'room_id': id, 'image_url': image_url})
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{id}/booked-dates')
async def get_room_booked_dates(id: int, db: Session=Depends(get_db)):
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        bookings = db.query(Booking).filter(and_(Booking.room_id == id, Booking.status != BookingStatus.cancelled)).all()
        booked_dates = []
        for booking in bookings:
            check_in = booking.check_in_date
            check_out = booking.check_out_date
            current_date = check_in.date()
            end_date = check_out.date()
            while current_date < end_date:
                booked_dates.append(current_date.isoformat())
                from datetime import timedelta
                current_date += timedelta(days=1)
        booked_dates = sorted(list(set(booked_dates)))
        return {'status': 'success', 'data': {'room_id': id, 'booked_dates': booked_dates}}
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching booked dates: {str(e)}', exc_info=True, extra={'room_id': id})
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{id}/reviews')
async def get_room_reviews_route(id: int, db: Session=Depends(get_db)):
    from ...reviews.models.review import Review, ReviewStatus
    try:
        room = db.query(Room).filter(Room.id == id).first()
        if not room:
            raise HTTPException(status_code=404, detail='Room not found')
        reviews = db.query(Review).filter(Review.room_id == id, Review.status == ReviewStatus.approved).order_by(Review.created_at.desc()).all()
        result = []
        for review in reviews:
            review_dict = {'id': review.id, 'user_id': review.user_id, 'room_id': review.room_id, 'rating': review.rating, 'comment': review.comment, 'status': review.status.value if isinstance(review.status, ReviewStatus) else review.status, 'created_at': review.created_at.isoformat() if review.created_at else None}
            if review.user:
                review_dict['user'] = {'id': review.user.id, 'full_name': review.user.full_name, 'email': review.user.email}
            result.append(review_dict)
        return {'status': 'success', 'data': {'reviews': result}}
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching room reviews: {str(e)}', exc_info=True, extra={'room_id': id})
        raise HTTPException(status_code=500, detail=str(e))