"""
Routes for guest complaint management.
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func
from typing import 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.guest_complaint import (
    GuestComplaint, ComplaintStatus, ComplaintPriority, ComplaintCategory, ComplaintUpdate
)
from ..schemas.complaint import (
    CreateComplaintRequest, UpdateComplaintRequest, 
    AddComplaintUpdateRequest, ResolveComplaintRequest
)
from ...shared.utils.response_helpers import success_response
from ...analytics.services.audit_service import audit_service

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


@router.post('/')
async def create_complaint(
    complaint_data: CreateComplaintRequest,
    request: Request,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Create a new guest complaint."""
    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:
        # Verify booking ownership if booking_id provided
        if complaint_data.booking_id:
            from ...bookings.models.booking import Booking
            booking = db.query(Booking).filter(Booking.id == complaint_data.booking_id).first()
            if not booking:
                db.rollback()
                raise HTTPException(status_code=404, detail='Booking not found')
            
            # Check if user owns the booking (unless admin/staff)
            from ...shared.utils.role_helpers import is_admin, is_staff
            if not (is_admin(current_user, db) or is_staff(current_user, db)):
                if booking.user_id != current_user.id:
                    db.rollback()
                    raise HTTPException(status_code=403, detail='Access denied')
        
        complaint = GuestComplaint(
            guest_id=current_user.id,
            booking_id=complaint_data.booking_id,
            room_id=complaint_data.room_id,
            category=ComplaintCategory(complaint_data.category),
            priority=ComplaintPriority(complaint_data.priority),
            status=ComplaintStatus.open,
            title=complaint_data.title,
            description=complaint_data.description,
            attachments=complaint_data.attachments or []
        )
        
        db.add(complaint)
        db.flush()
        
        # Create initial update
        update = ComplaintUpdate(
            complaint_id=complaint.id,
            update_type='status_change',
            description=f'Complaint created: {complaint_data.title}',
            updated_by=current_user.id,
            update_metadata={'status': 'open', 'priority': complaint_data.priority}
        )
        db.add(update)
        
        db.commit()
        db.refresh(complaint)
        
        # SECURITY: Log complaint creation for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='complaint_created',
                resource_type='complaint',
                user_id=current_user.id,
                resource_id=complaint.id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'complaint_id': complaint.id,
                    'title': complaint.title,
                    'category': complaint.category.value,
                    'priority': complaint.priority.value,
                    'status': complaint.status.value,
                    'booking_id': complaint.booking_id,
                    'room_id': complaint.room_id
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log complaint creation audit: {e}')
        
        return success_response(
            data={'complaint': {
                'id': complaint.id,
                'title': complaint.title,
                'status': complaint.status.value,
                'priority': complaint.priority.value,
                'category': complaint.category.value,
                'created_at': complaint.created_at.isoformat()
            }},
            message='Complaint created successfully'
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating complaint: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while creating the complaint')


@router.get('/')
async def get_complaints(
    status: Optional[str] = Query(None),
    priority: Optional[str] = Query(None),
    category: Optional[str] = Query(None),
    assigned_to: Optional[int] = 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 complaints with filtering."""
    try:
        from ...shared.utils.role_helpers import is_admin, is_staff
        
        query = db.query(GuestComplaint)
        
        # Filter by user role
        try:
            is_admin_user = is_admin(current_user, db)
            is_staff_user = is_staff(current_user, db)
        except Exception as role_error:
            logger.warning(f'Error checking user role: {str(role_error)}')
            is_admin_user = False
            is_staff_user = False
        
        if not (is_admin_user or is_staff_user):
            # Customers can only see their own complaints
            query = query.filter(GuestComplaint.guest_id == current_user.id)
        elif assigned_to:
            # Staff/admin can filter by assignee
            query = query.filter(GuestComplaint.assigned_to == assigned_to)
        
        from sqlalchemy.orm import joinedload
        from ...rooms.models.room import Room
        
        # Eager load relationships
        query = query.options(
            joinedload(GuestComplaint.guest),
            joinedload(GuestComplaint.assignee)
        )
        
        # Apply filters
        if status:
            try:
                query = query.filter(GuestComplaint.status == ComplaintStatus(status))
            except ValueError:
                logger.warning(f'Invalid status filter: {status}')
        if priority:
            try:
                query = query.filter(GuestComplaint.priority == ComplaintPriority(priority))
            except ValueError:
                logger.warning(f'Invalid priority filter: {priority}')
        if category:
            try:
                query = query.filter(GuestComplaint.category == ComplaintCategory(category))
            except ValueError:
                logger.warning(f'Invalid category filter: {category}')
        
        # Pagination
        total = query.count()
        offset = (page - 1) * limit
        complaints = query.order_by(GuestComplaint.created_at.desc()).offset(offset).limit(limit).all()
        
        complaints_data = []
        for complaint in complaints:
            # Get room number if room_id exists
            room_number = None
            if complaint.room_id:
                room = db.query(Room).filter(Room.id == complaint.room_id).first()
                if room:
                    room_number = room.room_number
            
            complaints_data.append({
                'id': complaint.id,
                'title': complaint.title,
                'description': complaint.description,
                'category': complaint.category.value,
                'priority': complaint.priority.value,
                'status': complaint.status.value,
                'guest_id': complaint.guest_id,
                'guest_name': complaint.guest.full_name if complaint.guest else None,
                'booking_id': complaint.booking_id,
                'room_id': complaint.room_id,
                'room_number': room_number,
                'assigned_to': complaint.assigned_to,
                'assigned_staff_name': complaint.assignee.full_name if complaint.assignee else None,
                'resolution_notes': complaint.resolution,
                'resolved_at': complaint.resolved_at.isoformat() if complaint.resolved_at else None,
                'created_at': complaint.created_at.isoformat(),
                'updated_at': complaint.updated_at.isoformat()
            })
        
        return success_response(
            data={
                'complaints': complaints_data,
                'pagination': {
                    'page': page,
                    'limit': limit,
                    'total': total,
                    'total_pages': (total + limit - 1) // limit
                }
            },
            message='Complaints retrieved successfully'
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error retrieving complaints: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=f'An error occurred while retrieving complaints: {str(e)}')


@router.get('/{complaint_id}')
async def get_complaint(
    complaint_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get a specific complaint with details."""
    try:
        from sqlalchemy.orm import joinedload
        from ...rooms.models.room import Room
        
        complaint = db.query(GuestComplaint).options(
            joinedload(GuestComplaint.guest),
            joinedload(GuestComplaint.assignee),
            joinedload(GuestComplaint.room)
        ).filter(GuestComplaint.id == complaint_id).first()
        if not complaint:
            raise HTTPException(status_code=404, detail='Complaint not found')
        
        # Check access
        from ...shared.utils.role_helpers import is_admin, is_staff
        is_admin_user = is_admin(current_user, db)
        is_staff_user = is_staff(current_user, db)
        if not (is_admin_user or is_staff_user):
            if complaint.guest_id != current_user.id:
                raise HTTPException(status_code=403, detail='Access denied')
        
        # Get updates
        updates = db.query(ComplaintUpdate).options(
            joinedload(ComplaintUpdate.updater)
        ).filter(
            ComplaintUpdate.complaint_id == complaint_id
        ).order_by(ComplaintUpdate.created_at.asc()).all()
        
        # Get room number
        room_number = None
        if complaint.room:
            room_number = complaint.room.room_number
        
        complaint_data = {
            'id': complaint.id,
            'title': complaint.title,
            'description': complaint.description,
            'category': complaint.category.value,
            'priority': complaint.priority.value,
            'status': complaint.status.value,
            'guest_id': complaint.guest_id,
            'guest_name': complaint.guest.full_name if complaint.guest else None,
            'booking_id': complaint.booking_id,
            'room_id': complaint.room_id,
            'room_number': room_number,
            'assigned_to': complaint.assigned_to,
            'assigned_staff_name': complaint.assignee.full_name if complaint.assignee else None,
            'resolution_notes': complaint.resolution,
            'resolved_at': complaint.resolved_at.isoformat() if complaint.resolved_at else None,
            'resolved_by': complaint.resolved_by,
            'guest_satisfaction_rating': complaint.guest_satisfaction_rating,
            'guest_feedback': complaint.guest_feedback,
            'internal_notes': complaint.internal_notes if (is_admin_user or is_staff_user) else None,
            'attachments': complaint.attachments,
            'requires_follow_up': complaint.requires_follow_up,
            'follow_up_date': complaint.follow_up_date.isoformat() if complaint.follow_up_date else None,
            'created_at': complaint.created_at.isoformat(),
            'updated_at': complaint.updated_at.isoformat(),
            'updates': [{
                'id': u.id,
                'update_type': u.update_type,
                'description': u.description,
                'updated_by': u.updated_by,
                'updated_by_name': u.updater.full_name if u.updater else None,
                'created_at': u.created_at.isoformat()
            } for u in updates]
        }
        
        return success_response(
            data={'complaint': complaint_data},
            message='Complaint retrieved successfully'
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error retrieving complaint: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while retrieving complaint')


@router.put('/{complaint_id}')
async def update_complaint(
    complaint_id: int,
    update_data: UpdateComplaintRequest,
    request: Request,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Update a complaint (admin/staff only)."""
    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:
        complaint = db.query(GuestComplaint).filter(GuestComplaint.id == complaint_id).first()
        if not complaint:
            db.rollback()
            raise HTTPException(status_code=404, detail='Complaint not found')
        
        # Track changes for audit
        old_values = {
            'status': complaint.status.value,
            'priority': complaint.priority.value,
            'assigned_to': complaint.assigned_to
        }
        changes = []
        
        if update_data.status:
            old_status = complaint.status.value
            complaint.status = ComplaintStatus(update_data.status)
            changes.append(f'Status changed from {old_status} to {update_data.status}')
            
            if update_data.status == 'resolved' and not complaint.resolved_at:
                complaint.resolved_at = datetime.utcnow()
                complaint.resolved_by = current_user.id
        
        if update_data.priority:
            old_priority = complaint.priority.value
            complaint.priority = ComplaintPriority(update_data.priority)
            changes.append(f'Priority changed from {old_priority} to {update_data.priority}')
        
        if update_data.assigned_to is not None:
            old_assignee = complaint.assigned_to
            complaint.assigned_to = update_data.assigned_to
            changes.append(f'Assigned to user {update_data.assigned_to}')
        
        if update_data.resolution:
            complaint.resolution = update_data.resolution
        
        if update_data.internal_notes is not None:
            complaint.internal_notes = update_data.internal_notes
        
        if update_data.requires_follow_up is not None:
            complaint.requires_follow_up = update_data.requires_follow_up
        
        if update_data.follow_up_date:
            complaint.follow_up_date = datetime.fromisoformat(update_data.follow_up_date.replace('Z', '+00:00'))
        
        # Create update record
        if changes:
            update = ComplaintUpdate(
                complaint_id=complaint.id,
                update_type='status_change' if update_data.status else 'note',
                description='; '.join(changes),
                updated_by=current_user.id
            )
            db.add(update)
        
        db.commit()
        
        # SECURITY: Log complaint status change for audit trail
        if update_data.status and old_values['status'] != update_data.status:
            try:
                await audit_service.log_action(
                    db=db,
                    action='complaint_status_changed',
                    resource_type='complaint',
                    user_id=current_user.id,
                    resource_id=complaint.id,
                    ip_address=client_ip,
                    user_agent=user_agent,
                    request_id=request_id,
                    details={
                        'complaint_id': complaint.id,
                        'old_status': old_values['status'],
                        'new_status': update_data.status,
                        'guest_id': complaint.guest_id,
                        'resolved_by': current_user.id if update_data.status == 'resolved' else None
                    },
                    status='success'
                )
            except Exception as e:
                logger.warning(f'Failed to log complaint status change audit: {e}')
        
        # SECURITY: Log complaint assignment change for audit trail
        if update_data.assigned_to is not None and old_values['assigned_to'] != update_data.assigned_to:
            try:
                await audit_service.log_action(
                    db=db,
                    action='complaint_assigned',
                    resource_type='complaint',
                    user_id=current_user.id,
                    resource_id=complaint.id,
                    ip_address=client_ip,
                    user_agent=user_agent,
                    request_id=request_id,
                    details={
                        'complaint_id': complaint.id,
                        'old_assigned_to': old_values['assigned_to'],
                        'new_assigned_to': update_data.assigned_to,
                        'guest_id': complaint.guest_id
                    },
                    status='success'
                )
            except Exception as e:
                logger.warning(f'Failed to log complaint assignment audit: {e}')
        db.refresh(complaint)
        
        return success_response(
            data={'complaint': {
                'id': complaint.id,
                'status': complaint.status.value,
                'priority': complaint.priority.value,
                'assigned_to': complaint.assigned_to
            }},
            message='Complaint updated successfully'
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating complaint: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while updating complaint')


@router.post('/{complaint_id}/resolve')
async def resolve_complaint(
    complaint_id: int,
    resolve_data: ResolveComplaintRequest,
    request: Request,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Resolve a complaint."""
    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:
        complaint = db.query(GuestComplaint).filter(GuestComplaint.id == complaint_id).first()
        if not complaint:
            db.rollback()
            raise HTTPException(status_code=404, detail='Complaint not found')
        
        old_status = complaint.status.value
        
        complaint.status = ComplaintStatus.resolved
        complaint.resolution = resolve_data.resolution
        complaint.resolved_at = datetime.utcnow()
        complaint.resolved_by = current_user.id
        
        if resolve_data.guest_satisfaction_rating:
            complaint.guest_satisfaction_rating = resolve_data.guest_satisfaction_rating
        if resolve_data.guest_feedback:
            complaint.guest_feedback = resolve_data.guest_feedback
        
        # Create update record
        update = ComplaintUpdate(
            complaint_id=complaint.id,
            update_type='resolution',
            description=f'Complaint resolved: {resolve_data.resolution}',
            updated_by=current_user.id,
            update_metadata={
                'satisfaction_rating': resolve_data.guest_satisfaction_rating,
                'guest_feedback': resolve_data.guest_feedback
            }
        )
        db.add(update)
        
        db.commit()
        
        # SECURITY: Log complaint resolution for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='complaint_resolved',
                resource_type='complaint',
                user_id=current_user.id,
                resource_id=complaint.id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details={
                    'complaint_id': complaint.id,
                    'old_status': old_status,
                    'new_status': 'resolved',
                    'guest_id': complaint.guest_id,
                    'resolved_by': current_user.id,
                    'satisfaction_rating': resolve_data.guest_satisfaction_rating,
                    'has_feedback': bool(resolve_data.guest_feedback)
                },
                status='success'
            )
        except Exception as e:
            logger.warning(f'Failed to log complaint resolution audit: {e}')
        
        db.refresh(complaint)
        
        return success_response(
            data={'complaint': {
                'id': complaint.id,
                'status': complaint.status.value,
                'resolved_at': complaint.resolved_at.isoformat()
            }},
            message='Complaint resolved successfully'
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error resolving complaint: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while resolving complaint')


@router.post('/{complaint_id}/updates')
async def add_complaint_update(
    complaint_id: int,
    update_data: AddComplaintUpdateRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Add an update to a complaint."""
    try:
        complaint = db.query(GuestComplaint).filter(GuestComplaint.id == complaint_id).first()
        if not complaint:
            db.rollback()
            raise HTTPException(status_code=404, detail='Complaint not found')
        
        # Check access
        from ...shared.utils.role_helpers import is_admin, is_staff
        if not (is_admin(current_user, db) or is_staff(current_user, db)):
            if complaint.guest_id != current_user.id:
                db.rollback()
                raise HTTPException(status_code=403, detail='Access denied')
        
        update = ComplaintUpdate(
            complaint_id=complaint_id,
            update_type=update_data.update_type,
            description=update_data.description,
            updated_by=current_user.id,
            update_metadata=update_data.metadata or {}
        )
        
        db.add(update)
        db.commit()
        db.refresh(update)
        
        return success_response(
            data={'update': {
                'id': update.id,
                'update_type': update.update_type,
                'description': update.description,
                'created_at': update.created_at.isoformat()
            }},
            message='Update added successfully'
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error adding complaint update: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while adding update')

