# MongoDB 连接与数据库访问（基类：统一做 URL 规范化）

from typing import Any, Optional
from urllib.parse import urlparse

from pymongo import MongoClient
from pymongo.collection import Collection
from pymongo.cursor import Cursor
from pymongo.results import DeleteResult, InsertOneResult, InsertManyResult, UpdateResult


def normalize_mongodb_url(raw_url: str) -> str:
    """将用户输入的 MongoDB 地址规范化为可直接给 MongoClient 使用的 URI。"""
    if raw_url is None:
        raise ValueError("mongodb_url 不能为空")
    value = str(raw_url).strip()
    if not value:
        raise ValueError("mongodb_url 不能为空")

    if value.startswith("mongodb://") or value.startswith("mongodb+srv://"):
        return value

    if value.startswith("mongodb:"):
        suffix = value[len("mongodb:") :].lstrip("/")
        if not suffix:
            raise ValueError("mongodb_url 不合法")
        return f"mongodb://{suffix}"

    if "://" not in value:
        return f"mongodb://{value}"

    parsed = urlparse(value)
    if parsed.scheme == "mongodb":
        return value
    raise ValueError(f"不支持的 mongodb_url: {raw_url!r}")


class MongoRepositoryBase:
    """
    MongoDB 集合访问的基类。

    子类继承后，在实例化时必须传入：
    - 数据库连接参数：mongodb_url、db_name，以及可选的 auth_source
    - 集合名称：collection_name

    示例：
        class MyRepo(MongoRepositoryBase):
            def __init__(self):
                super().__init__(
                    mongodb_url="mongodb://localhost:27017",
                    db_name="mydb",
                    collection_name="my_coll",
                    auth_source="admin",  # 可选
                )
    """

    def __init__(
        self,
        *,
        mongodb_url: str,
        db_name: str,
        collection_name: str,
        auth_source: Optional[str] = None,
    ) -> None:
        if not mongodb_url or not str(mongodb_url).strip():
            raise ValueError("mongodb_url 不能为空")
        if not db_name or not str(db_name).strip():
            raise ValueError("db_name 不能为空")
        if not collection_name or not str(collection_name).strip():
            raise ValueError("collection_name 不能为空")
        kwargs: dict[str, Any] = {}
        if auth_source and str(auth_source).strip():
            kwargs["authSource"] = auth_source.strip()
        self._client = MongoClient(normalize_mongodb_url(mongodb_url), **kwargs)
        self._db = self._client[db_name.strip()]
        self._coll = self._db[collection_name.strip()]

    @property
    def collection(self) -> Collection:
        """当前集合。"""
        return self._coll

    def find(self, filter: Optional[dict[str, Any]] = None, **kwargs: Any) -> Cursor:
        """查询多条。filter 为 None 时查全部。"""
        return self._coll.find(filter or {}, **kwargs)

    def find_one(
        self, filter: Optional[dict[str, Any]] = None, **kwargs: Any
    ) -> Optional[dict[str, Any]]:
        """查询单条。"""
        return self._coll.find_one(filter or {}, **kwargs)

    def insert_one(self, document: dict[str, Any], **kwargs: Any) -> InsertOneResult:
        """新增一条。"""
        return self._coll.insert_one(document, **kwargs)

    def insert_many(
        self, documents: list[dict[str, Any]], **kwargs: Any
    ) -> InsertManyResult:
        """新增多条。"""
        return self._coll.insert_many(documents, **kwargs)

    def update_one(
        self, filter: dict[str, Any], update: dict[str, Any], **kwargs: Any
    ) -> UpdateResult:
        """修改一条。"""
        return self._coll.update_one(filter, update, **kwargs)

    def update_many(
        self, filter: dict[str, Any], update: dict[str, Any], **kwargs: Any
    ) -> UpdateResult:
        """修改多条。"""
        return self._coll.update_many(filter, update, **kwargs)

    def delete_one(self, filter: dict[str, Any], **kwargs: Any) -> DeleteResult:
        """删除一条。"""
        return self._coll.delete_one(filter, **kwargs)

    def delete_many(self, filter: dict[str, Any], **kwargs: Any) -> DeleteResult:
        """删除多条。"""
        return self._coll.delete_many(filter, **kwargs)

    def aggregate(self, pipeline: list[dict[str, Any]], **kwargs: Any) -> Any:
        """聚合查询。"""
        return self._coll.aggregate(pipeline, **kwargs)
