from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from datetime import datetime
from decimal import Decimal
from . import repository
from .schema import PaymentCreate, PaymentUpdate, PaymentInitiateRequest, PaymentWebhookRequest, PaymentWebhookResponse
from booking_management import repository as booking_repo
from reservation_management import repository as reservation_repo
from user_management import repository as user_repo

def _get_or_raise(db: Session, payment_id: str) -> "Payment":
    payment = repository.get_by_id(db, payment_id)
    if not payment:
        raise HTTPException(status_code=404, detail=f"Payment with id {payment_id} not found")
    return payment

def create_payment(db: Session, data: PaymentCreate):
    booking = booking_repo.get_by_id(db, data.booking_id)
    if not booking:
        raise HTTPException(status_code=404, detail=f"Booking with id {data.booking_id} not found")
    
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(status_code=404, detail=f"User with id {data.user_id} not found")
    
    payment_data = data.model_dump()
    payment = repository.create(db, payment_data)
    db.commit()
    db.refresh(payment)
    return payment

def get_payment(db: Session, payment_id: str):
    return _get_or_raise(db, payment_id)

def list_payments(db: Session, limit: int = 20, offset: int = 0, user_id: Optional[str] = None, booking_id: Optional[str] = None, payment_status: Optional[str] = None, payment_method: Optional[str] = None):
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if booking_id:
        filters["booking_id"] = booking_id
    if payment_status:
        filters["status"] = payment_status
    if payment_method:
        filters["payment_method"] = payment_method
    
    return repository.list_all(db, limit, offset, **filters)

def update_payment(db: Session, payment_id: str, data: PaymentUpdate):
    payment = _get_or_raise(db, payment_id)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "booking_id" in update_data:
        booking = booking_repo.get_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(status_code=404, detail=f"Booking with id {update_data['booking_id']} not found")
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(status_code=404, detail=f"User with id {update_data['user_id']} not found")
    
    updated_payment = repository.update(db, payment_id, update_data)
    db.commit()
    db.refresh(updated_payment)
    return updated_payment

def delete_payment(db: Session, payment_id: str):
    payment = _get_or_raise(db, payment_id)
    success = repository.delete(db, payment_id)
    if not success:
        raise HTTPException(status_code=500, detail="Failed to delete payment")
    db.commit()
    return {"message": "Payment deleted successfully"}

def initiate_payment(db: Session, data: PaymentInitiateRequest):
    from reservation_management.models import Reservation
    from booking_management.models import Booking
    from booking_management import repository as booking_repo
    from reservation_management import repository as reservation_repo
    from schedule_management import repository as schedule_repo
    from user_management import repository as user_repo
    
    reservation = reservation_repo.get_by_id(db, data.reservation_id)
    if not reservation:
        raise HTTPException(status_code=404, detail=f"Reservation with id {data.reservation_id} not found")
    
    if reservation.status != "active":
        raise HTTPException(status_code=400, detail=f"Reservation status is {reservation.status}, must be active to initiate payment")
    
    if reservation.expiry_datetime < datetime.utcnow():
        raise HTTPException(status_code=400, detail="Reservation has expired")
    
    existing_booking = booking_repo.get_by_reservation_id(db, data.reservation_id)
    if existing_booking:
        raise HTTPException(status_code=409, detail="Booking already exists for this reservation")
    
    booking_reference = f"BK{datetime.utcnow().strftime('%Y%m%d%H%M%S')}{reservation.id[:6].upper()}"
    
    booking_data = {
        "reservation_id": reservation.id,
        "user_id": reservation.user_id,
        "schedule_id": reservation.schedule_id,
        "booking_datetime": datetime.utcnow(),
        "total_amount": reservation.total_amount,
        "payment_status": "pending",
        "booking_reference": booking_reference,
        "status": "confirmed"
    }
    
    booking = booking_repo.create(db, booking_data)
    db.flush()
    
    payment_gateway_reference = f"PG{datetime.utcnow().strftime('%Y%m%d%H%M%S')}{booking.id[:8].upper()}"
    
    payment_data = {
        "booking_id": booking.id,
        "user_id": reservation.user_id,
        "amount": reservation.total_amount,
        "payment_method": data.payment_method.value,
        "payment_gateway_reference": payment_gateway_reference,
        "transaction_datetime": datetime.utcnow(),
        "status": "pending"
    }
    
    payment = repository.create(db, payment_data)
    db.commit()
    db.refresh(payment)
    
    return {
        "payment_id": payment.id,
        "booking_id": booking.id,
        "amount": payment.amount,
        "payment_method": payment.payment_method,
        "payment_gateway_reference": payment.payment_gateway_reference,
        "status": payment.status,
        "transaction_datetime": payment.transaction_datetime
    }

