from fastapi import APIRouter, Depends, HTTPException, status, Query, Request, Header
from sqlalchemy.orm import Session, joinedload, selectinload, load_only
from typing import Optional
from datetime import datetime
import os
from ..config.database import get_db
from ..config.settings import settings
from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.payment import Payment, PaymentMethod, PaymentType, PaymentStatus
from ..models.booking import Booking, BookingStatus
from ..utils.role_helpers import can_access_all_payments
from ..utils.currency_helpers import get_currency_symbol
from ..utils.response_helpers import success_response
from ..utils.mailer import send_email
from ..utils.email_templates import payment_confirmation_email_template, booking_status_changed_email_template
from ..services.stripe_service import StripeService
from ..services.paypal_service import PayPalService
from ..services.borica_service import BoricaService
from ..services.loyalty_service import LoyaltyService
router = APIRouter(prefix='/payments', tags=['payments'])

async def cancel_booking_on_payment_failure(booking: Booking, db: Session, reason: str='Payment failed or canceled'):
    if booking.status == BookingStatus.cancelled:
        return
    from sqlalchemy.orm import selectinload
    # Use load_only to exclude non-existent columns (rate_plan_id, group_booking_id)
    booking = db.query(Booking).options(
        load_only(
            Booking.id, Booking.booking_number, Booking.user_id, Booking.room_id,
            Booking.check_in_date, Booking.check_out_date, Booking.num_guests,
            Booking.total_price, Booking.original_price, Booking.discount_amount,
            Booking.promotion_code, Booking.status, Booking.deposit_paid,
            Booking.requires_deposit, Booking.special_requests,
            Booking.created_at, Booking.updated_at
        ),
        selectinload(Booking.payments)
    ).filter(Booking.id == booking.id).first()
    if booking.payments:
        for payment in booking.payments:
            if payment.payment_status == PaymentStatus.pending:
                payment.payment_status = PaymentStatus.failed
                existing_notes = payment.notes or ''
                cancellation_note = f'\nPayment cancelled due to booking cancellation: {reason} on {datetime.utcnow().isoformat()}'
                payment.notes = existing_notes + cancellation_note if existing_notes else cancellation_note.strip()
    booking.status = BookingStatus.cancelled
    db.commit()
    db.refresh(booking)
    try:
        from ..models.system_settings import SystemSettings
        client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
        client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
        if booking.user:
            email_html = booking_status_changed_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name if booking.user else 'Guest', status='cancelled', client_url=client_url)
            await send_email(to=booking.user.email, subject=f'Booking Cancelled - {booking.booking_number}', html=email_html)
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Failed to send cancellation email: {e}')

