"""Test repository for database operations on tests."""
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.test import Test

COL = "tests"

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

def find_by_patient_id(
    patient_id: str,
    start_date: Optional[datetime] = None,
    end_date: Optional[datetime] = None,
) -> list[Test]:
    """Find all tests for a patient, optionally filtered by date range."""
    if not ObjectId.is_valid(patient_id):
        raise ValueError("Invalid patient ID")

    query: dict = {"patientId": ObjectId(patient_id)}
    if start_date is not None or end_date is not None:
        query["date"] = {}
        if start_date is not None:
            query["date"]["$gte"] = start_date
        if end_date is not None:
            query["date"]["$lte"] = end_date

    docs = Database.get_db().get_collection(COL).find(query)
    return [Test.from_bson(doc) for doc in docs]


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

def update(test_id: str, updates: dict) -> Optional[Test]:
    """Update a test record."""
    if not ObjectId.is_valid(test_id):
        raise ValueError("Invalid test ID")
    db = Database.get_db()
    existing = db.get_collection(COL).find_one({"_id": ObjectId(test_id)})
    if not existing:
        return None

    # Merge existing document with updates, validate via model,
    # and ensure updated_at is refreshed.
    merged = {**existing, **updates}
    candidate = Test.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(test_id)},
        {"$set": to_set},
        return_document=ReturnDocument.AFTER,
    )
    return Test.from_bson(doc) if doc else None


def delete_by_patient_id(patient_id: str) -> int:
    """Delete all tests for a patient and return the deleted count."""
    if not ObjectId.is_valid(patient_id):
        raise ValueError("Invalid patient ID")

    result = Database.get_db().get_collection(COL).delete_many({"patientId": ObjectId(patient_id)})
    return result.deleted_count


def delete_by_id(test_id: str) -> bool:
    """Delete one test by ID and return whether a document was deleted."""
    if not ObjectId.is_valid(test_id):
        raise ValueError("Invalid test ID")

    result = Database.get_db().get_collection(COL).delete_one({"_id": ObjectId(test_id)})
    return result.deleted_count > 0
