from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload, selectinload
from typing import Optional, List
from datetime import datetime
from decimal import Decimal

from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ...auth.models.role import Role
from ..models.group_booking import (
    GroupBooking, GroupBookingMember, GroupRoomBlock, GroupPayment,
    GroupBookingStatus, PaymentOption
)
from ...rooms.models.room import Room
from ...rooms.models.room_type import RoomType
from ..services.group_booking_service import GroupBookingService
from ...rooms.services.room_service import get_base_url
from fastapi import Request

router = APIRouter(prefix='/group-bookings', tags=['group-bookings'])


@router.post('/')
async def create_group_booking(
    booking_data: dict,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Create a new group booking"""
    try:
        # Extract data
        coordinator_name = booking_data.get('coordinator_name') or current_user.full_name
        coordinator_email = booking_data.get('coordinator_email') or current_user.email
        coordinator_phone = booking_data.get('coordinator_phone') or current_user.phone
        
        check_in_date = datetime.fromisoformat(booking_data['check_in_date'].replace('Z', '+00:00'))
        check_out_date = datetime.fromisoformat(booking_data['check_out_date'].replace('Z', '+00:00'))
        
        room_blocks = booking_data.get('room_blocks', [])
        if not room_blocks:
            raise HTTPException(status_code=400, detail="At least one room block is required")
        
        payment_option = PaymentOption(booking_data.get('payment_option', 'coordinator_pays_all'))
        deposit_required = booking_data.get('deposit_required', False)
        deposit_percentage = booking_data.get('deposit_percentage')
        
        group_booking = GroupBookingService.create_group_booking(
            db=db,
            coordinator_id=current_user.id,
            coordinator_name=coordinator_name,
            coordinator_email=coordinator_email,
            coordinator_phone=coordinator_phone,
            check_in_date=check_in_date,
            check_out_date=check_out_date,
            room_blocks=room_blocks,
            group_name=booking_data.get('group_name'),
            group_type=booking_data.get('group_type'),
            payment_option=payment_option,
            deposit_required=deposit_required,
            deposit_percentage=deposit_percentage,
            special_requests=booking_data.get('special_requests'),
            notes=booking_data.get('notes'),
            cancellation_policy=booking_data.get('cancellation_policy'),
            cancellation_deadline=datetime.fromisoformat(booking_data['cancellation_deadline'].replace('Z', '+00:00')) if booking_data.get('cancellation_deadline') else None,
            cancellation_penalty_percentage=booking_data.get('cancellation_penalty_percentage'),
            group_discount_percentage=booking_data.get('group_discount_percentage')
        )
        
        # Load relationships
        db.refresh(group_booking)
        group_booking = db.query(GroupBooking).options(
            selectinload(GroupBooking.room_blocks).joinedload(GroupRoomBlock.room_type),
            selectinload(GroupBooking.members),
            selectinload(GroupBooking.coordinator)
        ).filter(GroupBooking.id == group_booking.id).first()
        
        return {
            'status': 'success',
            'message': 'Group booking created successfully',
            'data': {
                'group_booking': _serialize_group_booking(group_booking)
            }
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        import traceback
        logger = logging.getLogger(__name__)
        logger.error(f'Error creating group booking: {str(e)}')
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/')
async def get_group_bookings(
    search: Optional[str] = Query(None),
    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', 'staff')),
    db: Session = Depends(get_db)
):
    """Get all group bookings (admin/staff only)"""
    try:
        query = db.query(GroupBooking).options(
            selectinload(GroupBooking.room_blocks).joinedload(GroupRoomBlock.room_type),
            selectinload(GroupBooking.members),
            selectinload(GroupBooking.coordinator),
            selectinload(GroupBooking.payments)
        )
        
        if search:
            query = query.filter(
                GroupBooking.group_booking_number.like(f'%{search}%') |
                GroupBooking.group_name.like(f'%{search}%') |
                GroupBooking.coordinator_name.like(f'%{search}%')
            )
        
        if status_filter:
            try:
                query = query.filter(GroupBooking.status == GroupBookingStatus(status_filter))
            except ValueError:
                pass
        
        total = query.count()
        offset = (page - 1) * limit
        group_bookings = query.order_by(GroupBooking.created_at.desc()).offset(offset).limit(limit).all()
        
        return {
            'status': 'success',
            'data': {
                'group_bookings': [_serialize_group_booking(gb) for gb in group_bookings],
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'totalPages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error getting group bookings: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/me')
async def get_my_group_bookings(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get group bookings for current user (as coordinator)"""
    try:
        group_bookings = db.query(GroupBooking).options(
            selectinload(GroupBooking.room_blocks).joinedload(GroupRoomBlock.room_type),
            selectinload(GroupBooking.members),
            selectinload(GroupBooking.payments)
        ).filter(GroupBooking.coordinator_id == current_user.id).order_by(GroupBooking.created_at.desc()).all()
        
        return {
            'status': 'success',
            'data': {
                'group_bookings': [_serialize_group_booking(gb) for gb in group_bookings]
            }
        }
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error getting my group bookings: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/{group_booking_id}')
async def get_group_booking(
    group_booking_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get a specific group booking"""
    try:
        group_booking = db.query(GroupBooking).options(
            selectinload(GroupBooking.room_blocks).joinedload(GroupRoomBlock.room_type),
            selectinload(GroupBooking.members),
            selectinload(GroupBooking.coordinator),
            selectinload(GroupBooking.payments),
            selectinload(GroupBooking.individual_bookings)
        ).filter(GroupBooking.id == group_booking_id).first()
        
        if not group_booking:
            raise HTTPException(status_code=404, detail="Group booking not found")
        
        # Check authorization
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        if role and role.name not in ['admin', 'staff']:
            if group_booking.coordinator_id != current_user.id:
                raise HTTPException(status_code=403, detail="Not authorized to view this group booking")
        
        return {
            'status': 'success',
            'data': {
                'group_booking': _serialize_group_booking(group_booking, detailed=True)
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error getting group booking: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{group_booking_id}/members')
async def add_member(
    group_booking_id: int,
    member_data: dict,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Add a member to a group booking"""
    try:
        # Check authorization
        group_booking = db.query(GroupBooking).filter(GroupBooking.id == group_booking_id).first()
        if not group_booking:
            raise HTTPException(status_code=404, detail="Group booking not found")
        
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        if role and role.name not in ['admin', 'staff']:
            if group_booking.coordinator_id != current_user.id:
                raise HTTPException(status_code=403, detail="Not authorized to add members")
        
        member = GroupBookingService.add_member_to_group(
            db=db,
            group_booking_id=group_booking_id,
            full_name=member_data.get('full_name'),
            email=member_data.get('email'),
            phone=member_data.get('phone'),
            user_id=member_data.get('user_id'),
            room_block_id=member_data.get('room_block_id'),
            special_requests=member_data.get('special_requests'),
            preferences=member_data.get('preferences')
        )
        
        db.refresh(member)
        member = db.query(GroupBookingMember).options(
            joinedload(GroupBookingMember.user),
            joinedload(GroupBookingMember.room_block),
            joinedload(GroupBookingMember.assigned_room)
        ).filter(GroupBookingMember.id == member.id).first()
        
        return {
            'status': 'success',
            'message': 'Member added successfully',
            'data': {
                'member': _serialize_member(member)
            }
        }
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error adding member: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{group_booking_id}/confirm')
async def confirm_group_booking(
    group_booking_id: int,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Confirm a group booking"""
    try:
        group_booking = GroupBookingService.confirm_group_booking(db, group_booking_id)
        
        return {
            'status': 'success',
            'message': 'Group booking confirmed successfully',
            'data': {
                'group_booking': _serialize_group_booking(group_booking)
            }
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error confirming group booking: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{group_booking_id}/members/{member_id}/assign-room')
async def assign_room_to_member(
    group_booking_id: int,
    member_id: int,
    assignment_data: dict,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Assign a room to a group member and create individual booking"""
    try:
        room_id = assignment_data.get('room_id')
        if not room_id:
            raise HTTPException(status_code=400, detail="room_id is required")
        
        booking = GroupBookingService.create_individual_booking_from_member(
            db=db,
            member_id=member_id,
            room_id=room_id
        )
        
        return {
            'status': 'success',
            'message': 'Room assigned and booking created successfully',
            'data': {
                'booking': {
                    'id': booking.id,
                    'booking_number': booking.booking_number,
                    'room_id': booking.room_id,
                    'status': booking.status.value if hasattr(booking.status, 'value') else str(booking.status)
                }
            }
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error assigning room: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{group_booking_id}/payments')
async def add_payment(
    group_booking_id: int,
    payment_data: dict,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Add a payment to a group booking"""
    try:
        # Check authorization
        group_booking = db.query(GroupBooking).filter(GroupBooking.id == group_booking_id).first()
        if not group_booking:
            raise HTTPException(status_code=404, detail="Group booking not found")
        
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        if role and role.name not in ['admin', 'staff']:
            if group_booking.coordinator_id != current_user.id:
                raise HTTPException(status_code=403, detail="Not authorized to add payments")
        
        payment = GroupBookingService.add_group_payment(
            db=db,
            group_booking_id=group_booking_id,
            amount=Decimal(str(payment_data.get('amount'))),
            payment_method=payment_data.get('payment_method'),
            payment_type=payment_data.get('payment_type', 'deposit'),
            transaction_id=payment_data.get('transaction_id'),
            paid_by_member_id=payment_data.get('paid_by_member_id'),
            paid_by_user_id=payment_data.get('paid_by_user_id', current_user.id),
            notes=payment_data.get('notes')
        )
        
        return {
            'status': 'success',
            'message': 'Payment added successfully',
            'data': {
                'payment': _serialize_payment(payment)
            }
        }
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error adding payment: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{group_booking_id}/cancel')
async def cancel_group_booking(
    group_booking_id: int,
    cancellation_data: dict,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Cancel a group booking"""
    try:
        group_booking = GroupBookingService.cancel_group_booking(
            db=db,
            group_booking_id=group_booking_id,
            cancellation_reason=cancellation_data.get('reason')
        )
        
        return {
            'status': 'success',
            'message': 'Group booking cancelled successfully',
            'data': {
                'group_booking': _serialize_group_booking(group_booking)
            }
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error cancelling group booking: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/{group_booking_id}/availability')
async def check_availability(
    group_booking_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Check room availability for a group booking"""
    try:
        group_booking = db.query(GroupBooking).filter(GroupBooking.id == group_booking_id).first()
        if not group_booking:
            raise HTTPException(status_code=404, detail="Group booking not found")
        
        availability_results = []
        room_blocks = db.query(GroupRoomBlock).filter(
            GroupRoomBlock.group_booking_id == group_booking_id
        ).all()
        
        for room_block in room_blocks:
            availability = GroupBookingService.check_room_availability(
                db=db,
                room_type_id=room_block.room_type_id,
                check_in=group_booking.check_in_date,
                check_out=group_booking.check_out_date,
                num_rooms=room_block.rooms_blocked
            )
            
            availability_results.append({
                'room_block_id': room_block.id,
                'room_type_id': room_block.room_type_id,
                'rooms_blocked': room_block.rooms_blocked,
                'availability': availability
            })
        
        return {
            'status': 'success',
            'data': {
                'availability': availability_results
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error checking availability: {str(e)}')
        raise HTTPException(status_code=500, detail=str(e))


# Helper functions for serialization
def _serialize_group_booking(group_booking: GroupBooking, detailed: bool = False) -> dict:
    """Serialize group booking to dict"""
    data = {
        'id': group_booking.id,
        'group_booking_number': group_booking.group_booking_number,
        'coordinator': {
            'id': group_booking.coordinator_id,
            'name': group_booking.coordinator_name,
            'email': group_booking.coordinator_email,
            'phone': group_booking.coordinator_phone
        },
        'group_name': group_booking.group_name,
        'group_type': group_booking.group_type,
        'total_rooms': group_booking.total_rooms,
        'total_guests': group_booking.total_guests,
        'check_in_date': group_booking.check_in_date.isoformat() if group_booking.check_in_date else None,
        'check_out_date': group_booking.check_out_date.isoformat() if group_booking.check_out_date else None,
        'base_rate_per_room': float(group_booking.base_rate_per_room) if group_booking.base_rate_per_room else 0.0,
        'group_discount_percentage': float(group_booking.group_discount_percentage) if group_booking.group_discount_percentage else 0.0,
        'group_discount_amount': float(group_booking.group_discount_amount) if group_booking.group_discount_amount else 0.0,
        'original_total_price': float(group_booking.original_total_price) if group_booking.original_total_price else 0.0,
        'discount_amount': float(group_booking.discount_amount) if group_booking.discount_amount else 0.0,
        'total_price': float(group_booking.total_price) if group_booking.total_price else 0.0,
        'payment_option': group_booking.payment_option.value if hasattr(group_booking.payment_option, 'value') else str(group_booking.payment_option),
        'deposit_required': group_booking.deposit_required,
        'deposit_percentage': group_booking.deposit_percentage,
        'deposit_amount': float(group_booking.deposit_amount) if group_booking.deposit_amount else None,
        'amount_paid': float(group_booking.amount_paid) if group_booking.amount_paid else 0.0,
        'balance_due': float(group_booking.balance_due) if group_booking.balance_due else 0.0,
        'status': group_booking.status.value if hasattr(group_booking.status, 'value') else str(group_booking.status),
        'special_requests': group_booking.special_requests,
        'notes': group_booking.notes,
        'created_at': group_booking.created_at.isoformat() if group_booking.created_at else None,
        'updated_at': group_booking.updated_at.isoformat() if group_booking.updated_at else None
    }
    
    if detailed:
        data['room_blocks'] = [_serialize_room_block(rb) for rb in group_booking.room_blocks] if group_booking.room_blocks else []
        data['members'] = [_serialize_member(m) for m in group_booking.members] if group_booking.members else []
        data['payments'] = [_serialize_payment(p) for p in group_booking.payments] if group_booking.payments else []
        data['cancellation_policy'] = group_booking.cancellation_policy
        data['cancellation_deadline'] = group_booking.cancellation_deadline.isoformat() if group_booking.cancellation_deadline else None
        data['cancellation_penalty_percentage'] = float(group_booking.cancellation_penalty_percentage) if group_booking.cancellation_penalty_percentage else None
        data['confirmed_at'] = group_booking.confirmed_at.isoformat() if group_booking.confirmed_at else None
        data['cancelled_at'] = group_booking.cancelled_at.isoformat() if group_booking.cancelled_at else None
    
    return data


def _serialize_room_block(room_block: GroupRoomBlock) -> dict:
    """Serialize room block to dict"""
    return {
        'id': room_block.id,
        'room_type_id': room_block.room_type_id,
        'room_type': {
            'id': room_block.room_type.id,
            'name': room_block.room_type.name,
            'base_price': float(room_block.room_type.base_price) if room_block.room_type.base_price else 0.0
        } if room_block.room_type else None,
        'rooms_blocked': room_block.rooms_blocked,
        'rooms_confirmed': room_block.rooms_confirmed,
        'rooms_available': room_block.rooms_available,
        'rate_per_room': float(room_block.rate_per_room) if room_block.rate_per_room else 0.0,
        'total_block_price': float(room_block.total_block_price) if room_block.total_block_price else 0.0,
        'is_active': room_block.is_active,
        'block_released_at': room_block.block_released_at.isoformat() if room_block.block_released_at else None
    }


def _serialize_member(member: GroupBookingMember) -> dict:
    """Serialize group member to dict"""
    return {
        'id': member.id,
        'full_name': member.full_name,
        'email': member.email,
        'phone': member.phone,
        'user_id': member.user_id,
        'room_block_id': member.room_block_id,
        'assigned_room_id': member.assigned_room_id,
        'individual_booking_id': member.individual_booking_id,
        'special_requests': member.special_requests,
        'preferences': member.preferences,
        'individual_amount': float(member.individual_amount) if member.individual_amount else None,
        'individual_paid': float(member.individual_paid) if member.individual_paid else 0.0,
        'individual_balance': float(member.individual_balance) if member.individual_balance else 0.0,
        'is_checked_in': member.is_checked_in,
        'checked_in_at': member.checked_in_at.isoformat() if member.checked_in_at else None,
        'is_checked_out': member.is_checked_out,
        'checked_out_at': member.checked_out_at.isoformat() if member.checked_out_at else None
    }


def _serialize_payment(payment: GroupPayment) -> dict:
    """Serialize group payment to dict"""
    return {
        'id': payment.id,
        'amount': float(payment.amount) if payment.amount else 0.0,
        'payment_method': payment.payment_method,
        'payment_type': payment.payment_type,
        'payment_status': payment.payment_status,
        'transaction_id': payment.transaction_id,
        'payment_date': payment.payment_date.isoformat() if payment.payment_date else None,
        'notes': payment.notes,
        'paid_by_member_id': payment.paid_by_member_id,
        'paid_by_user_id': payment.paid_by_user_id,
        'created_at': payment.created_at.isoformat() if payment.created_at else None
    }

