from sqlalchemy.orm import Session, joinedload
from typing import Optional, List
from .models import Calculationsession, Calculation, Amortizationentry


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


def get_session_by_session_id(db: Session, session_id: str) -> Optional[Calculationsession]:
    return db.query(Calculationsession).filter(Calculationsession.session_id == session_id).first()


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


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


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


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


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


def get_calculation_with_details(db: Session, calculation_id: str) -> Optional[Calculation]:
    return (
        db.query(Calculation)
        .options(
            joinedload(Calculation.amortization_entries)
        )
        .filter(Calculation.id == calculation_id)
        .first()
    )


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


def create_calculation(db: Session, data: dict) -> Calculation:
    calculation = Calculation(**data)
    db.add(calculation)
    db.flush()
    return calculation


def update_calculation(db: Session, entity_id: str, data: dict) -> Optional[Calculation]:
    calculation = get_calculation_by_id(db, entity_id)
    if not calculation:
        return None
    for key, value in data.items():
        setattr(calculation, key, value)
    db.flush()
    return calculation


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


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


def list_amortization_entries(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Amortizationentry]:
    query = db.query(Amortizationentry)
    if "calculation_id" in filters and filters["calculation_id"]:
        query = query.filter(Amortizationentry.calculation_id == filters["calculation_id"])
    return query.order_by(Amortizationentry.payment_number).limit(limit).offset(offset).all()


def list_amortization_by_calculation(db: Session, calculation_id: str) -> List[Amortizationentry]:
    return (
        db.query(Amortizationentry)
        .filter(Amortizationentry.calculation_id == calculation_id)
        .order_by(Amortizationentry.payment_number)
        .all()
    )


def create_amortization(db: Session, data: dict) -> Amortizationentry:
    entry = Amortizationentry(**data)
    db.add(entry)
    db.flush()
    return entry


def update_amortization(db: Session, entity_id: str, data: dict) -> Optional[Amortizationentry]:
    entry = get_amortization_by_id(db, entity_id)
    if not entry:
        return None
    for key, value in data.items():
        setattr(entry, key, value)
    db.flush()
    return entry


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