"""Hospital repository for database operations on hospitals."""
from datetime import UTC, datetime
from typing import Optional

from bson import ObjectId
from pymongo import ReturnDocument

from app.db import Database
from app.models.hospital import Hospital

COL = "hospitals"


def create(hospital: Hospital) -> Hospital:
    """Create a new hospital record in the database."""
    now = datetime.now(UTC)
    hospital_to_insert = hospital.model_copy(
        update={"created_at": now, "updated_at": now}
    ).to_bson()
    res = Database.get_db().get_collection(COL).insert_one(hospital_to_insert)
    return Hospital.from_bson({**hospital_to_insert, "_id": res.inserted_id})


def find_by_id(hospital_id: str) -> Optional[Hospital]:
    """Find a hospital by its ID."""
    if not ObjectId.is_valid(hospital_id):
        return None
    doc = Database.get_db().get_collection(COL).find_one({"_id": ObjectId(hospital_id)})
    return Hospital.from_bson(doc) if doc else None


def find_by_tracking_id(tracking_id: str) -> Optional[Hospital]:
    """Find a hospital by its tracking ID."""
    doc = Database.get_db().get_collection(COL).find_one({"trackingId": tracking_id})
    return Hospital.from_bson(doc) if doc else None


def update(hospital_id: str, updates: dict) -> Optional[Hospital]:
    """Update a hospital record."""
    if not ObjectId.is_valid(hospital_id):
        return None
    db = Database.get_db()
    existing = db.get_collection(COL).find_one({"_id": ObjectId(hospital_id)})
    if not existing:
        return None

    # Merge existing document with updates, validate via model,
    # and ensure updated_at is refreshed.
    merged = {**existing, **updates}
    candidate = Hospital.from_bson(merged).model_copy(update={"updated_at": datetime.now(UTC)})
    to_set = candidate.to_bson()
    to_set.pop("_id", None)

    doc = db.get_collection(COL).find_one_and_update(
        {"_id": ObjectId(hospital_id)},
        {"$set": to_set},
        return_document=ReturnDocument.AFTER,
    )
    return Hospital.from_bson(doc) if doc else None
