import json
from http import HTTPMethod
from typing import Callable, Dict, Generic, Type, TypeVar

from pydantic import BaseModel

from dojocommons.model.app_configuration import AppConfiguration
from dojocommons.model.base_event import BaseEvent
from dojocommons.model.base_resource import BaseResource
from dojocommons.model.response import Response
from dojocommons.service.base_service import BaseService
from dojocommons.exception.business_exception import BusinessException

# Define um TypeVar para o tipo genérico da entidade
T = TypeVar("T", bound=BaseModel)


# noinspection PyArgumentList
class BaseController(Generic[T]):
    """
    A base controller class for handling HTTP requests and delegating
     operations to the appropriate service and resource.

    This class is generic and works with any model that extends
     `pydantic.BaseModel`.

    :param cfg: The application configuration.
    :type cfg: AppConfiguration
    :param service_class: The service class responsible for business logic.
    :type service_class: Type[BaseService[T]]
    :param resource: The resource associated with the controller.
    :type resource: Type[BaseResource]
    :param model_class: The model class associated with the controller.
    :type model_class: Type[T]
    """

    def __init__(
        self,
        cfg: AppConfiguration,
        service_class: Type[BaseService[T]],
        resource: str,
        model_class: Type[T],
    ):
        """
        Initializes the BaseController with the given configuration, service,
         resource, and model class.
        """
        self._service = service_class(cfg)  # type: ignore
        self._resource = resource
        self._model_class = model_class

        # Tipagem explícita para o _strategy
        self._strategy: Dict[HTTPMethod, Callable[[BaseEvent], Response]] = {
            HTTPMethod.GET: self._get,
            HTTPMethod.POST: self._post,
            HTTPMethod.PUT: self._put,
            HTTPMethod.DELETE: self._delete,
        }

    def dispatch(self, event: BaseEvent) -> Response:
        """
        Dispatches the HTTP event to the appropriate handler based on the
         HTTP method.

        :param event: The HTTP event to be processed.
        :type event: BaseEvent
        :return: The response generated by the handler.
        :rtype: Response
        """
        print("[DEBUG][dispatch] Entrou em dispatch")
        method = self._strategy.get(event.http_method)
        if not method:
            return Response(status_code=405, body="Method Not Allowed")
        return method(event)

    def _get(self, event: BaseEvent) -> Response:
        """
        Handles HTTP GET requests.

        :param event: The HTTP event to be processed.
        :type event: BaseEvent
        :return: The response generated by the GET handler.
        :rtype: Response
        """
        print("[DEBUG][_get] Entrou em _get")
        print(f"[DEBUG][_get] event.resource: {event.resource}")
        print(f"[DEBUG][_get] event.resource com repr: {repr(event.resource)}")
        print(f"[DEBUG][_get] self._resource com repr: {repr(self._resource)}")
        print(f"[DEBUG][_get] self._resource: {self._resource}")
        print(f"[DEBUG][_get] self._resource_ID: {getattr(self, '_resource', None)}_ID")
        print(f"[DEBUG][_get] event.resource == self._resource? {event.resource == self._resource}")
        try:
            if event.resource == self._resource:
                print("[DEBUG][_get] Entrou em _list")
                return self._list(event)
            elif event.resource == f"{self._resource}/{{id}}":
                print("[DEBUG][_get] Entrou em _get_by_id")
                return self._get_by_id(event)
            else:
                print("[DEBUG][_get] Nenhuma condição satisfeita")
        except Exception as e:
            print(f"[DEBUG][_get] Exceção: {e}")

        return Response(status_code=404, body="Resource not Found")

    def _list(self, _event: BaseEvent) -> Response:
        """
        Retrieves a list of all entities.

        :param _event: The HTTP event to be processed.
        :type _event: BaseEvent
        :return: A response containing the list of entities.
        :rtype: Response
        """
        print("[DEBUG][Controller] Entrou em _list")
        filters = _event.query_parameters or {}
        entities = self._service.list_all(**filters)
        entity_list = {"items": [entity.model_dump() for entity in entities]}
        return Response(
            status_code=200,
            body=json.dumps(entity_list, ensure_ascii=False, default=str),
        )

    def _get_by_id(self, event: BaseEvent) -> Response:
        """
        Retrieves a single entity by its ID.

        :param event: The HTTP event containing the ID of the entity.
        :type event: BaseEvent
        :return: A response containing the entity or a 404 error if not found.
        :rtype: Response
        """
        entity_id = event.path_parameters.get("id")
        entity = self._service.get_by_id(int(entity_id))
        if not entity:
            return Response(status_code=404, body="Entity not found")
        body = f'{{"item": {entity.model_dump_json(exclude_none=True)}}}'
        return Response(status_code=200, body=body)

    def _post(self, event: BaseEvent) -> Response:
        """
        Creates a new entity.

        :param event: The HTTP event containing the entity data in the body.
        :type event: BaseEvent
        :return: A response containing the created entity.
        :rtype: Response
        """
        try:
            print("[DEBUG][_post] event.body:", event.body)
            entity = self._model_class.model_validate_json(event.body)
            entity = self._service.create(entity)
            body = f'{{"item": {entity.model_dump_json(exclude_none=True)}}}'
            self._service.persist()
            return Response(status_code=201, body=body)
        except BusinessException as e:
            return Response(status_code=e.status_code, body={"error": e.message})
        except Exception as e:
            print(f"[ERROR] Erro ao criar entidade: {str(e)}")
            return Response(
                status_code=500,
                body={"error": "Erro interno ao criar entidade"}
            )

    def _put(self, event: BaseEvent) -> Response:
        """
        Updates an existing entity.

        :param event: The HTTP event containing the entity ID and updated data.
        :type event: BaseEvent
        :return: A response containing the updated entity or a 404 error if not found.
        :rtype: Response
        """
        entity_id = event.path_parameters.get("id")
        existing_entity = self._service.get_by_id(int(entity_id))

        if not existing_entity:
            return Response(status_code=404, body="Entity not found")

        updates = self._model_class.model_validate_json(event.body)
        updated_entity = self._service.update(updates)
        body = f'{{"item": {updated_entity.model_dump_json(exclude_none=True)}}}'
        self._service.persist()
        return Response(status_code=200, body=body)

    def _delete(self, event: BaseEvent) -> Response:
        """
        Deletes an entity by its ID.

        :param event: The HTTP event containing the entity ID.
        :type event: BaseEvent
        :return: A response indicating the deletion status.
        :rtype: Response
        """
        entity_id = event.path_parameters.get("id")
        self._service.delete(int(entity_id))
        self._service.persist()

        return Response(status_code=204, body=None)
    