from sqlalchemy.orm import Session, joinedload
from typing import Optional, List
from .models import Payment, Invoice, Invoicelineitem, Discount

# Payment repository functions
def get_by_id(db: Session, entity_id: str) -> Optional[Payment]:
    return db.query(Payment).filter(Payment.id == entity_id).first()

def list_all(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Payment]:
    query = db.query(Payment)
    
    if "booking_id" in filters and filters["booking_id"]:
        query = query.filter(Payment.booking_id == filters["booking_id"])
    
    if "payment_status" in filters and filters["payment_status"]:
        query = query.filter(Payment.payment_status == filters["payment_status"])
    
    if "payment_method" in filters and filters["payment_method"]:
        query = query.filter(Payment.payment_method == filters["payment_method"])
    
    return query.order_by(Payment.created_at.desc()).limit(limit).offset(offset).all()

def create(db: Session, data: dict) -> Payment:
    payment = Payment(**data)
    db.add(payment)
    db.flush()
    return payment

def update(db: Session, entity_id: str, data: dict) -> Optional[Payment]:
    payment = get_by_id(db, entity_id)
    if not payment:
        return None
    for key, value in data.items():
        setattr(payment, key, value)
    db.flush()
    return payment

def delete(db: Session, entity_id: str) -> bool:
    payment = get_by_id(db, entity_id)
    if not payment:
        return False
    db.delete(payment)
    db.flush()
    return True

# Invoice repository functions
def get_invoice_by_id(db: Session, entity_id: str) -> Optional[Invoice]:
    return db.query(Invoice).filter(Invoice.id == entity_id).first()

def list_invoices(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Invoice]:
    query = db.query(Invoice)
    
    if "booking_id" in filters and filters["booking_id"]:
        query = query.filter(Invoice.booking_id == filters["booking_id"])
    
    if "invoice_status" in filters and filters["invoice_status"]:
        query = query.filter(Invoice.invoice_status == filters["invoice_status"])
    
    if "invoice_number" in filters and filters["invoice_number"]:
        query = query.filter(Invoice.invoice_number == filters["invoice_number"])
    
    return query.order_by(Invoice.created_at.desc()).limit(limit).offset(offset).all()

def create_invoice(db: Session, data: dict) -> Invoice:
    invoice = Invoice(**data)
    db.add(invoice)
    db.flush()
    return invoice

def update_invoice(db: Session, entity_id: str, data: dict) -> Optional[Invoice]:
    invoice = get_invoice_by_id(db, entity_id)
    if not invoice:
        return None
    for key, value in data.items():
        setattr(invoice, key, value)
    db.flush()
    return invoice

def delete_invoice(db: Session, entity_id: str) -> bool:
    invoice = get_invoice_by_id(db, entity_id)
    if not invoice:
        return False
    db.delete(invoice)
    db.flush()
    return True

def get_invoice_by_number(db: Session, invoice_number: str) -> Optional[Invoice]:
    return db.query(Invoice).filter(Invoice.invoice_number == invoice_number).first()

def get_invoice_with_details(db: Session, entity_id: str) -> Optional[Invoice]:
    return (
        db.query(Invoice)
        .options(
            joinedload(Invoice.invoice_line_items)
        )
        .filter(Invoice.id == entity_id)
        .first()
    )

# Invoice Line Item repository functions
def get_line_item_by_id(db: Session, entity_id: str) -> Optional[Invoicelineitem]:
    return db.query(Invoicelineitem).filter(Invoicelineitem.id == entity_id).first()

def list_line_items(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Invoicelineitem]:
    query = db.query(Invoicelineitem)
    
    if "invoice_id" in filters and filters["invoice_id"]:
        query = query.filter(Invoicelineitem.invoice_id == filters["invoice_id"])
    
    return query.order_by(Invoicelineitem.item_order).limit(limit).offset(offset).all()

def create_line_item(db: Session, data: dict) -> Invoicelineitem:
    line_item = Invoicelineitem(**data)
    db.add(line_item)
    db.flush()
    return line_item

def update_line_item(db: Session, entity_id: str, data: dict) -> Optional[Invoicelineitem]:
    line_item = get_line_item_by_id(db, entity_id)
    if not line_item:
        return None
    for key, value in data.items():
        setattr(line_item, key, value)
    db.flush()
    return line_item

def delete_line_item(db: Session, entity_id: str) -> bool:
    line_item = get_line_item_by_id(db, entity_id)
    if not line_item:
        return False
    db.delete(line_item)
    db.flush()
    return True

# Discount repository functions
def get_discount_by_id(db: Session, entity_id: str) -> Optional[Discount]:
    return db.query(Discount).filter(Discount.id == entity_id).first()

def list_discounts(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Discount]:
    query = db.query(Discount)
    
    if "is_active" in filters and filters["is_active"] is not None:
        query = query.filter(Discount.is_active == filters["is_active"])
    
    if "discount_type" in filters and filters["discount_type"]:
        query = query.filter(Discount.discount_type == filters["discount_type"])
    
    if "code" in filters and filters["code"]:
        query = query.filter(Discount.code.ilike(f"%{filters['code']}%"))
    
    return query.order_by(Discount.created_at.desc()).limit(limit).offset(offset).all()

def create_discount(db: Session, data: dict) -> Discount:
    discount = Discount(**data)
    db.add(discount)
    db.flush()
    return discount

def update_discount(db: Session, entity_id: str, data: dict) -> Optional[Discount]:
    discount = get_discount_by_id(db, entity_id)
    if not discount:
        return None
    for key, value in data.items():
        setattr(discount, key, value)
    db.flush()
    return discount

def delete_discount(db: Session, entity_id: str) -> bool:
    discount = get_discount_by_id(db, entity_id)
    if not discount:
        return False
    db.delete(discount)
    db.flush()
    return True

def get_discount_by_code(db: Session, code: str) -> Optional[Discount]:
    return db.query(Discount).filter(Discount.code == code.upper()).first()