import uuid from datetime import UTC, datetime from typing import Any from motor.motor_asyncio import AsyncIOMotorDatabase from pymongo import ReturnDocument from app.services.document_service import access_query, can_edit COLLECTION = "document_versions" async def ensure_history_indexes(db: AsyncIOMotorDatabase) -> None: await db[COLLECTION].create_index([("document_id", 1), ("created_at", -1)]) def serialize_version(version: dict[str, Any]) -> dict[str, Any]: return { "id": version["_id"], "document_id": version["document_id"], "title": version["title"], "content": version.get("content", {}), "created_by": version["created_by"], "created_at": version["created_at"], "reason": version.get("reason", "Updated"), } async def create_version( db: AsyncIOMotorDatabase, document: dict[str, Any], actor_id: int, actor_name: str, reason: str, ) -> None: if not can_edit(document, actor_id): return await db[COLLECTION].insert_one({ "_id": str(uuid.uuid4()), "document_id": document["_id"], "title": document["title"], "content": document.get("content", {}), "created_by": actor_name, "created_at": datetime.now(UTC), "reason": reason, }) async def list_versions( db: AsyncIOMotorDatabase, document_id: str, user_id: int, ) -> list[dict[str, Any]] | None: document = await db["documents"].find_one({"_id": document_id, **access_query(user_id)}) if document is None: return None cursor = db[COLLECTION].find({"document_id": document_id}).sort("created_at", -1).limit(50) return [serialize_version(version) async for version in cursor] async def restore_version( db: AsyncIOMotorDatabase, document_id: str, version_id: str, user_id: int, actor_name: str, ) -> dict[str, Any] | None: document = await db["documents"].find_one({"_id": document_id, **access_query(user_id)}) if document is None or not can_edit(document, user_id): return None version = await db[COLLECTION].find_one({"_id": version_id, "document_id": document_id}) if version is None: return None await create_version(db, document, user_id, actor_name, "Before restore") now = datetime.now(UTC) updated = await db["documents"].find_one_and_update( {"_id": document_id}, {"$set": {"title": version["title"], "content": version.get("content", {}), "updated_at": now}}, return_document=ReturnDocument.AFTER, ) return updated