from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import Optional
from . import repository
from .schema import PatientCreate, PatientUpdate, MedicalrecordCreate, MedicalrecordUpdate
from insurance import repository as insurance_repo


def create_patient(db: Session, data: PatientCreate) -> dict:
    data_dict = data.model_dump()
    if data_dict.get("insurance_provider_id"):
        provider = insurance_repo.get_by_id(db, data_dict["insurance_provider_id"])
        if not provider:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Insurance provider does not exist."
            )
    try:
        patient = repository.create(db, data_dict)
        db.flush()
        medical_record_data = {"patient_id": patient.id}
        from .models import MedicalRecord
        medical_record = MedicalRecord(**medical_record_data)
        db.add(medical_record)
        db.commit()
        db.refresh(patient)
        db.refresh(medical_record)
        return patient
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Patient number already exists."
            )
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Referenced resource does not exist."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def list_patients(db: Session, limit: int, offset: int, **filters) -> dict:
    items = repository.list_all(db, limit=limit, offset=offset, **filters)
    total = repository.count_all(db, **filters)
    return {"items": items, "total": total, "limit": limit, "offset": offset}


def get_patient(db: Session, patient_id: str):
    patient = repository.get_by_id(db, patient_id)
    if not patient:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Patient not found."
        )
    return patient


def get_patient_details(db: Session, patient_id: str):
    patient = repository.get_with_details(db, patient_id)
    if not patient:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Patient not found."
        )
    return patient


def update_patient(db: Session, patient_id: str, data: PatientUpdate):
    data_dict = data.model_dump(exclude_unset=True)
    if not data_dict:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No fields provided for update."
        )
    if data_dict.get("insurance_provider_id"):
        provider = insurance_repo.get_by_id(db, data_dict["insurance_provider_id"])
        if not provider:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Insurance provider does not exist."
            )
    try:
        patient = repository.update(db, patient_id, data_dict)
        if not patient:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Patient not found."
            )
        db.commit()
        db.refresh(patient)
        return patient
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Patient number already exists."
            )
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Referenced resource does not exist."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_patient(db: Session, patient_id: str):
    try:
        success = repository.delete(db, patient_id)
        if not success:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Patient not found."
            )
        db.commit()
        return {"message": "Patient deleted successfully."}
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Cannot delete patient with existing records."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def create_medicalrecord(db: Session, data: MedicalrecordCreate):
    data_dict = data.model_dump()
    patient = repository.get_by_id(db, data_dict["patient_id"])
    if not patient:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Patient does not exist."
        )
    existing = repository.get_medicalrecord_by_patient_id(db, data_dict["patient_id"])
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Medical record already exists for this patient."
        )
    try:
        from .models import MedicalRecord
        record = MedicalRecord(**data_dict)
        db.add(record)
        db.commit()
        db.refresh(record)
        return record
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Medical record already exists for this patient."
            )
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Patient does not exist."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def list_medicalrecords(db: Session, limit: int, offset: int, **filters) -> dict:
    from .models import MedicalRecord
    q = db.query(MedicalRecord)
    patient_id = filters.get("patient_id")
    if patient_id:
        q = q.filter(MedicalRecord.patient_id == patient_id)
    items = q.order_by(MedicalRecord.created_at.desc()).limit(limit).offset(offset).all()
    
    q_count = db.query(MedicalRecord.id)
    if patient_id:
        q_count = q_count.filter(MedicalRecord.patient_id == patient_id)
    total = q_count.count()
    
    return {"items": items, "total": total, "limit": limit, "offset": offset}


def get_medicalrecord(db: Session, record_id: str):
    from .models import MedicalRecord
    record = db.query(MedicalRecord).filter(MedicalRecord.id == record_id).first()
    if not record:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Medical record not found."
        )
    return record


def update_medicalrecord(db: Session, record_id: str, data: MedicalrecordUpdate):
    data_dict = data.model_dump(exclude_unset=True)
    if not data_dict:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No fields provided for update."
        )
    if data_dict.get("patient_id"):
        patient = repository.get_by_id(db, data_dict["patient_id"])
        if not patient:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Patient does not exist."
            )
    try:
        from .models import MedicalRecord
        record = db.query(MedicalRecord).filter(MedicalRecord.id == record_id).first()
        if not record:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Medical record not found."
            )
        for key, value in data_dict.items():
            setattr(record, key, value)
        db.commit()
        db.refresh(record)
        return record
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Medical record already exists for this patient."
            )
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Patient does not exist."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_medicalrecord(db: Session, record_id: str):
    try:
        from .models import MedicalRecord
        record = db.query(MedicalRecord).filter(MedicalRecord.id == record_id).first()
        if not record:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Medical record not found."
            )
        db.delete(record)
        db.commit()
        return {"message": "Medical record deleted successfully."}
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Cannot delete medical record with existing references."
            )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Data integrity error."
        )
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise