"""Patients addressed by a doctor, and the BLE measurements saved for them."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from database import get_db
from model.doctor import Doctor
from model.patient import Patient
from model.measurement import Measurement
from schemas import PatientCreate, PatientOut, MeasurementCreate, MeasurementOut

router = APIRouter(tags=["Patients"])


def _clean(s: str | None) -> str | None:
    """Strip NUL (0x00) bytes — BLE device names often carry null padding and
    Postgres text columns reject NUL characters."""
    return s.replace("\x00", "") if isinstance(s, str) else s


# ============================================================
# Patients
# ============================================================
@router.post("/patients", response_model=PatientOut, status_code=status.HTTP_201_CREATED)
def create_patient(payload: PatientCreate, db: Session = Depends(get_db)):
    if not db.get(Doctor, payload.doctor_id):
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Doctor not found")
    patient = Patient(
        doctor_id=payload.doctor_id,
        full_name=payload.full_name,
        father_husband_name=payload.father_husband_name,
        age=payload.age,
        gender=payload.gender,
        marital_status=payload.marital_status,
        mobile_number=payload.mobile_number,
        address=payload.address,
    )
    db.add(patient)
    db.commit()
    db.refresh(patient)
    return patient


@router.get("/patients", response_model=list[PatientOut])
def list_all_patients(db: Session = Depends(get_db)):
    """Every patient across all doctors (admin dashboard), newest first."""
    patients = db.query(Patient).order_by(Patient.created_at.desc()).all()
    for p in patients:
        p.doctor_name = p.doctor.full_name if p.doctor else None
    return patients


@router.get("/doctors/{doctor_id}/patients", response_model=list[PatientOut])
def list_patients_of_doctor(doctor_id: int, db: Session = Depends(get_db)):
    """Patients this doctor has already addressed (newest first)."""
    if not db.get(Doctor, doctor_id):
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Doctor not found")
    return (
        db.query(Patient)
        .filter(Patient.doctor_id == doctor_id)
        .order_by(Patient.created_at.desc())
        .all()
    )


@router.get("/patients/{patient_id}", response_model=PatientOut)
def get_patient(patient_id: int, db: Session = Depends(get_db)):
    patient = db.get(Patient, patient_id)
    if not patient:
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Patient not found")
    patient.doctor_name = patient.doctor.full_name if patient.doctor else None
    return patient


# ============================================================
# Measurements
# ============================================================
@router.get("/patients/{patient_id}/measurements", response_model=list[MeasurementOut])
def list_measurements(patient_id: int, db: Session = Depends(get_db)):
    if not db.get(Patient, patient_id):
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Patient not found")
    return (
        db.query(Measurement)
        .filter(Measurement.patient_id == patient_id)
        .order_by(Measurement.created_at.desc())
        .all()
    )


@router.post(
    "/patients/{patient_id}/measurements",
    response_model=MeasurementOut,
    status_code=status.HTTP_201_CREATED,
)
def add_measurement(patient_id: int, payload: MeasurementCreate, db: Session = Depends(get_db)):
    if not db.get(Patient, patient_id):
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Patient not found")
    m = Measurement(
        patient_id=patient_id,
        test_type=_clean(payload.test_type),
        value=_clean(payload.value),
        unit=_clean(payload.unit),
        pulse=_clean(payload.pulse),
        status=_clean(payload.status),
        device_name=_clean(payload.device_name),
        latitude=payload.latitude,
        longitude=payload.longitude,
    )
    db.add(m)
    db.commit()
    db.refresh(m)
    return m