def process_payment_webhook(db: Session, data: PaymentWebhookRequest) -> PaymentWebhookResponse:
    from booking_management import repository as booking_repo
    from reservation_management import repository as reservation_repo
    from schedule_management import repository as schedule_repo
    from reservation_management.models import ReservationSeat
    from schedule_management.models import Seat
    from booking_management.models import BookingSeat
    payment = repository.get_by_gateway_reference(db, data.payment_gateway_reference)
    if not payment:
        return PaymentWebhookResponse(
            success=False,
            message=f"Payment with gateway reference {data.payment_gateway_reference} not found"
        )
    
    if payment.status in ["completed", "refunded"]:
        return PaymentWebhookResponse(
            success=True,
            message="Payment already processed",
            payment_id=payment.id,
            booking_id=payment.booking_id
        )
    
    if data.status.lower() == "success" or data.status.lower() == "completed":
        payment.status = "completed"
        payment.transaction_datetime = datetime.utcnow()
        
        booking = booking_repo.get_by_id(db, payment.booking_id)
        if booking:
            booking.payment_status = "completed"
            
            reservation = reservation_repo.get_by_id(db, booking.reservation_id)
            if reservation:
                reservation.status = "confirmed"
                
                reservation_seats = db.query(ReservationSeat).filter(
                    ReservationSeat.reservation_id == reservation.id
                ).all()
                
                seat_ids = [rs.seat_id for rs in reservation_seats]
                
                if seat_ids:
                    seats = db.query(Seat).filter(Seat.id.in_(seat_ids)).all()
                    for seat in seats:
                        seat.status = "booked"
                    
                    for rs in reservation_seats:
                        seat = next((s for s in seats if s.id == rs.seat_id), None)
                        if seat:
                            booking_seat_data = {
                                "booking_id": booking.id,
                                "seat_id": seat.id,
                                "seat_number": seat.seat_number
                            }
                            booking_seat = BookingSeat(**booking_seat_data)
                            db.add(booking_seat)
                
                ticket_number = f"TK{datetime.utcnow().strftime('%Y%m%d%H%M%S')}{booking.id[:6].upper()}"
                qr_code = f"QR-{booking.booking_reference}-{ticket_number}"
                
                ticket_data = {
                    "booking_id": booking.id,
                    "ticket_number": ticket_number,
                    "qr_code": qr_code,
                    "issue_datetime": datetime.utcnow(),
                    "validity_status": "valid"
                }
                
                from booking_management.models import Ticket
                ticket = Ticket(**ticket_data)
                db.add(ticket)
        
        db.commit()
        db.refresh(payment)
        
        return PaymentWebhookResponse(
            success=True,
            message="Payment completed successfully and booking confirmed",
            payment_id=payment.id,
            booking_id=payment.booking_id
        )
    
    elif data.status.lower() == "failed":
        payment.status = "failed"
        payment.transaction_datetime = datetime.utcnow()
        
        booking = booking_repo.get_by_id(db, payment.booking_id)
        if booking:
            booking.payment_status = "failed"
        
        db.commit()
        db.refresh(payment)
        
        return PaymentWebhookResponse(
            success=True,
            message="Payment failed, booking not confirmed",
            payment_id=payment.id,
            booking_id=payment.booking_id
        )
    
    else:
        return PaymentWebhookResponse(
            success=False,
            message=f"Unknown payment status: {data.status}"
        )

def get_payment_details(db: Session, payment_id: str):
    payment = repository.get_with_details(db, payment_id)
    if not payment:
        raise HTTPException(status_code=404, detail=f"Payment with id {payment_id} not found")
    return payment