import json

from fastapi.routing import APIRouter
from fastapi.requests import Request

from api_validations.validations_request import EndpointValidation
from ApiServices.api_handlers.core_response import HTTPExceptionEvyos
from pydantic import BaseModel


validations_router = APIRouter(prefix="/validations", tags=["Validations"])
validations_router.include_router(validations_router, include_in_schema=True)


class EndpointValidationResponse(BaseModel):
    language: str
    headers: dict
    validation: dict


class ValidationParser:

    def __init__(self, active_validation):
        self.core_validation = active_validation
        self.annotations = (
            active_validation.model_json_schema() if active_validation else None
        )
        self.annotations = json.loads(json.dumps(self.annotations))
        self.schema = {}
        self.parse()

    def parse(self):
        from api_validations.validations_request import PydanticBaseModel, CrudRecords
        properties = self.annotations.get("properties").items()
        for key, value in properties:
            default, title, type_field = value.get("default", None), value.get("title", ""), value.get("type", None)
            field_type, required, possible_types = "string", False, []
            if not type_field:
                for _ in value.get("anyOf") or []:
                    type_opt = json.loads(json.dumps(_))
                    if not type_opt.get("type") == "null":
                        possible_types.append(type_opt.get("type"))
                field_type = possible_types[0]
            else:
                field_type, required = type_field, True
            total_class_annotations = {
                **self.core_validation.__annotations__
                **PydanticBaseModel.__annotations__,
                **CrudRecords.__annotations__,
            }
            attribute_of_class = total_class_annotations.get(key, None)
            if (
                str(attribute_of_class) == "<class 'str'>"
                or str(attribute_of_class) == "typing.Optional[str]"
            ):
                field_type = "string"
                required = not str(attribute_of_class) == "typing.Optional[str]"
            elif (
                str(attribute_of_class) == "<class 'int'>"
                or str(attribute_of_class) == "typing.Optional[int]"
            ):
                field_type = "integer"
                required = not str(attribute_of_class) == "typing.Optional[int]"
            elif (
                str(attribute_of_class) == "<class 'bool'>"
                or str(attribute_of_class) == "typing.Optional[bool]"
            ):
                field_type = "boolean"
                required = not str(attribute_of_class) == "typing.Optional[bool]"
            elif (
                str(attribute_of_class) == "<class 'float'>"
                or str(attribute_of_class) == "typing.Optional[float]"
            ):
                field_type = "float"
                required = not str(attribute_of_class) == "typing.Optional[bool]"
            elif (
                str(attribute_of_class) == "<class 'datetime.datetime'>"
                or str(attribute_of_class) == "typing.Optional[datetime.datetime]"
            ):
                field_type = "datetime"
                required = (
                    not str(attribute_of_class) == "typing.Optional[datetime.datetime]"
                )
            self.schema[key] = {"type": field_type, "required": required, "default": default}

    @classmethod
    def retrieve_validation_from_class(cls, selected_event, events, lang):
        event_function_class = getattr(selected_event, "function_class", None)
        event_function_code = getattr(selected_event, "function_code", None)
        function_class = getattr(events, event_function_class, None)
        event_headers = function_class.retrieve_language_parameters(
            language=lang, function_code=event_function_code
        )
        event_validation = function_class.__event_validation__.get(
            event_function_code, [None]
        )[0]
        if not event_validation:
            raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=lang)
        return event_validation, event_headers


@validations_router.post(path="/validations", summary="Get validations by endpoint")
def validation_by_endpoint(request: Request, validation: EndpointValidation):
    import api_events.events as events

    from api_services.redis.functions import RedisActions
    from databases import EndpointRestriction, Events

    valid_token = RedisActions.get_object_via_access_key(request=request)
    language = str(valid_token.lang).lower()
    if not valid_token:
        raise HTTPExceptionEvyos(error_code="", lang=language)
    endpoint_active = EndpointRestriction.filter_one(
        EndpointRestriction.endpoint_name.ilike(f"%{str(validation.endpoint)}%"),
        system=True,
    ).data
    if not endpoint_active:
        raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language)
    if valid_token.user_type == 1 and not valid_token.selected_company:
        raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language)
    elif valid_token.user_type == 2 and not valid_token.selected_occupant:
        raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language)
    selected_event = Events.filter_one(
        Events.endpoint_id == endpoint_active.id,
        Events.id.in_(valid_token.selected_company.reachable_event_list_id),
    ).data
    if not selected_event:
        raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language)
    active_validation, active_headers = ValidationParser.retrieve_validation_from_class(
        selected_event=selected_event, events=events, lang=language
    )
    if not active_validation:
        raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language)
    validation_parse = ValidationParser(active_validation=active_validation)
    return EndpointValidationResponse(
        language=language,
        headers=active_headers,
        validation=validation_parse.schema,
    )