@router.get('/')
async def get_payments(booking_id: Optional[int]=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(get_current_user), db: Session=Depends(get_db)):
    try:
        if booking_id:
            query = db.query(Payment).filter(Payment.booking_id == booking_id)
        else:
            query = db.query(Payment)
        if status_filter:
            try:
                query = query.filter(Payment.payment_status == PaymentStatus(status_filter))
            except ValueError:
                pass
        if not can_access_all_payments(current_user, db):
            query = query.join(Booking).filter(Booking.user_id == current_user.id)
        total = query.count()
        # Use load_only to exclude non-existent columns (rate_plan_id, group_booking_id)
        query = query.options(
            selectinload(Payment.booking).options(
                load_only(
                    Booking.id, Booking.booking_number, Booking.user_id, Booking.room_id,
                    Booking.check_in_date, Booking.check_out_date, Booking.num_guests,
                    Booking.total_price, Booking.original_price, Booking.discount_amount,
                    Booking.promotion_code, Booking.status, Booking.deposit_paid,
                    Booking.requires_deposit, Booking.special_requests,
                    Booking.created_at, Booking.updated_at
                ),
                joinedload(Booking.user)
            )
        )
        offset = (page - 1) * limit
        payments = query.order_by(Payment.created_at.desc()).offset(offset).limit(limit).all()
        result = []
        for payment in payments:
            payment_dict = {'id': payment.id, 'booking_id': payment.booking_id, 'amount': float(payment.amount) if payment.amount else 0.0, 'payment_method': payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else payment.payment_method, 'payment_type': payment.payment_type.value if isinstance(payment.payment_type, PaymentType) else payment.payment_type, 'deposit_percentage': payment.deposit_percentage, 'related_payment_id': payment.related_payment_id, 'payment_status': payment.payment_status.value if isinstance(payment.payment_status, PaymentStatus) else payment.payment_status, 'transaction_id': payment.transaction_id, 'payment_date': payment.payment_date.isoformat() if payment.payment_date else None, 'notes': payment.notes, 'created_at': payment.created_at.isoformat() if payment.created_at else None}
            if payment.booking:
                payment_dict['booking'] = {'id': payment.booking.id, 'booking_number': payment.booking.booking_number}
                if payment.booking.user:
                    payment_dict['booking']['user'] = {'id': payment.booking.user.id, 'name': payment.booking.user.full_name, 'full_name': payment.booking.user.full_name, 'email': payment.booking.user.email}
            result.append(payment_dict)
        return success_response(data={'payments': result, 'pagination': {'total': total, 'page': page, 'limit': limit, 'totalPages': (total + limit - 1) // limit}})
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Error fetching payments: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=f'Error fetching payments: {str(e)}')

@router.get('/booking/{booking_id}')
async def get_payments_by_booking_id(booking_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        from ..utils.role_helpers import is_admin
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        # Use load_only to exclude non-existent columns (rate_plan_id, group_booking_id)
        payments = db.query(Payment).options(
            joinedload(Payment.booking).options(
                load_only(
                    Booking.id, Booking.booking_number, Booking.user_id, Booking.room_id,
                    Booking.check_in_date, Booking.check_out_date, Booking.num_guests,
                    Booking.total_price, Booking.original_price, Booking.discount_amount,
                    Booking.promotion_code, Booking.status, Booking.deposit_paid,
                    Booking.requires_deposit, Booking.special_requests,
                    Booking.created_at, Booking.updated_at
                ),
                joinedload(Booking.user)
            )
        ).filter(Payment.booking_id == booking_id).order_by(Payment.created_at.desc()).all()
        result = []
        for payment in payments:
            payment_dict = {'id': payment.id, 'booking_id': payment.booking_id, 'amount': float(payment.amount) if payment.amount else 0.0, 'payment_method': payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else payment.payment_method, 'payment_type': payment.payment_type.value if isinstance(payment.payment_type, PaymentType) else payment.payment_type, 'deposit_percentage': payment.deposit_percentage, 'related_payment_id': payment.related_payment_id, 'payment_status': payment.payment_status.value if isinstance(payment.payment_status, PaymentStatus) else payment.payment_status, 'transaction_id': payment.transaction_id, 'payment_date': payment.payment_date.isoformat() if payment.payment_date else None, 'notes': payment.notes, 'created_at': payment.created_at.isoformat() if payment.created_at else None}
            if payment.booking:
                payment_dict['booking'] = {'id': payment.booking.id, 'booking_number': payment.booking.booking_number}
                if payment.booking.user:
                    payment_dict['booking']['user'] = {'id': payment.booking.user.id, 'name': payment.booking.user.full_name, 'full_name': payment.booking.user.full_name, 'email': payment.booking.user.email}
            result.append(payment_dict)
        return success_response(data={'payments': result})
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{id}')
async def get_payment_by_id(id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        payment = db.query(Payment).filter(Payment.id == id).first()
        if not payment:
            raise HTTPException(status_code=404, detail='Payment not found')
        if not can_access_all_payments(current_user, db):
            if payment.booking and payment.booking.user_id != current_user.id:
                raise HTTPException(status_code=403, detail='Forbidden')
        payment_dict = {'id': payment.id, 'booking_id': payment.booking_id, 'amount': float(payment.amount) if payment.amount else 0.0, 'payment_method': payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else payment.payment_method, 'payment_type': payment.payment_type.value if isinstance(payment.payment_type, PaymentType) else payment.payment_type, 'deposit_percentage': payment.deposit_percentage, 'related_payment_id': payment.related_payment_id, 'payment_status': payment.payment_status.value if isinstance(payment.payment_status, PaymentStatus) else payment.payment_status, 'transaction_id': payment.transaction_id, 'payment_date': payment.payment_date.isoformat() if payment.payment_date else None, 'notes': payment.notes, 'created_at': payment.created_at.isoformat() if payment.created_at else None}
        if payment.booking:
            payment_dict['booking'] = {'id': payment.booking.id, 'booking_number': payment.booking.booking_number}
        return success_response(data={'payment': payment_dict})
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/')
async def create_payment(payment_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        booking_id = payment_data.get('booking_id')
        amount = float(payment_data.get('amount', 0))
        payment_method = payment_data.get('payment_method', 'cash')
        payment_type = payment_data.get('payment_type', 'full')
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        from ..utils.role_helpers import is_admin
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        payment = Payment(booking_id=booking_id, amount=amount, payment_method=PaymentMethod(payment_method), payment_type=PaymentType(payment_type), payment_status=PaymentStatus.pending, payment_date=datetime.utcnow() if payment_data.get('mark_as_paid') else None, notes=payment_data.get('notes'))
        if payment_data.get('mark_as_paid'):
            payment.payment_status = PaymentStatus.completed
            payment.payment_date = datetime.utcnow()
        db.add(payment)
        db.commit()
        db.refresh(payment)
        
        # Send payment receipt notification
        if payment.payment_status == PaymentStatus.completed:
            try:
                from ..services.notification_service import NotificationService
                NotificationService.send_payment_receipt(db, payment)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment receipt notification: {e}')
        
        # Award loyalty points if payment completed and booking is confirmed
        if payment.payment_status == PaymentStatus.completed and booking:
            try:
                db.refresh(booking)
                if booking.status == BookingStatus.confirmed and booking.user:
                    # Check if booking already earned points
                    from ..models.loyalty_point_transaction import LoyaltyPointTransaction, TransactionSource
                    existing_points = db.query(LoyaltyPointTransaction).filter(
                        LoyaltyPointTransaction.booking_id == booking.id,
                        LoyaltyPointTransaction.source == TransactionSource.booking
                    ).first()
                    
                    if not existing_points:
                        # Award points based on payment amount
                        total_paid = sum(float(p.amount) for p in booking.payments if p.payment_status == PaymentStatus.completed)
                        if total_paid > 0:
                            LoyaltyService.earn_points_from_booking(db, booking.user_id, booking, total_paid)
            except Exception as loyalty_error:
                import logging
                logger = logging.getLogger(__name__)
                logger.error(f'Failed to award loyalty points: {loyalty_error}')
        
        if payment.payment_status == PaymentStatus.completed and booking.user:
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                email_html = payment_confirmation_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name, amount=float(payment.amount), payment_method=payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else str(payment.payment_method), transaction_id=payment.transaction_id, payment_type=payment.payment_type.value if payment.payment_type else None, total_price=float(booking.total_price), client_url=client_url, currency_symbol=currency_symbol)
                await send_email(to=booking.user.email, subject=f'Payment Confirmed - {booking.booking_number}', html=email_html)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.error(f'Failed to send payment confirmation email: {e}')
        return success_response(data={'payment': payment}, message='Payment created successfully')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}/status', dependencies=[Depends(authorize_roles('admin', 'staff', 'accountant'))])
async def update_payment_status(id: int, status_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
    try:
        payment = db.query(Payment).filter(Payment.id == id).first()
        if not payment:
            raise HTTPException(status_code=404, detail='Payment not found')
        status_value = status_data.get('status')
        old_status = payment.payment_status
        if status_value:
            try:
                new_status = PaymentStatus(status_value)
                payment.payment_status = new_status
                # Only cancel booking if it's a full refund or all payments are failed
                if new_status in [PaymentStatus.failed, PaymentStatus.refunded]:
                    # Use load_only to exclude non-existent columns (rate_plan_id, group_booking_id)
                    booking = db.query(Booking).options(
                        load_only(
                            Booking.id, Booking.booking_number, Booking.user_id, Booking.room_id,
                            Booking.check_in_date, Booking.check_out_date, Booking.num_guests,
                            Booking.total_price, Booking.original_price, Booking.discount_amount,
                            Booking.promotion_code, Booking.status, Booking.deposit_paid,
                            Booking.requires_deposit, Booking.special_requests,
                            Booking.created_at, Booking.updated_at
                        ),
                        selectinload(Booking.payments)
                    ).filter(Booking.id == payment.booking_id).first()
                    if booking and booking.status != BookingStatus.cancelled:
                        # Check if this is a full refund or if all payments are failed
                        total_paid = sum(float(p.amount) for p in booking.payments if p.payment_status == PaymentStatus.completed)
                        total_price = float(booking.total_price) if booking.total_price else 0.0
                        all_payments_failed = all(p.payment_status in [PaymentStatus.failed, PaymentStatus.refunded] for p in booking.payments)
                        is_full_refund = new_status == PaymentStatus.refunded and float(payment.amount) >= total_price
                        
                        # Only cancel if it's a full refund or all payments failed
                        if is_full_refund or (new_status == PaymentStatus.failed and all_payments_failed):
                            await cancel_booking_on_payment_failure(booking, db, reason=f'Payment {new_status.value}')
            except ValueError:
                raise HTTPException(status_code=400, detail='Invalid payment status')
        if status_data.get('transaction_id'):
            payment.transaction_id = status_data['transaction_id']
        if status_data.get('mark_as_paid'):
            payment.payment_status = PaymentStatus.completed
            payment.payment_date = datetime.utcnow()
        db.commit()
        db.refresh(payment)
        if payment.payment_status == PaymentStatus.completed and old_status != PaymentStatus.completed:
            # Send payment receipt notification
            try:
                from ..services.notification_service import NotificationService
                NotificationService.send_payment_receipt(db, payment)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment receipt notification: {e}')
            
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                payment = db.query(Payment).filter(Payment.id == id).first()
                if payment.booking and payment.booking.user:
                    client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                    client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                    currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                    currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                    currency_symbol = get_currency_symbol(currency)
                    email_html = payment_confirmation_email_template(booking_number=payment.booking.booking_number, guest_name=payment.booking.user.full_name, amount=float(payment.amount), payment_method=payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else str(payment.payment_method), transaction_id=payment.transaction_id, client_url=client_url, currency_symbol=currency_symbol)
                    await send_email(to=payment.booking.user.email, subject=f'Payment Confirmed - {payment.booking.booking_number}', html=email_html)
                    if payment.payment_type == PaymentType.deposit and payment.booking:
                        payment.booking.deposit_paid = True
                        if payment.booking.status in [BookingStatus.pending, BookingStatus.cancelled]:
                            payment.booking.status = BookingStatus.confirmed
                        db.commit()
                    elif payment.payment_type == PaymentType.full and payment.booking:
                        total_paid = sum((float(p.amount) for p in payment.booking.payments if p.payment_status == PaymentStatus.completed))
                        if total_paid >= float(payment.booking.total_price):
                            if payment.booking.status in [BookingStatus.pending, BookingStatus.cancelled]:
                                payment.booking.status = BookingStatus.confirmed
                            db.commit()
            except Exception as e:
                print(f'Failed to send payment confirmation email: {e}')
        return success_response(data={'payment': payment}, message='Payment status updated successfully')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/stripe/create-intent')
async def create_stripe_payment_intent(intent_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        from ..services.stripe_service import get_stripe_secret_key
        secret_key = get_stripe_secret_key(db)
        if not secret_key:
            secret_key = settings.STRIPE_SECRET_KEY
        if not secret_key:
            raise HTTPException(status_code=500, detail='Stripe is not configured. Please configure Stripe settings in Admin Panel or set STRIPE_SECRET_KEY environment variable.')
        booking_id = intent_data.get('booking_id')
        amount = float(intent_data.get('amount', 0))
        currency = intent_data.get('currency', 'usd')
        import logging
        logger = logging.getLogger(__name__)
        logger.info(f'Creating Stripe payment intent - Booking ID: {booking_id}, Amount: ${amount:,.2f}, Currency: {currency}')
        if not booking_id or amount <= 0:
            raise HTTPException(status_code=400, detail='booking_id and amount are required')
        if amount > 999999.99:
            logger.error(f"Amount ${amount:,.2f} exceeds Stripe's maximum of $999,999.99")
            raise HTTPException(status_code=400, detail=f"Amount ${amount:,.2f} exceeds Stripe's maximum of $999,999.99. Please contact support for large payments.")
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        from ..utils.role_helpers import is_admin
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        if booking.requires_deposit and (not booking.deposit_paid):
            deposit_payment = db.query(Payment).filter(Payment.booking_id == booking_id, Payment.payment_type == PaymentType.deposit, Payment.payment_status == PaymentStatus.pending).order_by(Payment.created_at.desc()).first()
            if deposit_payment:
                expected_deposit_amount = float(deposit_payment.amount)
                if abs(amount - expected_deposit_amount) > 0.01:
                    logger.warning(f'Amount mismatch for deposit payment: Requested ${amount:,.2f}, Expected deposit ${expected_deposit_amount:,.2f}, Booking total ${float(booking.total_price):,.2f}')
                    raise HTTPException(status_code=400, detail=f'For pay-on-arrival bookings, only the deposit amount (${expected_deposit_amount:,.2f}) should be charged, not the full booking amount (${float(booking.total_price):,.2f}).')
        intent = StripeService.create_payment_intent(amount=amount, currency=currency, metadata={'booking_id': str(booking_id), 'booking_number': booking.booking_number, 'user_id': str(current_user.id)}, db=db)
        from ..services.stripe_service import get_stripe_publishable_key
        publishable_key = get_stripe_publishable_key(db)
        if not publishable_key:
            publishable_key = settings.STRIPE_PUBLISHABLE_KEY
        if not publishable_key:
            import logging
            logger = logging.getLogger(__name__)
            logger.warning('Stripe publishable key is not configured')
            raise HTTPException(status_code=500, detail='Stripe publishable key is not configured. Please configure it in Admin Panel (Settings > Stripe Settings) or set STRIPE_PUBLISHABLE_KEY environment variable.')
        if not intent.get('client_secret'):
            import logging
            logger = logging.getLogger(__name__)
            logger.error('Payment intent created but client_secret is missing')
            raise HTTPException(status_code=500, detail='Failed to create payment intent. Client secret is missing.')
        return success_response(data={'client_secret': intent['client_secret'], 'payment_intent_id': intent['id'], 'publishable_key': publishable_key}, message='Payment intent created successfully')
    except HTTPException:
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Payment intent creation error: {str(e)}')
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error creating payment intent: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/stripe/confirm')
async def confirm_stripe_payment(payment_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        payment_intent_id = payment_data.get('payment_intent_id')
        booking_id = payment_data.get('booking_id')
        if not payment_intent_id:
            raise HTTPException(status_code=400, detail='payment_intent_id is required')
        payment = await StripeService.confirm_payment(payment_intent_id=payment_intent_id, db=db, booking_id=booking_id)
        try:
            db.commit()
        except Exception:
            pass
        booking = db.query(Booking).filter(Booking.id == payment['booking_id']).first()
        if booking:
            db.refresh(booking)
        if booking and booking.user:
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                email_html = payment_confirmation_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name, amount=payment['amount'], payment_method='stripe', transaction_id=payment['transaction_id'], payment_type=payment.get('payment_type'), total_price=float(booking.total_price), client_url=client_url, currency_symbol=currency_symbol)
                await send_email(to=booking.user.email, subject=f'Payment Confirmed - {booking.booking_number}', html=email_html)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment confirmation email: {e}')
        return success_response(data={'payment': payment, 'booking': {'id': booking.id if booking else None, 'booking_number': booking.booking_number if booking else None, 'status': booking.status.value if booking else None}}, message='Payment confirmed successfully')
    except HTTPException:
        db.rollback()
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Payment confirmation error: {str(e)}')
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error confirming payment: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/stripe/webhook')
async def stripe_webhook(request: Request, db: Session=Depends(get_db)):
    try:
        from ..services.stripe_service import get_stripe_webhook_secret
        webhook_secret = get_stripe_webhook_secret(db)
        if not webhook_secret:
            webhook_secret = settings.STRIPE_WEBHOOK_SECRET
        if not webhook_secret:
            raise HTTPException(status_code=503, detail={'status': 'error', 'message': 'Stripe webhook secret is not configured. Please configure it in Admin Panel (Settings > Stripe Settings) or set STRIPE_WEBHOOK_SECRET environment variable.'})
        payload = await request.body()
        signature = request.headers.get('stripe-signature')
        if not signature:
            raise HTTPException(status_code=400, detail='Missing stripe-signature header')
        result = await StripeService.handle_webhook(payload=payload, signature=signature, db=db)
        return success_response(data=result)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/paypal/create-order')
async def create_paypal_order(order_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        from ..services.paypal_service import get_paypal_client_id, get_paypal_client_secret
        client_id = get_paypal_client_id(db)
        if not client_id:
            client_id = settings.PAYPAL_CLIENT_ID
        client_secret = get_paypal_client_secret(db)
        if not client_secret:
            client_secret = settings.PAYPAL_CLIENT_SECRET
        if not client_id or not client_secret:
            raise HTTPException(status_code=500, detail='PayPal is not configured. Please configure PayPal settings in Admin Panel or set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables.')
        booking_id = order_data.get('booking_id')
        amount = float(order_data.get('amount', 0))
        currency = order_data.get('currency', 'USD')
        if not booking_id or amount <= 0:
            raise HTTPException(status_code=400, detail='booking_id and amount are required')
        if amount > 100000:
            raise HTTPException(status_code=400, detail=f"Amount ${amount:,.2f} exceeds PayPal's maximum of $100,000. Please contact support for large payments.")
        from ..utils.role_helpers import is_admin
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        if booking.requires_deposit and (not booking.deposit_paid):
            deposit_payment = db.query(Payment).filter(Payment.booking_id == booking_id, Payment.payment_type == PaymentType.deposit, Payment.payment_status == PaymentStatus.pending).order_by(Payment.created_at.desc()).first()
            if deposit_payment:
                expected_deposit_amount = float(deposit_payment.amount)
                if abs(amount - expected_deposit_amount) > 0.01:
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.warning(f'Amount mismatch for deposit payment: Requested ${amount:,.2f}, Expected deposit ${expected_deposit_amount:,.2f}, Booking total ${float(booking.total_price):,.2f}')
                    raise HTTPException(status_code=400, detail=f'For pay-on-arrival bookings, only the deposit amount (${expected_deposit_amount:,.2f}) should be charged, not the full booking amount (${float(booking.total_price):,.2f}).')
        client_url = settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
        return_url = order_data.get('return_url', f'{client_url}/payment/paypal/return')
        cancel_url = order_data.get('cancel_url', f'{client_url}/payment/paypal/cancel')
        order = PayPalService.create_order(amount=amount, currency=currency, metadata={'booking_id': str(booking_id), 'booking_number': booking.booking_number, 'user_id': str(current_user.id), 'description': f'Hotel Booking Payment - {booking.booking_number}', 'return_url': return_url, 'cancel_url': cancel_url}, db=db)
        if not order.get('approval_url'):
            raise HTTPException(status_code=500, detail='Failed to create PayPal order. Approval URL is missing.')
        return success_response(data={'order_id': order['id'], 'approval_url': order['approval_url'], 'status': order['status']}, message='PayPal order created successfully')
    except HTTPException:
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'PayPal order creation error: {str(e)}')
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error creating PayPal order: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/paypal/cancel')
async def cancel_paypal_payment(payment_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        booking_id = payment_data.get('booking_id')
        if not booking_id:
            raise HTTPException(status_code=400, detail='booking_id is required')
        payment = db.query(Payment).filter(Payment.booking_id == booking_id, Payment.payment_method == PaymentMethod.paypal, Payment.payment_status == PaymentStatus.pending).order_by(Payment.created_at.desc()).first()
        if not payment:
            payment = db.query(Payment).filter(Payment.booking_id == booking_id, Payment.payment_type == PaymentType.deposit, Payment.payment_status == PaymentStatus.pending).order_by(Payment.created_at.desc()).first()
        if payment:
            payment.payment_status = PaymentStatus.failed
            db.commit()
            db.refresh(payment)
            booking = db.query(Booking).filter(Booking.id == booking_id).first()
            if booking and booking.status != BookingStatus.cancelled:
                await cancel_booking_on_payment_failure(booking, db, reason='PayPal payment canceled by user')
        return success_response(message='Payment canceled and booking cancelled')
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/paypal/capture')
async def capture_paypal_payment(payment_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        order_id = payment_data.get('order_id')
        booking_id = payment_data.get('booking_id')
        if not order_id:
            raise HTTPException(status_code=400, detail='order_id is required')
        payment = await PayPalService.confirm_payment(order_id=order_id, db=db, booking_id=booking_id)
        try:
            db.commit()
        except Exception:
            pass
        booking = db.query(Booking).filter(Booking.id == payment['booking_id']).first()
        if booking:
            db.refresh(booking)
        if booking and booking.user:
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                email_html = payment_confirmation_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name, amount=payment['amount'], payment_method='paypal', transaction_id=payment['transaction_id'], payment_type=payment.get('payment_type'), total_price=float(booking.total_price), client_url=client_url, currency_symbol=currency_symbol)
                await send_email(to=booking.user.email, subject=f'Payment Confirmed - {booking.booking_number}', html=email_html)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment confirmation email: {e}')
        return success_response(data={'payment': payment, 'booking': {'id': booking.id if booking else None, 'booking_number': booking.booking_number if booking else None, 'status': booking.status.value if booking else None}}, message='Payment confirmed successfully')
    except HTTPException:
        db.rollback()
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'PayPal payment confirmation error: {str(e)}')
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error confirming PayPal payment: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/borica/create-payment')
async def create_borica_payment(payment_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        from ..services.borica_service import get_borica_terminal_id, get_borica_merchant_id
        terminal_id = get_borica_terminal_id(db)
        merchant_id = get_borica_merchant_id(db)
        if not terminal_id or not merchant_id:
            if not settings.BORICA_TERMINAL_ID or not settings.BORICA_MERCHANT_ID:
                raise HTTPException(status_code=500, detail='Borica is not configured. Please configure Borica settings in Admin Panel or set BORICA_TERMINAL_ID and BORICA_MERCHANT_ID environment variables.')
        booking_id = payment_data.get('booking_id')
        amount = float(payment_data.get('amount', 0))
        currency = payment_data.get('currency', 'BGN')
        if not booking_id or amount <= 0:
            raise HTTPException(status_code=400, detail='booking_id and amount are required')
        if amount > 100000:
            raise HTTPException(status_code=400, detail=f"Amount {amount:,.2f} exceeds maximum of 100,000. Please contact support for large payments.")
        from ..utils.role_helpers import is_admin
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        if booking.requires_deposit and (not booking.deposit_paid):
            deposit_payment = db.query(Payment).filter(Payment.booking_id == booking_id, Payment.payment_type == PaymentType.deposit, Payment.payment_status == PaymentStatus.pending).order_by(Payment.created_at.desc()).first()
            if deposit_payment:
                expected_deposit_amount = float(deposit_payment.amount)
                if abs(amount - expected_deposit_amount) > 0.01:
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.warning(f'Amount mismatch for deposit payment: Requested {amount:,.2f}, Expected deposit {expected_deposit_amount:,.2f}, Booking total {float(booking.total_price):,.2f}')
                    raise HTTPException(status_code=400, detail=f'For pay-on-arrival bookings, only the deposit amount ({expected_deposit_amount:,.2f}) should be charged, not the full booking amount ({float(booking.total_price):,.2f}).')
        transaction_id = BoricaService.generate_transaction_id(booking_id)
        client_url = settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
        return_url = payment_data.get('return_url', f'{client_url}/payment/borica/return')
        description = f'Hotel Booking Payment - {booking.booking_number}'
        payment_request = BoricaService.create_payment_request(amount=amount, currency=currency, order_id=transaction_id, description=description, return_url=return_url, db=db)
        payment_type = PaymentType.full
        if booking.requires_deposit and (not booking.deposit_paid):
            payment_type = PaymentType.deposit
        payment = Payment(booking_id=booking_id, amount=amount, payment_method=PaymentMethod.borica, payment_type=payment_type, payment_status=PaymentStatus.pending, transaction_id=transaction_id, notes=f'Borica payment initiated - Order: {transaction_id}')
        db.add(payment)
        db.commit()
        db.refresh(payment)
        return success_response(data={'payment_request': payment_request, 'payment_id': payment.id, 'transaction_id': transaction_id}, message='Borica payment request created successfully')
    except HTTPException:
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Borica payment creation error: {str(e)}')
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error creating Borica payment: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/borica/callback')
async def borica_callback(request: Request, db: Session=Depends(get_db)):
    """
    Handle Borica payment callback (POST from Borica gateway).
    Borica sends POST data with payment response.
    """
    try:
        form_data = await request.form()
        response_data = dict(form_data)
        
        # Also try to get from JSON if available
        try:
            json_data = await request.json()
            response_data.update(json_data)
        except:
            pass
        
        payment = await BoricaService.confirm_payment(response_data=response_data, db=db)
        try:
            db.commit()
        except Exception:
            pass
        
        booking = db.query(Booking).filter(Booking.id == payment['booking_id']).first()
        if booking:
            db.refresh(booking)
        
        if booking and booking.user:
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                email_html = payment_confirmation_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name, amount=payment['amount'], payment_method='borica', transaction_id=payment['transaction_id'], payment_type=payment.get('payment_type'), total_price=float(booking.total_price), client_url=client_url, currency_symbol=currency_symbol)
                await send_email(to=booking.user.email, subject=f'Payment Confirmed - {booking.booking_number}', html=email_html)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment confirmation email: {e}')
        
        # Redirect to return URL with success status
        return_url = response_data.get('BACKREF', '')
        if return_url:
            from fastapi.responses import RedirectResponse
            return RedirectResponse(url=f"{return_url}?status=success&order={response_data.get('ORDER', '')}&bookingId={payment['booking_id']}")
        
        return success_response(data={'payment': payment, 'booking': {'id': booking.id if booking else None, 'booking_number': booking.booking_number if booking else None, 'status': booking.status.value if booking else None}}, message='Payment confirmed successfully')
    except HTTPException:
        db.rollback()
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Borica payment callback error: {str(e)}')
        db.rollback()
        # Redirect to return URL with error status
        return_url = dict(await request.form()).get('BACKREF', '') if hasattr(request, 'form') else ''
        if return_url:
            from fastapi.responses import RedirectResponse
            return RedirectResponse(url=f"{return_url}?status=error&error={str(e)}")
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error in Borica callback: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/borica/confirm')
async def confirm_borica_payment(response_data: dict, db: Session=Depends(get_db)):
    try:
        payment = await BoricaService.confirm_payment(response_data=response_data, db=db)
        try:
            db.commit()
        except Exception:
            pass
        booking = db.query(Booking).filter(Booking.id == payment['booking_id']).first()
        if booking:
            db.refresh(booking)
        if booking and booking.user:
            try:
                from ..models.system_settings import SystemSettings
                client_url_setting = db.query(SystemSettings).filter(SystemSettings.key == 'client_url').first()
                client_url = client_url_setting.value if client_url_setting and client_url_setting.value else settings.CLIENT_URL or os.getenv('CLIENT_URL', 'http://localhost:5173')
                currency_setting = db.query(SystemSettings).filter(SystemSettings.key == 'platform_currency').first()
                currency = currency_setting.value if currency_setting and currency_setting.value else 'USD'
                currency_symbol = get_currency_symbol(currency)
                email_html = payment_confirmation_email_template(booking_number=booking.booking_number, guest_name=booking.user.full_name, amount=payment['amount'], payment_method='borica', transaction_id=payment['transaction_id'], payment_type=payment.get('payment_type'), total_price=float(booking.total_price), client_url=client_url, currency_symbol=currency_symbol)
                await send_email(to=booking.user.email, subject=f'Payment Confirmed - {booking.booking_number}', html=email_html)
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'Failed to send payment confirmation email: {e}')
        return success_response(data={'payment': payment, 'booking': {'id': booking.id if booking else None, 'booking_number': booking.booking_number if booking else None, 'status': booking.status.value if booking else None}}, message='Payment confirmed successfully')
    except HTTPException:
        db.rollback()
        raise
    except ValueError as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Borica payment confirmation error: {str(e)}')
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f'Unexpected error confirming Borica payment: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))