from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Request, Query
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func
from typing import List, Optional
from datetime import datetime
from ..config.database import get_db
from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.room import Room, RoomStatus
from ..models.room_type import RoomType
from ..models.review import Review, ReviewStatus
from ..models.booking import Booking, BookingStatus
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
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=100), 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:
        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:
        raise HTTPException(status_code=500, detail=str(e))

@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}}
            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)))
        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:
        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: dict, request: Request, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        room_type = db.query(RoomType).filter(RoomType.id == room_data.get('room_type_id')).first()
        if not room_type:
            raise HTTPException(status_code=404, detail='Room type not found')
        existing = db.query(Room).filter(Room.room_number == room_data.get('room_number')).first()
        if existing:
            raise HTTPException(status_code=400, detail='Room number already exists')
        amenities_value = room_data.get('amenities', [])
        if amenities_value is None:
            amenities_value = []
        elif not isinstance(amenities_value, list):
            amenities_value = []
        room = Room(room_type_id=room_data.get('room_type_id'), room_number=room_data.get('room_number'), floor=room_data.get('floor'), status=RoomStatus(room_data.get('status', 'available')), featured=room_data.get('featured', False), price=room_data.get('price', room_type.base_price), description=room_data.get('description'), capacity=room_data.get('capacity'), room_size=room_data.get('room_size'), view=room_data.get('view'), amenities=amenities_value)
        db.add(room)
        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 {'status': 'success', 'message': 'Room created successfully', 'data': {'room': room_dict}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def update_room(id: int, room_data: dict, request: Request, current_user: User=Depends(authorize_roles('admin')), 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')
        if room_data.get('room_type_id'):
            room_type = db.query(RoomType).filter(RoomType.id == room_data['room_type_id']).first()
            if not room_type:
                raise HTTPException(status_code=404, detail='Room type not found')
        if 'room_type_id' in room_data:
            room.room_type_id = room_data['room_type_id']
        if 'room_number' in room_data:
            room.room_number = room_data['room_number']
        if 'floor' in room_data:
            room.floor = room_data['floor']
        if 'status' in room_data:
            room.status = RoomStatus(room_data['status'])
        if 'featured' in room_data:
            room.featured = room_data['featured']
        if 'price' in room_data:
            room.price = room_data['price']
        if 'description' in room_data:
            room.description = room_data['description']
        if 'capacity' in room_data:
            room.capacity = room_data['capacity']
        if 'room_size' in room_data:
            room.room_size = room_data['room_size']
        if 'view' in room_data:
            room.view = room_data['view']
        if 'amenities' in room_data:
            amenities_value = room_data['amenities']
            if amenities_value is None:
                room.amenities = []
            elif isinstance(amenities_value, list):
                room.amenities = amenities_value
            else:
                room.amenities = []
        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 {'status': 'success', 'message': 'Room updated successfully', 'data': {'room': room_dict}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_room(id: int, current_user: User=Depends(authorize_roles('admin')), 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')
        db.delete(room)
        db.commit()
        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: dict, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        ids = room_ids.get('ids', [])
        if not ids or not isinstance(ids, list):
            raise HTTPException(status_code=400, detail='Invalid room IDs provided')
        if len(ids) == 0:
            raise HTTPException(status_code=400, detail='No room IDs provided')
        try:
            ids = [int(id) for id in ids]
        except (ValueError, TypeError):
            raise HTTPException(status_code=400, detail='All room IDs must be integers')
        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:
            raise HTTPException(status_code=404, detail=f'Rooms with IDs {not_found_ids} not found')
        deleted_count = db.query(Room).filter(Room.id.in_(ids)).delete(synchronize_session=False)
        db.commit()
        return {'status': 'success', 'message': f'Successfully deleted {deleted_count} room(s)', 'data': {'deleted_count': deleted_count, 'deleted_ids': ids}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@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')
        upload_dir = Path(__file__).parent.parent.parent / 'uploads' / 'rooms'
        upload_dir.mkdir(parents=True, exist_ok=True)
        image_urls = []
        for image in images:
            if not image.content_type or not image.content_type.startswith('image/'):
                continue
            if not image.filename:
                continue
            import uuid
            ext = Path(image.filename).suffix or '.jpg'
            filename = f'room-{uuid.uuid4()}{ext}'
            file_path = upload_dir / filename
            async with aiofiles.open(file_path, 'wb') as f:
                content = await image.read()
                if not content:
                    continue
                await f.write(content)
            image_urls.append(f'/uploads/rooms/{filename}')
        existing_images = room.images or []
        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:
        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')
        normalized_url = image_url
        if image_url.startswith('http://') or image_url.startswith('https://'):
            from urllib.parse import urlparse
            parsed = urlparse(image_url)
            normalized_url = parsed.path
        if not normalized_url.startswith('/'):
            normalized_url = f'/{normalized_url}'
        filename = Path(normalized_url).name
        existing_images = room.images or []
        updated_images = []
        for img in existing_images:
            stored_path = img if img.startswith('/') else f'/{img}'
            stored_filename = Path(stored_path).name
            if img != normalized_url and stored_path != normalized_url and (stored_filename != filename):
                updated_images.append(img)
        file_path = Path(__file__).parent.parent.parent / 'uploads' / 'rooms' / filename
        if file_path.exists():
            file_path.unlink()
        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()
        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:
        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 ..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:
        raise HTTPException(status_code=500, detail=str(e))