from typing import Any, Union, Callable, Any
from fastapi import status
from fastapi.responses import JSONResponse

from pydantic import BaseModel
from api_validations.validations_request import PydanticBaseModel, BaseModelRegular
from databases.sql_models.response_model import AlchemyResponse
from sqlalchemy.orm import Query
from api_objects.errors.errorHandlers import HTTPExceptionEvyos


class Pagination:
    size: int = 10
    page: int = 1
    orderField: str = "id"
    orderType: str = "asc"
    pageCount: int = 1
    totalCount: int = 1
    totalPage: int = 1

    def change(
        self,
        page: int = 1,
        size: int = 10,
        order_field: str = "id",
        order_type: str = "asc",
    ):
        self.size = size if 10 < size < 40 else 10
        self.page = page or self.page
        self.size = size or self.size
        self.orderField = order_field or self.orderField
        self.orderType = order_type or self.orderType
        self.setter_page()
        if self.page > self.totalPage:
            self.page = self.totalPage
        elif self.page < 1:
            self.page = 1
        self.setter_page()

    def feed(self, data):
        if isinstance(data, list):
            self.totalCount = len(data)
        elif isinstance(data, AlchemyResponse):
            self.totalCount = data.count
        elif isinstance(data, Query):
            self.totalCount = data.count()
        self.setter_page()

    def setter_page(self):
        self.pageCount = self.size
        self.totalPage = int(round(self.totalCount / self.size, 0))
        remainder = self.totalCount % self.size
        if remainder > 0:
            self.totalPage = int(round(self.totalCount / self.size, 0)) + 1
            if self.page == self.totalPage:
                self.pageCount = remainder

    def as_dict(self):
        return {
            "size": self.size,
            "page": self.page,
            "totalCount": self.totalCount,
            "totalPage": self.totalPage,
            "pageCount": self.pageCount,
            "orderField": self.orderField,
            "orderType": self.orderType,
        }


class SingleAlchemyResponse:
    status_code = "HTTP_200_OK"
    result: AlchemyResponse
    response_model: Any
    message: str
    completed: bool
    cls_object: Any = (None,)

    def __new__(
        cls,
        message: str,
        response_model: Any,
        status_code: str = "HTTP_200_OK",
        result: AlchemyResponse = None,
        completed: bool = True,
        cls_object: Any = None,
    ):
        cls.status_code = getattr(status, status_code, "HTTP_200_OK")
        cls.message = message
        cls.result = result
        cls.completed = completed
        cls.response_model = response_model

        if not isinstance(cls.result, AlchemyResponse):
            HTTPExceptionEvyos(
                lang=cls_object.lang,
                error_code="HTTP_400_BAD_REQUEST",
            )

        if not cls.result.first:
            HTTPExceptionEvyos(
                lang=cls_object.lang,
                error_code="HTTP_400_BAD_REQUEST",
            )

        pagination = Pagination()
        pagination.change(page=1)
        BaseModelRegular(**cls.result.data.get_dict())
        data = cls.result.data.get_dict()
        if cls.response_model:
            data = cls.response_model(**cls.result.data.get_dict()).dump()
        return JSONResponse(
            status_code=cls.status_code,
            content=dict(
                pagination=pagination.as_dict(),
                completed=cls.completed,
                message=cls.message,
                data=data,
            ),
        )


class AlchemyJsonResponse:
    status_code: status
    message: str
    result: AlchemyResponse
    completed: bool
    filter_attributes: Any = None
    response_model: Any = None
    cls_object: Any = None

    def __new__(
        cls,
        message: str,
        status_code: str = "HTTP_200_OK",
        result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
        completed: bool = True,
        response_model: Any = None,
        cls_object: Any = None,
        filter_attributes: Any = None,
    ):
        cls.status_code = getattr(status, status_code, "HTTP_200_OK")
        cls.message = message
        cls.result = result
        cls.completed = completed
        cls.response_model = response_model
        cls.filter_attributes = filter_attributes
        cls.cls_object = cls_object
        pagination = Pagination()

        if cls.result.first:
            HTTPExceptionEvyos(
                lang=cls_object.lang,
                error_code="HTTP_400_BAD_REQUEST",
            )

        if filter_attributes:
            pagination.change(
                page=filter_attributes.page,
                size=filter_attributes.size,
                order_field=filter_attributes.order_field,
                order_type=filter_attributes.order_type,
            )
        data = []
        for data_object in cls.result.data:
            data_dict = data_object.get_dict()
            if cls.response_model:
                data_dict = cls.response_model(**data_object.get_dict()).dump()
            data.append(data_dict)
        pagination.feed(data)
        return JSONResponse(
            status_code=cls.status_code,
            content=dict(
                pagination=pagination.as_dict(),
                message=cls.message,
                completed=cls.completed,
                data=data,
            ),
        )


class ListJsonResponse:
    status_code = "HTTP_200_OK"
    result: list
    message: str
    completed: bool
    filter_attributes: Any
    response_model: Any = (None,)
    cls_object: Any = (None,)

    def __new__(
        cls,
        message: str,
        status_code: str = "HTTP_200_OK",
        result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
        completed: bool = True,
        response_model: Any = None,
        cls_object: Any = None,
        filter_attributes: Any = None,
    ):
        cls.status_code = getattr(status, status_code, "HTTP_200_OK")
        cls.message = message
        cls.result = result
        cls.completed = completed
        cls.filter_attributes = filter_attributes
        cls.response_model: Any = response_model

        if not isinstance(cls.result, list):
            HTTPExceptionEvyos(
                lang=cls_object.lang,
                error_code="HTTP_400_BAD_REQUEST",
            )
        pagination = Pagination()
        pagination.change(page=1)
        data = list(cls.result)
        if cls.response_model:
            data = [
                cls.response_model(**data_object).dump() for data_object in cls.result
            ]
        pagination.feed(data)
        return JSONResponse(
            status_code=cls.status_code,
            content=dict(
                pagination=pagination.as_dict(),
                completed=cls.completed,
                message=cls.message,
                data=cls.result,
            ),
        )


class DictJsonResponse:
    status_code = "HTTP_200_OK"
    result: dict
    message: str
    completed: bool
    filter_attributes: Any
    response_model: Any = (None,)
    cls_object: Any = (None,)

    def __new__(
        cls,
        message: str,
        status_code: str = "HTTP_200_OK",
        result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
        completed: bool = True,
        response_model: Any = None,
        cls_object: Any = None,
        filter_attributes: Any = None,
    ):
        cls.status_code = getattr(status, status_code, "HTTP_200_OK")
        cls.message = message
        cls.result = result
        cls.completed = completed
        cls.filter_attributes = filter_attributes
        cls.response_model: Any = response_model

        if not isinstance(cls.result, dict):
            HTTPExceptionEvyos(
                lang=cls_object.lang,
                error_code="HTTP_400_BAD_REQUEST",
            )

        pagination = Pagination()
        pagination.change(page=1)
        data = cls.result
        if cls.response_model:
            data = cls.response_model(**cls.result).dump()
        return JSONResponse(
            status_code=cls.status_code,
            content=dict(
                pagination=pagination.as_dict(),
                completed=cls.completed,
                message=cls.message,
                data=data,
            ),
        )
