from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import and_, or_, desc
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 ...auth.models.role import Role
from ..models.guest_request import GuestRequest, RequestType, RequestStatus, RequestPriority
from ...bookings.models.booking import Booking, BookingStatus
from ...rooms.models.room import Room
from pydantic import BaseModel
from ...shared.utils.sanitization import sanitize_text

logger = get_logger(__name__)
router = APIRouter(prefix='/guest-requests', tags=['guest-requests'])


# ==================== Pydantic Schemas ====================

class GuestRequestCreate(BaseModel):
    booking_id: int
    room_id: int
    request_type: str
    title: str
    description: Optional[str] = None
    priority: str = 'normal'
    guest_notes: Optional[str] = None

class GuestRequestUpdate(BaseModel):
    status: Optional[str] = None
    assigned_to: Optional[int] = None
    staff_notes: Optional[str] = None


# ==================== Guest Requests ====================

@router.get('/')
async def get_guest_requests(
    status: Optional[str] = Query(None),
    request_type: Optional[str] = Query(None),
    room_id: Optional[int] = Query(None),
    assigned_to: Optional[int] = Query(None),
    priority: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(20, ge=1, le=100),
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get guest requests with filtering"""
    try:
        query = db.query(GuestRequest).options(
            joinedload(GuestRequest.booking),
            joinedload(GuestRequest.room),
            joinedload(GuestRequest.guest)
        )
        
        # Check user role to determine access level
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        role_name = role.name if role else 'customer'
        
        # Customers can only see their own requests
        if role_name == 'customer':
            query = query.filter(GuestRequest.user_id == current_user.id)
        # Housekeeping can only see requests assigned to them or unassigned
        elif role_name == 'housekeeping':
            query = query.filter(
                or_(
                    GuestRequest.assigned_to == current_user.id,
                    GuestRequest.assigned_to.is_(None)
                )
            )
        # Admin and staff can see all requests (no additional filter needed)
        
        if status:
            query = query.filter(GuestRequest.status == status)
        
        if request_type:
            query = query.filter(GuestRequest.request_type == request_type)
        
        if room_id:
            query = query.filter(GuestRequest.room_id == room_id)
        
        if assigned_to:
            query = query.filter(GuestRequest.assigned_to == assigned_to)
        
        if priority:
            query = query.filter(GuestRequest.priority == priority)
        
        # Only show requests for checked-in bookings (guests must be in the room)
        query = query.join(Booking).filter(
            Booking.status == BookingStatus.checked_in
        )
        
        total = query.count()
        requests = query.order_by(
            desc(GuestRequest.priority == RequestPriority.urgent),
            desc(GuestRequest.priority == RequestPriority.high),
            desc(GuestRequest.requested_at)
        ).offset((page - 1) * limit).limit(limit).all()
        
        return {
            'status': 'success',
            'data': {
                'requests': [
                    {
                        'id': req.id,
                        'booking_id': req.booking_id,
                        'room_id': req.room_id,
                        'room_number': req.room.room_number if req.room else None,
                        'user_id': req.user_id,
                        'guest_name': req.guest.full_name if req.guest else None,
                        'request_type': req.request_type.value,
                        'status': req.status.value,
                        'priority': req.priority.value,
                        'title': req.title,
                        'description': req.description,
                        'guest_notes': req.guest_notes,
                        'staff_notes': req.staff_notes,
                        'assigned_to': req.assigned_to,
                        'assigned_staff_name': req.assigned_staff.full_name if req.assigned_staff else None,
                        'fulfilled_by': req.fulfilled_by,
                        'requested_at': req.requested_at.isoformat() if req.requested_at else None,
                        'started_at': req.started_at.isoformat() if req.started_at else None,
                        'fulfilled_at': req.fulfilled_at.isoformat() if req.fulfilled_at else None,
                        'response_time_minutes': req.response_time_minutes,
                        'fulfillment_time_minutes': req.fulfillment_time_minutes,
                    }
                    for req in requests
                ],
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'total_pages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        logger.error(f'Error fetching guest requests: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch guest requests')

@router.post('/')
async def create_guest_request(
    request_data: GuestRequestCreate,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Create a new guest request"""
    try:
        # Verify booking belongs to user
        booking = db.query(Booking).filter(Booking.id == request_data.booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        
        if booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='You can only create requests for your own bookings')
        
        # Guests can only create requests when they are checked in (in the room)
        if booking.status != BookingStatus.checked_in:
            raise HTTPException(
                status_code=400, 
                detail='You can only create requests when you are checked in. Please check in first or contact reception.'
            )
        
        # Verify room matches booking
        if booking.room_id != request_data.room_id:
            raise HTTPException(status_code=400, detail='Room ID does not match booking')
        
        # Sanitize user input to prevent XSS
        sanitized_title = sanitize_text(request_data.title)
        sanitized_description = sanitize_text(request_data.description) if request_data.description else None
        sanitized_guest_notes = sanitize_text(request_data.guest_notes) if request_data.guest_notes else None
        
        guest_request = GuestRequest(
            booking_id=request_data.booking_id,
            room_id=request_data.room_id,
            user_id=current_user.id,
            request_type=RequestType(request_data.request_type),
            priority=RequestPriority(request_data.priority),
            title=sanitized_title,
            description=sanitized_description,
            guest_notes=sanitized_guest_notes,
        )
        
        db.add(guest_request)
        db.flush()  # Flush to get the ID for task creation
        
        # Auto-create housekeeping task for request types that require housekeeping
        request_type = RequestType(request_data.request_type)
        task_types_requiring_housekeeping = {
            RequestType.extra_towels,
            RequestType.extra_pillows,
            RequestType.room_cleaning,
            RequestType.turndown_service,
            RequestType.amenities,
        }
        
        if request_type in task_types_requiring_housekeeping:
            try:
                from ...hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType
                from ...rooms.models.room import Room
                
                # Determine housekeeping task type based on request type
                task_type_map = {
                    RequestType.room_cleaning: HousekeepingType.stayover,
                    RequestType.turndown_service: HousekeepingType.turndown,
                    RequestType.extra_towels: HousekeepingType.stayover,
                    RequestType.extra_pillows: HousekeepingType.stayover,
                    RequestType.amenities: HousekeepingType.stayover,
                }
                
                housekeeping_task_type = task_type_map.get(request_type, HousekeepingType.stayover)
                
                # Create default checklist based on request type
                checklist_items = []
                if request_type == RequestType.room_cleaning:
                    checklist_items = [
                        {'item': 'Room cleaned', 'completed': False, 'notes': ''},
                        {'item': 'Bathroom cleaned', 'completed': False, 'notes': ''},
                        {'item': 'Trash emptied', 'completed': False, 'notes': ''},
                        {'item': 'Beds made', 'completed': False, 'notes': ''},
                    ]
                elif request_type == RequestType.turndown_service:
                    checklist_items = [
                        {'item': 'Beds turned down', 'completed': False, 'notes': ''},
                        {'item': 'Curtains closed', 'completed': False, 'notes': ''},
                        {'item': 'Lights dimmed', 'completed': False, 'notes': ''},
                        {'item': 'Amenities refreshed', 'completed': False, 'notes': ''},
                    ]
                elif request_type in [RequestType.extra_towels, RequestType.extra_pillows, RequestType.amenities]:
                    item_name = 'Extra towels' if request_type == RequestType.extra_towels else \
                               'Extra pillows' if request_type == RequestType.extra_pillows else 'Amenities'
                    checklist_items = [
                        {'item': f'{item_name} delivered', 'completed': False, 'notes': request_data.description or ''},
                    ]
                
                # Check if a similar task already exists for this room
                existing_task = db.query(HousekeepingTask).filter(
                    and_(
                        HousekeepingTask.room_id == request_data.room_id,
                        HousekeepingTask.task_type == housekeeping_task_type,
                        HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress]),
                        HousekeepingTask.booking_id == request_data.booking_id
                    )
                ).first()
                
                if not existing_task:
                    # Create housekeeping task
                    housekeeping_task = HousekeepingTask(
                        room_id=request_data.room_id,
                        booking_id=request_data.booking_id,
                        task_type=housekeeping_task_type,
                        status=HousekeepingStatus.pending,
                        scheduled_time=datetime.utcnow(),  # Schedule immediately for guest requests
                        created_by=None,  # Created by system/guest request
                        checklist_items=checklist_items,
                        notes=f'Auto-created from guest request: {request_data.title}. Guest notes: {request_data.guest_notes or "None"}',
                        estimated_duration_minutes=15 if request_type in [RequestType.extra_towels, RequestType.extra_pillows, RequestType.amenities] else 30
                    )
                    
                    db.add(housekeeping_task)
                    db.flush()
                    
                    # Link guest request to housekeeping task via notes
                    guest_request.staff_notes = f'Auto-created housekeeping task #{housekeeping_task.id}'
                    
                    # Send notification to housekeeping users
                    try:
                        from ...notifications.routes.notification_routes import notification_manager
                        room = db.query(Room).filter(Room.id == request_data.room_id).first()
                        
                        task_data_notification = {
                            'id': housekeeping_task.id,
                            'room_id': housekeeping_task.room_id,
                            'room_number': room.room_number if room else None,
                            'task_type': housekeeping_task.task_type.value,
                            'status': housekeeping_task.status.value,
                            'scheduled_time': housekeeping_task.scheduled_time.isoformat() if housekeeping_task.scheduled_time else None,
                            'guest_request_id': guest_request.id,
                            'guest_request_title': request_data.title,
                            'created_at': housekeeping_task.created_at.isoformat() if housekeeping_task.created_at else None
                        }
                        notification_data = {
                            'type': 'housekeeping_task_available',
                            'data': task_data_notification
                        }
                        
                        # Send notification to all housekeeping users
                        await notification_manager.send_to_role('housekeeping', notification_data)
                    except Exception as e:
                        logger.error(f'Error sending housekeeping notification for guest request: {str(e)}', exc_info=True)
                    
                    logger.info(f'Auto-created housekeeping task {housekeeping_task.id} for guest request {guest_request.id} (type: {request_type.value})')
            except Exception as e:
                # Don't fail guest request creation if task creation fails
                logger.error(f'Error auto-creating housekeeping task for guest request: {str(e)}', exc_info=True)
        
        db.commit()
        db.refresh(guest_request)
        
        return {
            'status': 'success',
            'message': 'Guest request created successfully',
            'data': {'id': guest_request.id}
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    except Exception as e:
        logger.error(f'Error creating guest request: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to create guest request')

@router.put('/{request_id}')
async def update_guest_request(
    request_id: int,
    request_data: GuestRequestUpdate,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Update a guest request (assign, update status, add notes)"""
    try:
        guest_request = db.query(GuestRequest).filter(GuestRequest.id == request_id).first()
        if not guest_request:
            raise HTTPException(status_code=404, detail='Guest request not found')
        
        # Check permissions
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        is_housekeeping = role and role.name == 'housekeeping'
        
        if is_housekeeping:
            # Housekeeping can only update requests assigned to them or unassigned
            if guest_request.assigned_to and guest_request.assigned_to != current_user.id:
                raise HTTPException(status_code=403, detail='You can only update requests assigned to you')
        
        update_data = request_data.dict(exclude_unset=True)
        
        # Handle status changes
        if 'status' in update_data:
            new_status = RequestStatus(update_data['status'])
            old_status = guest_request.status
            
            # Track timestamps
            if new_status == RequestStatus.in_progress and old_status == RequestStatus.pending:
                guest_request.started_at = datetime.utcnow()
                if guest_request.requested_at:
                    delta = datetime.utcnow() - guest_request.requested_at
                    guest_request.response_time_minutes = int(delta.total_seconds() / 60)
                # Auto-assign if not assigned
                if not guest_request.assigned_to:
                    guest_request.assigned_to = current_user.id
            
            elif new_status == RequestStatus.fulfilled and old_status != RequestStatus.fulfilled:
                guest_request.fulfilled_at = datetime.utcnow()
                guest_request.fulfilled_by = current_user.id
                if guest_request.started_at:
                    delta = datetime.utcnow() - guest_request.started_at
                    guest_request.fulfillment_time_minutes = int(delta.total_seconds() / 60)
            
            update_data['status'] = new_status
        
        if 'assigned_to' in update_data:
            update_data['assigned_to'] = update_data['assigned_to'] if update_data['assigned_to'] else None
        
        for key, value in update_data.items():
            setattr(guest_request, key, value)
        
        db.commit()
        db.refresh(guest_request)
        
        return {
            'status': 'success',
            'message': 'Guest request updated successfully'
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    except Exception as e:
        logger.error(f'Error updating guest request: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to update guest request')

@router.get('/{request_id}')
async def get_guest_request(
    request_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get a single guest request"""
    try:
        request = db.query(GuestRequest).options(
            joinedload(GuestRequest.booking),
            joinedload(GuestRequest.room),
            joinedload(GuestRequest.guest),
            joinedload(GuestRequest.assigned_staff),
            joinedload(GuestRequest.fulfilled_staff)
        ).filter(GuestRequest.id == request_id).first()
        
        if not request:
            raise HTTPException(status_code=404, detail='Guest request not found')
        
        # Check permissions
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        role_name = role.name if role else 'customer'
        
        # Customers can only view their own requests
        if role_name == 'customer':
            if request.user_id != current_user.id:
                raise HTTPException(status_code=403, detail='You can only view your own requests')
        # Housekeeping can only view requests assigned to them or unassigned
        elif role_name == 'housekeeping':
            if request.assigned_to and request.assigned_to != current_user.id:
                raise HTTPException(status_code=403, detail='You can only view requests assigned to you')
        # Admin and staff can view all requests (no additional check needed)
        
        return {
            'status': 'success',
            'data': {
                'id': request.id,
                'booking_id': request.booking_id,
                'room_id': request.room_id,
                'room_number': request.room.room_number if request.room else None,
                'user_id': request.user_id,
                'guest_name': request.guest.full_name if request.guest else None,
                'guest_email': request.guest.email if request.guest else None,
                'request_type': request.request_type.value,
                'status': request.status.value,
                'priority': request.priority.value,
                'title': request.title,
                'description': request.description,
                'guest_notes': request.guest_notes,
                'staff_notes': request.staff_notes,
                'assigned_to': request.assigned_to,
                'assigned_staff_name': request.assigned_staff.full_name if request.assigned_staff else None,
                'fulfilled_by': request.fulfilled_by,
                'fulfilled_staff_name': request.fulfilled_staff.full_name if request.fulfilled_staff else None,
                'requested_at': request.requested_at.isoformat() if request.requested_at else None,
                'started_at': request.started_at.isoformat() if request.started_at else None,
                'fulfilled_at': request.fulfilled_at.isoformat() if request.fulfilled_at else None,
                'response_time_minutes': request.response_time_minutes,
                'fulfillment_time_minutes': request.fulfillment_time_minutes,
            }
        }
    except Exception as e:
        logger.error(f'Error fetching guest request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch guest request')

@router.post('/{request_id}/assign')
async def assign_request(
    request_id: int,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Assign a request to the current user (housekeeping)"""
    try:
        request = db.query(GuestRequest).filter(GuestRequest.id == request_id).first()
        if not request:
            raise HTTPException(status_code=404, detail='Guest request not found')
        
        if request.status == RequestStatus.fulfilled:
            raise HTTPException(status_code=400, detail='Cannot assign a fulfilled request')
        
        request.assigned_to = current_user.id
        if request.status == RequestStatus.pending:
            request.status = RequestStatus.in_progress
            request.started_at = datetime.utcnow()
            if request.requested_at:
                delta = datetime.utcnow() - request.requested_at
                request.response_time_minutes = int(delta.total_seconds() / 60)
        
        db.commit()
        
        return {
            'status': 'success',
            'message': 'Request assigned successfully'
        }
    except Exception as e:
        logger.error(f'Error assigning request: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to assign request')

@router.post('/{request_id}/fulfill')
async def fulfill_request(
    request_id: int,
    staff_notes: Optional[str] = None,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Mark a request as fulfilled"""
    try:
        request = db.query(GuestRequest).filter(GuestRequest.id == request_id).first()
        if not request:
            raise HTTPException(status_code=404, detail='Guest request not found')
        
        # Check permissions
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        is_housekeeping = role and role.name == 'housekeeping'
        
        if is_housekeeping and request.assigned_to != current_user.id:
            raise HTTPException(status_code=403, detail='You can only fulfill requests assigned to you')
        
        if request.status == RequestStatus.fulfilled:
            raise HTTPException(status_code=400, detail='Request is already fulfilled')
        
        request.status = RequestStatus.fulfilled
        request.fulfilled_by = current_user.id
        request.fulfilled_at = datetime.utcnow()
        
        if staff_notes:
            sanitized_notes = sanitize_text(staff_notes)
            request.staff_notes = (request.staff_notes or '') + f'\n{sanitized_notes}' if request.staff_notes else sanitized_notes
        
        if request.started_at:
            delta = datetime.utcnow() - request.started_at
            request.fulfillment_time_minutes = int(delta.total_seconds() / 60)
        elif request.requested_at:
            # If never started, calculate from request time
            delta = datetime.utcnow() - request.requested_at
            request.fulfillment_time_minutes = int(delta.total_seconds() / 60)
        
        db.commit()
        
        return {
            'status': 'success',
            'message': 'Request marked as fulfilled'
        }
    except Exception as e:
        logger.error(f'Error fulfilling request: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to fulfill request')

