"""Top-level ECM async facade; entry point for all asynchronous DMS and security operations."""
from typing import TYPE_CHECKING, Any

from ecmind_blue_client.ecm._ecm import ECMAbstract
from ecmind_blue_client.ecm._ecm_exception import raise_for_blue_exception
from ecmind_blue_client.ecm.asynchron._ecm_db_async import ECMDbAsync
from ecmind_blue_client.ecm.asynchron._ecm_dms_async import ECMDmsAsync
from ecmind_blue_client.ecm.asynchron._ecm_security_async import ECMSecurityAsync
from ecmind_blue_client.ecm.asynchron._ecm_system_async import ECMSystemAsync
from ecmind_blue_client.pool import AsyncPoolClient
from ecmind_blue_client.rpc._blue_exception import BlueException

if TYPE_CHECKING:
    from ecmind_blue_client import Jobs


class ECMAsync(ECMAbstract[AsyncPoolClient]):
    """Asynchronous high-level ECM API.

    Provides access to folder, register, and document operations via ``ecm.dms``
    and server-level metadata via ``ecm.system``.
    All operations are coroutine-based and intended for use with ``asyncio``.
    """

    def __init__(self, client: AsyncPoolClient) -> None:
        """Initialise the ECM instance with a pool client.

        Args:
            client: The pool client used to communicate with the ECMind Blue server.
        """
        super().__init__(client)
        self.dms: ECMDmsAsync = ECMDmsAsync(self)  # type: ignore[arg-type]
        self.security: ECMSecurityAsync = ECMSecurityAsync(self)  # type: ignore[arg-type]
        self.system: ECMSystemAsync = ECMSystemAsync(self)  # type: ignore[arg-type]
        self.db: ECMDbAsync = ECMDbAsync(self)  # type: ignore[arg-type]

    def impersonate(self, username: str) -> "ECMAsync":
        """Return a user-scoped ``ECMAsync`` instance, usable directly or as an async context manager.

        Creates a new ``ECMAsync`` bound to the same client that injects the
        ``$$$SwitchContextUserName$$$`` parameter into every server request.
        The returned instance supports ``async with`` blocks (via ``__aenter__`` / ``__aexit__``)
        but can equally be used as a plain object without a ``with`` statement — just
        like :func:`open`.

        Args:
            username: The ECMind Blue username to impersonate.

        Returns:
            A new ``ECMAsync`` instance with the user context set to ``username``.

        Example:
            ::

                # as async context manager
                async with ecm.impersonate("john") as ecm_john:
                    folder = await ecm_john.dms.insert_and_get(MyFolder(Name="Test"))

                # direct usage
                ecm_john = ecm.impersonate("john")
                folder = await ecm_john.dms.insert_and_get(MyFolder(Name="Test"))
        """
        instance = ECMAsync(self._client)
        instance._impersonate_username = username  # pylint: disable=protected-access
        return instance

    async def __aenter__(self) -> "ECMAsync":
        return self

    async def __aexit__(self, exc_type, exc_value, traceback) -> None:
        pass

    async def execute(self, job: "Jobs", **kwargs: Any) -> Any:
        """Execute a job on the underlying client, passing kwargs through ``_inject_params``.

        Args:
            job: The ECM job identifier.
            **kwargs: Job parameters forwarded to ``client.execute`` after injection.

        Returns:
            The raw result returned by ``client.execute``.
        """
        try:
            return await self._client.execute(job, **self._inject_params(kwargs))
        except BlueException as exc:
            raise_for_blue_exception(exc)
            raise
