from __future__ import annotations

from functools import wraps

from qbittorrentapi.app import AppAPIMixIn
from qbittorrentapi.definitions import APIKwargsT
from qbittorrentapi.definitions import APINames
from qbittorrentapi.definitions import ClientCache
from qbittorrentapi.definitions import List
from qbittorrentapi.definitions import ListEntry
from qbittorrentapi.definitions import ListInputT


class LogPeer(ListEntry):
    """Item in :class:`LogPeersList`"""


class LogPeersList(List[LogPeer]):
    """Response for :meth:`~LogAPIMixIn.log_peers`"""

    def __init__(self, list_entries: ListInputT, client: LogAPIMixIn | None = None):
        super().__init__(list_entries, entry_class=LogPeer)


class LogEntry(ListEntry):
    """Item in :class:`LogMainList`"""


class LogMainList(List[LogEntry]):
    """Response to :meth:`~LogAPIMixIn.log_main`"""

    def __init__(self, list_entries: ListInputT, client: LogAPIMixIn | None = None):
        super().__init__(list_entries, entry_class=LogEntry)


class LogAPIMixIn(AppAPIMixIn):
    """
    Implementation of all ``Log`` API methods.

    :Usage:
        >>> from qbittorrentapi import Client
        >>> client = Client(host="localhost:8080", username="admin", password="adminadmin")
        >>> client.log_main(info=False)
        >>> client.log_peers()
    """

    @property
    def log(self) -> Log:
        """
        Allows for transparent interaction with Log endpoints.

        See Log class for usage.
        """
        if self._log is None:
            self._log = Log(client=self)
        return self._log

    def log_main(
        self,
        normal: bool | None = None,
        info: bool | None = None,
        warning: bool | None = None,
        critical: bool | None = None,
        last_known_id: str | int | None = None,
        **kwargs: APIKwargsT,
    ) -> LogMainList:
        """
        Retrieve the qBittorrent log entries. Iterate over returned object.

        :param normal: False to exclude ``normal`` entries
        :param info: False to exclude ``info`` entries
        :param warning: False to exclude ``warning`` entries
        :param critical: False to exclude ``critical`` entries
        :param last_known_id: only entries with an ID greater than this value will be returned
        """
        params = {
            "normal": None if normal is None else bool(normal),
            "info": None if info is None else bool(info),
            "warning": None if warning is None else bool(warning),
            "critical": None if critical is None else bool(critical),
            "last_known_id": last_known_id,
        }
        return self._get_cast(
            _name=APINames.Log,
            _method="main",
            params=params,
            response_class=LogMainList,
            **kwargs,
        )

    def log_peers(
        self,
        last_known_id: str | int | None = None,
        **kwargs: APIKwargsT,
    ) -> LogPeersList:
        """
        Retrieve qBittorrent peer log.

        :param last_known_id: only entries with an ID greater than this value will be
            returned
        """
        params = {"last_known_id": last_known_id}
        return self._get_cast(
            _name=APINames.Log,
            _method="peers",
            params=params,
            response_class=LogPeersList,
            **kwargs,
        )


class Log(ClientCache[LogAPIMixIn]):
    """
    Allows interaction with ``Log`` API endpoints.

    :Usage:
        >>> from qbittorrentapi import Client
        >>> client = Client(host="localhost:8080", username="admin", password="adminadmin")
        >>> # this is all the same attributes that are available as named in the
        >>> #  endpoints or the more pythonic names in Client (with or without 'log_' prepended)
        >>> log_list = client.log.main()
        >>> peers_list = client.log.peers(last_known_id="...")
        >>> # can also filter log down with additional attributes
        >>> log_info = client.log.main.info(last_known_id=1)
        >>> log_warning = client.log.main.warning(last_known_id=1)
    """

    def __init__(self, client: LogAPIMixIn):
        super().__init__(client=client)
        self.main = Log._Main(client=client)

    @wraps(LogAPIMixIn.log_peers)
    def peers(
        self,
        last_known_id: str | int | None = None,
        **kwargs: APIKwargsT,
    ) -> LogPeersList:
        return self._client.log_peers(last_known_id=last_known_id, **kwargs)

    class _Main(ClientCache["LogAPIMixIn"]):
        def _api_call(
            self,
            normal: bool | None = None,
            info: bool | None = None,
            warning: bool | None = None,
            critical: bool | None = None,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._client.log_main(
                normal=normal,
                info=info,
                warning=warning,
                critical=critical,
                last_known_id=last_known_id,
                **kwargs,
            )

        def __call__(
            self,
            normal: bool | None = True,
            info: bool | None = True,
            warning: bool | None = True,
            critical: bool | None = True,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._api_call(
                normal=normal,
                info=info,
                warning=warning,
                critical=critical,
                last_known_id=last_known_id,
                **kwargs,
            )

        def info(
            self,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._api_call(last_known_id=last_known_id, **kwargs)

        def normal(
            self,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._api_call(info=False, last_known_id=last_known_id, **kwargs)

        def warning(
            self,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._api_call(
                info=False,
                normal=False,
                last_known_id=last_known_id,
                **kwargs,
            )

        def critical(
            self,
            last_known_id: str | int | None = None,
            **kwargs: APIKwargsT,
        ) -> LogMainList:
            return self._api_call(
                info=False,
                normal=False,
                warning=False,
                last_known_id=last_known_id,
                **kwargs,
            )
