# -*- coding: utf-8 -*-
# cython: language_level=3
# Copyright (c) 2021-present VincentRPS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE
"""
The ConnectionState Caches most things during connection.
"""
import asyncio
from collections import OrderedDict
from typing import Any, Callable, Coroutine, List, Tuple, TypeVar, Union

from discord.types.dict import Dict

__all__: List = ["Hold", "ConnectionState"]

T = TypeVar("T")
Coro = Coroutine[Any, Any, T]
CoroFunc = Callable[..., Coro[Any]]


# so there are 2 pretty big problems with this right now,
# 1 it's not async so the db would have to be blocking
# 2 it's repetitive to switch out since you need to switch all cache objects.
class Hold:
    """A hold of cache, easily swapable with a db."""

    def __init__(self):
        self._cache = OrderedDict()

    def view(self) -> List[Dict]:
        self._cache.values()

    def list(self):
        return self._cache.items()

    def new(self, name: str, data: Union[str, int, Dict, Any]):
        self._cache[name] = data

    def edit(self, name: str, data: Union[str, int, Dict, Any]):
        self._cache.update({name: data})

    def get(self, name: str):
        return self._cache.get(name)

    def pop(self, name: str):
        return self._cache.pop(name)

    def reset(self) -> None:
        del self._cache


class ConnectionState:
    """The Connection State

    .. versionadded:: 0.4.0

    .. note::

        The connection state is responsible for caching
        everything, meaning most classes will depend on it.

    .. note::

        .. data:: ConnectionState._speaking

        is planned to be deprecated soon.

    Attributes
    ----------
    _bot_intents: :class:`int`
        The cached bot intents, used for Gateway

    _session_id: :class:`int`
        The Gateway, session id

    _voice_session_id: :class:`int`
        The Voice Gateway Session ID

    _seq: :class:`int`
        The Gateway seq number, can be None.

    app: :class:`Client`
        The bot app

        .. versionadded:: 0.5.0

    _voice_seq: :class:`int`
        The Voice Gateway seq

        .. versionadded:: 0.5.0

    _voice_user_data: :class:`dict`
        The Voice User Data given by the Voice Gateway.

        .. versionadded:: 0.5.0

    _speaking: :class:`bool`
        If the bot is currently speaking, defaults False

        .. versionadded:: 0.5.0

    _said_hello: :class:`bool`
        If the Gateway got a hello or not.

    loop :class:`asyncio.AbstractEventLoop`
        The current loop

    _bot_presences: :class:`list`
        A list of the bots presences

    _bot_status: :class:`str`
        The bot status, e.g. online

    _bot_presence_type: :class:`int`
        The bot presence type, defaults to 0

    listeners: :class:`dict`
        The bot listeners

    shard_count: :class:`int`
        the number of shards.

        .. versionadded:: 0.6.0
    """

    def __init__(self, **options):
        self.guilds = options.get("guild_cache_hold") or Hold()
        self.members = options.get("members_cache_hold") or Hold()
        self.roles = options.get("roles_cache_hold") or Hold()
        self.guild_events = options.get("guild_events_cache_hold") or Hold()
        self.channels = options.get("channel_cache_hold") or Hold()
        self.messages = options.get("messages_cache_hold") or Hold()
        self.stage_instances = options.get("stage_instances_cache_hold") or Hold()
        self._ready: asyncio.Event = asyncio.Event()

        self._bot_intents: int = options.get("intents", 0)
        """The cached bot intents, used for Gateway"""

        self._bot_id: int = None

        self.bot_info = {}

        self._voice_session_id: int = None

        self._seq: int = None
        """The seq number"""

        self.app = options.get("bot", None)
        """The bot app"""

        self._voice_seq: int = None
        """The Voice Gateway seq"""

        self._voice_user_data: Dict = {}
        """The Voice User Data given by the Voice Gateway."""

        self._speaking: bool = False
        """If the bot is currently speaking"""

        self._said_hello: bool = False
        """If the Gateway got a hello or not."""

        self.loop: asyncio.AbstractEventLoop = options.get("loop", None)
        """The current loop"""

        self._bot_presences: list[str, Any] = []
        """The precenses"""

        self._bot_status: str = "online"
        """The status"""

        self._bot_presence_type: int = 0
        """Precense type"""

        self.listeners: dict[str, List[Tuple[asyncio.Future, Callable[..., bool]]]] = {}
        """The listeners"""

        self.shard_count: int = options.get("shard_count", None)
        """The shard count"""

        self.components: dict[str, Any] = {}

        self.prefixed_commands: dict[str, List[CoroFunc]] = {}

        self.application_commands: dict[str, Any] = {}

        self.prefix = options.get("prefix")


def member_cacher(state: ConnectionState, data: Any):
    for member in data:
        state.members.new(member["id"], member)
