from datetime import datetime
from typing import List, Optional

from pymongo import ReturnDocument

from app.core.database import get_db
from app.models.document import RepoDocument, RepoDocumentSection


def _serialize(doc: dict) -> dict:
    if doc and "_id" in doc:
        doc["id"] = str(doc.pop("_id"))
    return doc


async def upsert_document(document: RepoDocument) -> str:
    """Insert or update a document by (repo_id, doc_path). Returns the MongoDB _id as str."""
    db = get_db()
    # exclude created_at from $set — it must only appear in $setOnInsert to avoid conflict
    doc_dict = document.model_dump(exclude={"id", "created_at"})
    doc_dict["updated_at"] = datetime.utcnow()
    result = await db.repo_documents.find_one_and_update(
        {"repo_id": document.repo_id, "doc_path": document.doc_path},
        {"$set": doc_dict, "$setOnInsert": {"created_at": datetime.utcnow()}},
        upsert=True,
        return_document=ReturnDocument.AFTER,
    )
    return str(result["_id"])


async def get_document(repo_id: str, doc_path: str) -> Optional[RepoDocument]:
    db = get_db()
    doc = await db.repo_documents.find_one({"repo_id": repo_id, "doc_path": doc_path})
    return RepoDocument(**_serialize(doc)) if doc else None


async def list_documents(repo_id: str) -> List[RepoDocument]:
    db = get_db()
    cursor = db.repo_documents.find({"repo_id": repo_id})
    return [RepoDocument(**_serialize(doc)) async for doc in cursor]


async def delete_document_sections(doc_id: str) -> None:
    db = get_db()
    await db.repo_document_sections.delete_many({"doc_id": doc_id})


async def insert_sections(sections: List[RepoDocumentSection]) -> None:
    db = get_db()
    if not sections:
        return
    await db.repo_document_sections.insert_many(
        [s.model_dump(exclude={"id"}) for s in sections]
    )
