from sqlalchemy.orm import Session, joinedload
from typing import Optional, List
from .models import Appointment


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


def list_all(db: Session, limit: int = 20, offset: int = 0, **filters) -> List[Appointment]:
    q = db.query(Appointment)
    if filters.get("patient_id") is not None:
        q = q.filter(Appointment.patient_id == filters["patient_id"])
    if filters.get("doctor_id") is not None:
        q = q.filter(Appointment.doctor_id == filters["doctor_id"])
    if filters.get("department_id") is not None:
        q = q.filter(Appointment.department_id == filters["department_id"])
    if filters.get("status") is not None:
        q = q.filter(Appointment.status == filters["status"])
    if filters.get("appointment_type") is not None:
        q = q.filter(Appointment.appointment_type == filters["appointment_type"])
    if filters.get("appointment_date") is not None:
        q = q.filter(Appointment.appointment_date == filters["appointment_date"])
    return q.order_by(Appointment.created_at.desc()).limit(limit).offset(offset).all()


def count_all(db: Session, **filters) -> int:
    q = db.query(Appointment.id)
    if filters.get("patient_id") is not None:
        q = q.filter(Appointment.patient_id == filters["patient_id"])
    if filters.get("doctor_id") is not None:
        q = q.filter(Appointment.doctor_id == filters["doctor_id"])
    if filters.get("department_id") is not None:
        q = q.filter(Appointment.department_id == filters["department_id"])
    if filters.get("status") is not None:
        q = q.filter(Appointment.status == filters["status"])
    if filters.get("appointment_type") is not None:
        q = q.filter(Appointment.appointment_type == filters["appointment_type"])
    if filters.get("appointment_date") is not None:
        q = q.filter(Appointment.appointment_date == filters["appointment_date"])
    return q.count()


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


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


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


def get_with_details(db: Session, entity_id: str) -> Optional[Appointment]:
    return (
        db.query(Appointment)
        .options(
            joinedload(Appointment.patient),
            joinedload(Appointment.doctor),
            joinedload(Appointment.department),
        )
        .filter(Appointment.id == entity_id)
        .first()
    )