from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import or_
from typing import Optional
import bcrypt
from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.role import Role
from ...bookings.models.booking import Booking, BookingStatus
from ...shared.utils.role_helpers import can_manage_users
from ...shared.utils.response_helpers import success_response
from ...analytics.services.audit_service import audit_service
from ..schemas.user import CreateUserRequest, UpdateUserRequest
router = APIRouter(prefix='/users', tags=['users'])

@router.get('/')
async def get_users(search: Optional[str]=Query(None), role: 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)):
    try:
        query = db.query(User)
        if search:
            query = query.filter(or_(User.full_name.like(f'%{search}%'), User.email.like(f'%{search}%'), User.phone.like(f'%{search}%')))
        if role:
            # Query role by name instead of hardcoded IDs for flexibility
            role_obj = db.query(Role).filter(Role.name == role).first()
            if role_obj:
                query = query.filter(User.role_id == role_obj.id)
        if status_filter:
            is_active = status_filter == 'active'
            query = query.filter(User.is_active == is_active)
        total = query.count()
        offset = (page - 1) * limit
        users = query.order_by(User.created_at.desc()).offset(offset).limit(limit).all()
        result = []
        for user in users:
            user_dict = {'id': user.id, 'email': user.email, 'full_name': user.full_name, 'phone': user.phone, 'phone_number': user.phone, 'address': user.address, 'avatar': user.avatar, 'currency': getattr(user, 'currency', 'VND'), 'is_active': user.is_active, 'status': 'active' if user.is_active else 'inactive', 'role_id': user.role_id, 'role': user.role.name if user.role else 'customer', 'created_at': user.created_at.isoformat() if user.created_at else None, 'updated_at': user.updated_at.isoformat() if user.updated_at else None}
            result.append(user_dict)
        return success_response(data={'users': result, '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('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def get_user_by_id(id: int, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        user = db.query(User).filter(User.id == id).first()
        if not user:
            raise HTTPException(status_code=404, detail='User not found')
        bookings = db.query(Booking).filter(Booking.user_id == id).order_by(Booking.created_at.desc()).limit(5).all()
        user_dict = {'id': user.id, 'email': user.email, 'full_name': user.full_name, 'phone': user.phone, 'phone_number': user.phone, 'address': user.address, 'avatar': user.avatar, 'currency': getattr(user, 'currency', 'VND'), 'is_active': user.is_active, 'status': 'active' if user.is_active else 'inactive', 'role_id': user.role_id, 'role': user.role.name if user.role else 'customer', 'created_at': user.created_at.isoformat() if user.created_at else None, 'updated_at': user.updated_at.isoformat() if user.updated_at else None, 'bookings': [{'id': b.id, 'booking_number': b.booking_number, 'status': b.status.value if isinstance(b.status, BookingStatus) else b.status, 'created_at': b.created_at.isoformat() if b.created_at else None} for b in bookings]}
        return success_response(data={'user': user_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_user(
    request: Request,
    user_data: CreateUserRequest, 
    current_user: User=Depends(authorize_roles('admin')), 
    db: Session=Depends(get_db)
):
    """Create a user with validated input using Pydantic schema."""
    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:
        email = user_data.email
        password = user_data.password
        full_name = user_data.full_name
        phone_number = user_data.phone_number
        role_id = user_data.role_id or 3  # Default to customer role
        existing = db.query(User).filter(User.email == email).first()
        if existing:
            raise HTTPException(status_code=400, detail='Email already exists')
        password_bytes = password.encode('utf-8')
        salt = bcrypt.gensalt()
        hashed_password = bcrypt.hashpw(password_bytes, salt).decode('utf-8')
        user = User(email=email, password=hashed_password, full_name=full_name, phone=phone_number, role_id=role_id, is_active=True)
        db.add(user)
        db.commit()
        db.refresh(user)
        
        # Log admin action - user creation
        await audit_service.log_action(
            db=db,
            action='admin_user_created',
            resource_type='user',
            user_id=current_user.id,
            resource_id=user.id,
            ip_address=client_ip,
            user_agent=user_agent,
            request_id=request_id,
            details={
                'created_user_email': user.email,
                'created_user_name': user.full_name,
                'role_id': user.role_id,
                'is_active': user.is_active
            },
            status='success'
        )
        
        user_dict = {'id': user.id, 'email': user.email, 'full_name': user.full_name, 'phone': user.phone, 'phone_number': user.phone, 'currency': getattr(user, 'currency', 'VND'), 'role_id': user.role_id, 'is_active': user.is_active}
        return success_response(data={'user': user_dict}, message='User created successfully')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}')
async def update_user(id: int, request: Request, user_data: UpdateUserRequest, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    """Update a user with validated input using Pydantic schema."""
    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:
        if not can_manage_users(current_user, db) and current_user.id != id:
            raise HTTPException(status_code=403, detail='Forbidden')
        user = db.query(User).options(joinedload(User.role)).filter(User.id == id).first()
        if not user:
            raise HTTPException(status_code=404, detail='User not found')
        
        # Track changes for audit logging
        changes = {}
        old_role_id = user.role_id
        old_role_name = user.role.name if user.role else None
        old_is_active = user.is_active
        
        # Check email uniqueness if being updated
        if user_data.email and user_data.email != user.email:
            existing = db.query(User).filter(User.email == user_data.email).first()
            if existing:
                raise HTTPException(status_code=400, detail='Email already exists')
        
        # Update fields if provided
        if user_data.full_name is not None:
            user.full_name = user_data.full_name
        if user_data.email is not None and can_manage_users(current_user, db):
            if user_data.email != user.email:
                changes['email'] = {'old': user.email, 'new': user_data.email}
            user.email = user_data.email
        if user_data.phone_number is not None:
            user.phone = user_data.phone_number
        if user_data.role_id is not None and can_manage_users(current_user, db):
            # SECURITY: Prevent admin from changing their own role
            if current_user.id == id:
                raise HTTPException(
                    status_code=400,
                    detail='You cannot change your own role. Please ask another admin to do it.'
                )
            new_role = db.query(Role).filter(Role.id == user_data.role_id).first()
            new_role_name = new_role.name if new_role else None
            if user_data.role_id != old_role_id:
                changes['role'] = {'old': old_role_name, 'new': new_role_name, 'old_id': old_role_id, 'new_id': user_data.role_id}
            user.role_id = user_data.role_id
        if user_data.is_active is not None and can_manage_users(current_user, db):
            if user_data.is_active != old_is_active:
                changes['is_active'] = {'old': old_is_active, 'new': user_data.is_active}
            user.is_active = user_data.is_active
        db.commit()
        db.refresh(user)
        
        # SECURITY: Log sensitive user changes for audit trail
        if changes and can_manage_users(current_user, db):
            try:
                audit_details = {
                    'updated_user_id': user.id,
                    'updated_user_email': user.email,
                    'changes': changes
                }
                
                # Log role changes separately as high-priority audit events
                if 'role' in changes:
                    await audit_service.log_action(
                        db=db,
                        action='user_role_changed',
                        resource_type='user',
                        user_id=current_user.id,
                        resource_id=user.id,
                        ip_address=client_ip,
                        user_agent=user_agent,
                        request_id=request_id,
                        details=audit_details,
                        status='success'
                    )
                
                # Log account status changes (activation/deactivation)
                if 'is_active' in changes:
                    action_name = 'user_deactivated' if not user_data.is_active else 'user_activated'
                    await audit_service.log_action(
                        db=db,
                        action=action_name,
                        resource_type='user',
                        user_id=current_user.id,
                        resource_id=user.id,
                        ip_address=client_ip,
                        user_agent=user_agent,
                        request_id=request_id,
                        details=audit_details,
                        status='success'
                    )
                
                # Log other significant changes
                if 'email' in changes:
                    await audit_service.log_action(
                        db=db,
                        action='user_email_changed',
                        resource_type='user',
                        user_id=current_user.id,
                        resource_id=user.id,
                        ip_address=client_ip,
                        user_agent=user_agent,
                        request_id=request_id,
                        details=audit_details,
                        status='success'
                    )
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to log user update audit: {e}')
        
        user_dict = {'id': user.id, 'email': user.email, 'full_name': user.full_name, 'phone': user.phone, 'phone_number': user.phone, 'currency': getattr(user, 'currency', 'VND'), 'role_id': user.role_id, 'is_active': user.is_active}
        return success_response(data={'user': user_dict}, message='User updated successfully')
    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_user(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:
        user = db.query(User).options(joinedload(User.role)).filter(User.id == id).first()
        if not user:
            raise HTTPException(status_code=404, detail='User not found')
        
        # SECURITY: Prevent admin from deleting themselves
        if current_user.id == id:
            raise HTTPException(
                status_code=400,
                detail='You cannot delete your own account. Please ask another admin to do it.'
            )
        
        # Capture user info before deletion for audit
        deleted_user_info = {
            'user_id': user.id,
            'email': user.email,
            'full_name': user.full_name,
            'role': user.role.name if user.role else None,
            'role_id': user.role_id,
            'is_active': user.is_active
        }
        
        active_bookings = db.query(Booking).filter(Booking.user_id == id, Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])).count()
        if active_bookings > 0:
            raise HTTPException(status_code=400, detail='Cannot delete user with active bookings')
        
        db.delete(user)
        db.commit()
        
        # SECURITY: Log user deletion for audit trail
        try:
            await audit_service.log_action(
                db=db,
                action='user_deleted',
                resource_type='user',
                user_id=current_user.id,
                resource_id=id,
                ip_address=client_ip,
                user_agent=user_agent,
                request_id=request_id,
                details=deleted_user_info,
                status='success'
            )
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.warning(f'Failed to log user deletion audit: {e}')
        
        return success_response(message='User deleted successfully')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))