from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
from ...shared.utils.response_helpers import success_response, error_response
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import func, and_
from typing import Optional
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.review import Review, ReviewStatus
from ...rooms.models.room import Room
from ...bookings.models.booking import Booking, BookingStatus
from ..schemas.review import CreateReviewRequest
from ...analytics.services.audit_service import audit_service

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

@router.get('/room/{room_id}')
async def get_room_reviews(
    room_id: int,
    page: int = Query(1, ge=1),
    limit: int = Query(10, ge=1, le=100),
    db: Session = Depends(get_db)
):
    try:
        # Calculate average rating and total reviews for approved reviews
        stats = db.query(
            func.avg(Review.rating).label('average_rating'),
            func.count(Review.id).label('total_reviews')
        ).filter(
            Review.room_id == room_id,
            Review.status == ReviewStatus.approved
        ).first()
        
        average_rating = round(float(stats.average_rating or 0), 1) if stats and stats.average_rating else 0
        total_reviews = stats.total_reviews or 0 if stats else 0
        
        query = db.query(Review).options(
            joinedload(Review.user)
        ).filter(Review.room_id == room_id, Review.status == ReviewStatus.approved)
        total = query.count()
        offset = (page - 1) * limit
        reviews = query.order_by(Review.created_at.desc()).offset(offset).limit(limit).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, 
                    'name': review.user.full_name,  # Add name alias for compatibility
                    'email': review.user.email
                }
            result.append(review_dict)
        return {
            'status': 'success',
            'data': {
                'reviews': result,
                'average_rating': average_rating,
                'total_reviews': total_reviews,
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'totalPages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching room reviews: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/', dependencies=[Depends(authorize_roles('admin'))])
async def get_all_reviews(status_filter: Optional[str]=Query(None, alias='status'), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=100), current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        query = db.query(Review).options(
            joinedload(Review.user),
            joinedload(Review.room).joinedload(Room.room_type)
        )
        if status_filter:
            try:
                query = query.filter(Review.status == ReviewStatus(status_filter))
            except ValueError:
                pass
        total = query.count()
        offset = (page - 1) * limit
        reviews = query.order_by(Review.created_at.desc()).offset(offset).limit(limit).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, 
                    'name': review.user.full_name,  # Add name alias for frontend compatibility
                    'email': review.user.email, 
                    'phone': review.user.phone
                }
            if review.room:
                room_dict = {
                    'id': review.room.id, 
                    'room_number': review.room.room_number
                }
                # Include room type if available
                if review.room.room_type:
                    room_dict['room_type'] = {
                        'id': review.room.room_type.id,
                        'name': review.room.room_type.name
                    }
                review_dict['room'] = room_dict
            result.append(review_dict)
        return {'status': 'success', 'data': {'reviews': result, 'pagination': {'total': total, 'page': page, 'limit': limit, 'totalPages': (total + limit - 1) // limit}}}
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching all reviews: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/')
async def create_review(review_data: CreateReviewRequest, request: Request, current_user: User=Depends(get_current_user), 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_id = review_data.room_id
        rating = review_data.rating
        # Ensure comment is a string (not None) since the model requires it
        comment = review_data.comment if review_data.comment else ''
        
        room = db.query(Room).filter(Room.id == room_id).first()
        if not room:
            raise HTTPException(
                status_code=404, 
                detail=error_response(message='Room not found')
            )
        
        existing = db.query(Review).filter(Review.user_id == current_user.id, Review.room_id == room_id).first()
        if existing:
            raise HTTPException(
                status_code=400, 
                detail=error_response(message='You have already reviewed this room')
            )
        
        # BUSINESS RULE: Verify user has actually stayed in this room
        # Users can only review rooms they've booked and checked out from
        from ...shared.utils.role_helpers import is_admin, is_staff
        if not (is_admin(current_user, db) or is_staff(current_user, db)):
            # Check if user has a checked-out booking for this room
            past_booking = db.query(Booking).filter(
                and_(
                    Booking.user_id == current_user.id,
                    Booking.room_id == room_id,
                    Booking.status == BookingStatus.checked_out
                )
            ).first()
            
            if not past_booking:
                raise HTTPException(
                    status_code=403,
                    detail=error_response(message='You can only review rooms you have stayed in. Please complete a booking and check out before leaving a review.')
                )
        
        review = Review(
            user_id=current_user.id, 
            room_id=room_id, 
            rating=rating, 
            comment=comment or '',  # Ensure comment is a string
            status=ReviewStatus.pending
        )
        db.add(review)
        db.commit()
        db.refresh(review)
        
        # SECURITY: Log review creation for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='review_created',
                resource_type='review',
                user_id=current_user.id,
                resource_id=review.id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'room_id': review.room_id,
                    'rating': review.rating,
                    'status': 'pending',
                    'comment_length': len(review.comment) if review.comment else 0
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log review creation audit: {e}')
        
        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
        }
        
        return success_response(
            data={'review': review_dict},
            message='Review submitted successfully and is pending approval'
        )
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating review: {str(e)}', exc_info=True)
        raise HTTPException(
            status_code=500, 
            detail=error_response(message='An error occurred while creating the review')
        )

@router.patch('/{id}/approve', dependencies=[Depends(authorize_roles('admin'))])
async def approve_review(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:
        review = db.query(Review).options(joinedload(Review.room)).filter(Review.id == id).first()
        if not review:
            raise HTTPException(
                status_code=404, 
                detail=error_response(message='Review not found')
            )
        old_status = review.status.value if hasattr(review.status, 'value') else str(review.status)
        review.status = ReviewStatus.approved
        db.commit()
        db.refresh(review)
        
        # SECURITY: Log review approval for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='review_approved',
                resource_type='review',
                user_id=current_user.id,
                resource_id=review.id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'review_user_id': review.user_id,
                    'room_id': review.room_id,
                    'room_number': review.room.room_number if review.room else None,
                    'rating': review.rating,
                    'old_status': old_status,
                    'new_status': 'approved'
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log review approval audit: {e}')
        
        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
        }
        
        return success_response(
            data={'review': review_dict},
            message='Review approved successfully'
        )
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error approving review: {str(e)}', exc_info=True)
        raise HTTPException(
            status_code=500, 
            detail=error_response(message='An error occurred while approving the review')
        )

@router.patch('/{id}/reject', dependencies=[Depends(authorize_roles('admin'))])
async def reject_review(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:
        review = db.query(Review).options(joinedload(Review.room)).filter(Review.id == id).first()
        if not review:
            raise HTTPException(
                status_code=404, 
                detail=error_response(message='Review not found')
            )
        old_status = review.status.value if hasattr(review.status, 'value') else str(review.status)
        review.status = ReviewStatus.rejected
        db.commit()
        db.refresh(review)
        
        # SECURITY: Log review rejection for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='review_rejected',
                resource_type='review',
                user_id=current_user.id,
                resource_id=review.id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'review_user_id': review.user_id,
                    'room_id': review.room_id,
                    'room_number': review.room.room_number if review.room else None,
                    'rating': review.rating,
                    'old_status': old_status,
                    'new_status': 'rejected'
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log review rejection audit: {e}')
        
        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
        }
        
        return success_response(
            data={'review': review_dict},
            message='Review rejected successfully'
        )
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error rejecting review: {str(e)}', exc_info=True)
        raise HTTPException(
            status_code=500, 
            detail=error_response(message='An error occurred while rejecting the review')
        )

@router.delete('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_review(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:
        review = db.query(Review).options(joinedload(Review.room)).filter(Review.id == id).first()
        if not review:
            raise HTTPException(status_code=404, detail='Review not found')
        
        # Capture review details before deletion for audit
        review_details = {
            'review_id': review.id,
            'review_user_id': review.user_id,
            'room_id': review.room_id,
            'room_number': review.room.room_number if review.room else None,
            'rating': review.rating,
            'status': review.status.value if hasattr(review.status, 'value') else str(review.status),
            'comment_length': len(review.comment) if review.comment else 0
        }
        
        db.delete(review)
        db.commit()
        
        # SECURITY: Log review deletion for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='review_deleted',
                resource_type='review',
                user_id=current_user.id,
                resource_id=id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details=review_details,
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log review deletion audit: {e}')
        
        return {'status': 'success', 'message': 'Review deleted successfully'}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error deleting review: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))