import dataclasses
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Generic, TypeVar

if TYPE_CHECKING:
    from vkbottle.api.abc import ABCAPI
    from vkbottle.dispatch.dispenser.abc import ABCStateDispenser
    from vkbottle.dispatch.handlers import ABCHandler

from vkbottle.modules import logger

from .view import ABCView

Event_contra = TypeVar("Event_contra", list, dict, contravariant=True)
HandlerBasement = TypeVar("HandlerBasement", bound="BaseHandlerBasement")


@dataclasses.dataclass(frozen=True)
class BaseHandlerBasement:
    dataclass: type[Any]
    handler: "ABCHandler"


class ABCRawEventView(ABCView[Event_contra], Generic[Event_contra, HandlerBasement]):
    handlers: dict[Any, list[HandlerBasement]]

    @abstractmethod
    def get_handler_basements(self, event: Event_contra) -> list[HandlerBasement]:
        pass

    @abstractmethod
    def get_event_model(
        self,
        handler_basement: HandlerBasement,
        event: Event_contra,
    ) -> Any:
        pass

    @staticmethod
    @abstractmethod
    def get_event_type(event: Event_contra) -> Any:
        pass

    async def handle_event(
        self,
        event: Event_contra,
        ctx_api: "ABCAPI",
        state_dispenser: "ABCStateDispenser",
    ) -> Any:
        logger.debug("Handling event ({}) with message view", self.get_event_type(event))

        context_variables: dict[str, Any] = {}
        handle_responses: list[Any] = []
        handlers: list["ABCHandler"] = []

        mw_instances = await self.pre_middleware(event, context_variables)
        if mw_instances is None:
            logger.info("Handling stopped, pre_middleware returned error")
            return

        for handler_basement in self.get_handler_basements(event):
            event_model = self.get_event_model(handler_basement, event)

            if isinstance(event_model, dict):
                event_model["ctx_api"] = ctx_api
            else:
                event_model.unprepared_ctx_api = ctx_api  # type: ignore

            result = await handler_basement.handler.filter(event_model, context_variables)
            logger.debug("Handler {} returned {}", handler_basement.handler, result)

            if result is False:
                continue

            elif isinstance(result, dict):
                context_variables.update(result)

            try:
                handler_response = await handler_basement.handler.handle(
                    event_model,
                    **context_variables,
                )
            except Exception as e:
                await self.error_handler.handle(e, event_model, **context_variables)
                continue

            handle_responses.append(handler_response)
            handlers.append(handler_basement.handler)

            return_handler = self.handler_return_manager.get_handler(handler_response)
            if return_handler is not None:
                await return_handler(
                    self.handler_return_manager,
                    handler_response,
                    event_model,
                    context_variables,
                )

            if handler_basement.handler.blocking:
                break

        await self.post_middleware(mw_instances, handle_responses, handlers)


__all__ = ("ABCRawEventView",)
