"""Queries like `find many`."""

import json
from typing import Any

from pymongo.asynchronous.collection import AsyncCollection
from pymongo.asynchronous.cursor import AsyncCursor, CursorType
from pymongo.results import DeleteResult

from .. import store
from ..errors import PanicError


class ManyMixin:
    """Queries like `find many`."""

    @classmethod
    async def find_many(
        cls,
        filter=None,
        projection=None,
        skip=0,
        limit=0,
        no_cursor_timeout=False,
        cursor_type=CursorType.NON_TAILABLE,
        sort=None,
        allow_partial_results=False,
        oplog_replay=False,
        batch_size=0,
        collation=None,
        hint=None,
        max_scan=None,
        max_time_ms=None,
        max=None,
        min=None,
        return_key=False,
        show_record_id=False,
        comment=None,
        session=None,
        allow_disk_use=None,
    ) -> list[dict[str, Any]]:
        """Find documents."""
        # Get collection for current model.
        collection: AsyncCollection = store.MONGO_DATABASE[cls.META["collection_name"]]  # type: ignore[index, attr-defined]
        # Get documents.
        doc_list: list[dict[str, Any]] = []
        cursor: AsyncCursor = collection.find(
            filter=filter,
            projection=projection,
            skip=skip,
            limit=limit or cls.META["db_query_docs_limit"],  # type: ignore[index, attr-defined]
            no_cursor_timeout=no_cursor_timeout,
            cursor_type=cursor_type,
            sort=sort,
            allow_partial_results=allow_partial_results,
            oplog_replay=oplog_replay,
            batch_size=batch_size,
            collation=collation,
            hint=hint,
            max_scan=max_scan,
            max_time_ms=max_time_ms,
            max=max,
            min=min,
            return_key=return_key,
            show_record_id=show_record_id,
            comment=comment,
            session=session,
            allow_disk_use=allow_disk_use,
        )
        async for mongo_doc in cursor:
            doc_list.append(cls.password_to_none(mongo_doc))
        return doc_list

    @classmethod
    async def find_many_to_raw_docs(
        cls,
        filter=None,
        projection=None,
        skip=0,
        limit=0,
        no_cursor_timeout=False,
        cursor_type=CursorType.NON_TAILABLE,
        sort=None,
        allow_partial_results=False,
        oplog_replay=False,
        batch_size=0,
        collation=None,
        hint=None,
        max_scan=None,
        max_time_ms=None,
        max=None,
        min=None,
        return_key=False,
        show_record_id=False,
        comment=None,
        session=None,
        allow_disk_use=None,
    ) -> list[dict[str, Any]]:
        """Find documents and convert to a raw documents.
        Special changes:
        _id to str
        password to None
        date to str
        datetime to str
        """
        # Get collection for current model.
        collection: AsyncCollection = store.MONGO_DATABASE[cls.META["collection_name"]]  # type: ignore[index, attr-defined]
        # Get documents.
        doc_list: list[dict[str, Any]] = []
        cursor: AsyncCursor = collection.find(
            filter=filter,
            projection=projection,
            skip=skip,
            limit=limit or cls.META["db_query_docs_limit"],  # type: ignore[index, attr-defined]
            no_cursor_timeout=no_cursor_timeout,
            cursor_type=cursor_type,
            sort=sort,
            allow_partial_results=allow_partial_results,
            oplog_replay=oplog_replay,
            batch_size=batch_size,
            collation=collation,
            hint=hint,
            max_scan=max_scan,
            max_time_ms=max_time_ms,
            max=max,
            min=min,
            return_key=return_key,
            show_record_id=show_record_id,
            comment=comment,
            session=session,
            allow_disk_use=allow_disk_use,
        )
        async for mongo_doc in cursor:
            doc_list.append(cls.mongo_doc_to_raw_doc(mongo_doc))  # type: ignore[index, attr-defined]
        return doc_list

    @classmethod
    async def find_many_to_json(
        cls,
        filter=None,
        projection=None,
        skip=0,
        limit=0,
        no_cursor_timeout=False,
        cursor_type=CursorType.NON_TAILABLE,
        sort=None,
        allow_partial_results=False,
        oplog_replay=False,
        batch_size=0,
        collation=None,
        hint=None,
        max_scan=None,
        max_time_ms=None,
        max=None,
        min=None,
        return_key=False,
        show_record_id=False,
        comment=None,
        session=None,
        allow_disk_use=None,
    ) -> str | None:
        """Find documents and convert to a json string."""
        # Get collection for current model.
        collection: AsyncCollection = store.MONGO_DATABASE[cls.META["collection_name"]]  # type: ignore[index, attr-defined]
        # Get documents.
        doc_list: list[dict[str, Any]] = []
        cursor: AsyncCursor = collection.find(
            filter=filter,
            projection=projection,
            skip=skip,
            limit=limit or cls.META["db_query_docs_limit"],  # type: ignore[index, attr-defined]
            no_cursor_timeout=no_cursor_timeout,
            cursor_type=cursor_type,
            sort=sort,
            allow_partial_results=allow_partial_results,
            oplog_replay=oplog_replay,
            batch_size=batch_size,
            collation=collation,
            hint=hint,
            max_scan=max_scan,
            max_time_ms=max_time_ms,
            max=max,
            min=min,
            return_key=return_key,
            show_record_id=show_record_id,
            comment=comment,
            session=session,
            allow_disk_use=allow_disk_use,
        )
        async for mongo_doc in cursor:
            doc_list.append(cls.mongo_doc_to_raw_doc(mongo_doc))  # type: ignore[index, attr-defined]
        return json.dumps(doc_list) if len(doc_list) > 0 else None

    @classmethod
    async def delete_many(
        cls, filter, collation=None, hint=None, session=None, let=None, comment=None
    ) -> DeleteResult:
        """Find documents matching with Model."""
        # Raises a panic if the Model cannot be removed.
        if not cls.META["is_delete_doc"]:  # type: ignore[index, attr-defined]
            msg = (
                f"Model: `{cls.META["full_model_name"]}` > "  # type: ignore[index, attr-defined]
                + "META param: `is_delete_doc` (False) => "
                + "Documents of this Model cannot be removed from the database!"
            )
            raise PanicError(msg)
        # Get collection for current model.
        collection: AsyncCollection = store.MONGO_DATABASE[cls.META["collection_name"]]  # type: ignore[index, attr-defined]
        # Delete documents.
        result: DeleteResult = await collection.delete_many(
            filter=filter,
            collation=collation,
            hint=hint,
            session=session,
            let=let,
            comment=comment,
        )
        return result
