from typing import Union

from api_library.date_time_actions.date_functions import system_arrow, client_arrow
from databases import (
    BuildDecisionBookProjects,
    BuildDecisionBookProjectPerson,
    BuildDecisionBookPayments,
    OccupantTypes,
)

from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.validations_request import (
    InsertBuildDecisionBookProjects,
    UpdateBuildDecisionBookProjects,
    ApprovalsBuildDecisionBookProjects,
    ListOptions,
)
from api_validations.core_response import AlchemyJsonResponse
from databases import Build, BuildLivingSpace, BuildParts, ApiEnumDropdown
from databases.sql_models.building.decision_book import (
    BuildDecisionBookProjectItems,
    BuildDecisionBookItems,
    BuildDecisionBook,
)


class ProjectDecisionBookListEventMethods(MethodToEvent):

    event_type = "LIST"

    __event_keys__ = {
        "96459b36-37f2-4d5b-8370-c459058d5bce": "project_decision_book_list",
    }

    @classmethod
    def project_decision_book_list(
        cls,
        list_options: ListOptions,
        token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        if isinstance(token_dict, EmployeeTokenObject):
            raise BuildDecisionBookProjects.raise_http_exception(
                status_code="HTTP_403_FORBIDDEN",
                error_case="NOT_ALLOWED",
                message="Employee cannot create project project decision book",
                data={},
            )
        elif isinstance(token_dict, OccupantTokenObject):
            build_decision_book = BuildDecisionBook.filter_one(
                BuildDecisionBook.build_id == token_dict.selected_occupant.build_id,
            ).data
            if not build_decision_book:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="Build decision book not found",
                    data={},
                )
            BuildDecisionBookProjects.filter_attr = list_options
            decision_book_projects = BuildDecisionBookProjects.filter_all(
                BuildDecisionBookProjects.build_decision_book_id
                == build_decision_book.id,
            )
            return AlchemyJsonResponse(
                status_code="HTTP_200_OK",
                message="Project decision book created successfully",
                result=decision_book_projects,
            )


class ProjectDecisionBookCreateEventMethods(MethodToEvent):

    event_type = "CREATE"

    __event_keys__ = {
        "b8e44bb2-f157-4dd5-8a24-0e02db4877c9": "project_decision_book_create",
    }

    @classmethod
    def project_decision_book_create(
        cls,
        data: InsertBuildDecisionBookProjects,
        token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        if isinstance(token_dict, EmployeeTokenObject):
            raise BuildDecisionBookProjects.raise_http_exception(
                status_code="HTTP_403_FORBIDDEN",
                error_case="NOT_ALLOWED",
                message="Employee cannot create project project decision book",
                data={},
            )
        elif isinstance(token_dict, OccupantTokenObject):
            living_space = BuildLivingSpace.filter_one(
                BuildLivingSpace.id == token_dict.selected_occupant.living_space_id,
            ).data
            if not living_space:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="Living space not found",
                    data={},
                )

            occupant_type = OccupantTypes.filter_by_one(
                occupant_category_type="PRJ",
                occupant_code="PRJ-LDR",
                id=living_space.occupant_type,
            ).data
            if not occupant_type:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message=f"{token_dict.selected_occupant.occupant_type_uu_id} occupant type is not allowed, only PRJ-LDR occupant type is allowed",
                    data={},
                )
            decision_book_project_person = BuildDecisionBookProjectPerson.filter_one(
                BuildDecisionBookProjectPerson.living_space_id
                == token_dict.selected_occupant.living_space_id,
            )
            return AlchemyJsonResponse(
                status_code="HTTP_200_OK",
                message="Project decision book created successfully",
                result=decision_book_project_person,
            )


class ProjectDecisionBookUpdateEventMethods(MethodToEvent):

    event_type = "UPDATE"

    __event_keys__ = {
        "bfe3ef13-030f-495f-b692-94bcb746d700": "project_decision_book_update",
    }

    @classmethod
    def project_decision_book_update(
        cls,
        data: UpdateBuildDecisionBookProjects,
        token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        if isinstance(token_dict, EmployeeTokenObject):
            raise BuildDecisionBookProjects.raise_http_exception(
                status_code="HTTP_403_FORBIDDEN",
                error_case="NOT_ALLOWED",
                message="Employee cannot create project project decision book",
                data={},
            )
        elif isinstance(token_dict, OccupantTokenObject):
            living_space = BuildLivingSpace.filter_one(
                BuildLivingSpace.id == token_dict.selected_occupant.living_space_id,
            ).data
            if not living_space:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="Living space not found",
                    data={},
                )
            occupant_type = OccupantTypes.filter_by_one(
                occupant_category_type="PRJ",
                occupant_code="PRJ-LDR",
                id=living_space.occupant_type,
            ).data
            if not occupant_type:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message=f"{token_dict.selected_occupant.occupant_type_uu_id} occupant type is not allowed, only PRJ-LDR occupant type is allowed",
                    data={},
                )
            decision_book_project_person = BuildDecisionBookProjectPerson.filter_one(
                BuildDecisionBookProjectPerson.build_decision_book_project_uu_id
                == data.build_decision_book_project_uu_id,
                BuildDecisionBookProjectPerson.living_space_id
                == token_dict.selected_occupant.living_space_id,
            ).data
            if not decision_book_project_person:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="This project is not allowed for this occupant",
                    data={},
                )
            decision_book_project = BuildDecisionBookProjects.filter_one(
                BuildDecisionBookProjects.id
                == decision_book_project_person.build_decision_book_project_id,
            )
            if decision_book_project.is_completed:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_403_FORBIDDEN",
                    error_case="NOT_ALLOWED",
                    message="Project decision book is closed. No modification is allowed",
                    data={},
                )
            data_dict = data.excluded_dump()
            decision_book_project.update(**data_dict)
            return AlchemyJsonResponse(
                status_code="HTTP_200_OK",
                message="Project decision book created successfully",
                result=decision_book_project_person,
            )


class ProjectDecisionBookApprovalEventMethods(MethodToEvent):

    event_type = "UPDATE"

    __event_keys__ = {
        "a83a83fe-8446-4c60-9ae5-d1c06adbf626": "project_decision_book_approval",
    }

    @classmethod
    def project_decision_book_approval(
        cls,
        data: ApprovalsBuildDecisionBookProjects,
        token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        BuildDecisionBookPayments.client_arrow = client_arrow
        BuildDecisionBookPayments.client_arrow.timezone = token_dict.timezone or "GMT+3"
        if isinstance(token_dict, EmployeeTokenObject):
            raise BuildDecisionBookProjects.raise_http_exception(
                status_code="HTTP_403_FORBIDDEN",
                error_case="NOT_ALLOWED",
                message="Employee cannot create project project decision book",
                data={},
            )
        elif isinstance(token_dict, OccupantTokenObject):
            living_space = BuildLivingSpace.filter_one(
                BuildLivingSpace.id == token_dict.selected_occupant.living_space_id,
            ).data
            if not living_space:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="Living space not found",
                    data={},
                )
            occupant_type = OccupantTypes.filter_by_one(
                system=True,
                occupant_category_type="PRJ",
                occupant_code="PRJ-LDR",
                id=living_space.occupant_type,
            ).data
            if not occupant_type:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message=f"{token_dict.selected_occupant.occupant_type_uu_id} occupant type is not allowed, only PRJ-LDR occupant type is allowed",
                    data={},
                )
            decision_book_project_person = BuildDecisionBookProjectPerson.filter_one(
                BuildDecisionBookProjectPerson.build_decision_book_project_uu_id
                == data.build_decision_book_project_uu_id,
                BuildDecisionBookProjectPerson.living_space_id
                == token_dict.selected_occupant.living_space_id,
            ).data
            if not decision_book_project_person:
                raise BuildDecisionBookProjectPerson.raise_http_exception(
                    status_code="HTTP_404_NOT_FOUND",
                    error_case="NOT_FOUND",
                    message="This project is not allowed for this occupant",
                    data={},
                )

            decision_book_project = BuildDecisionBookProjects.filter_one(
                BuildDecisionBookProjects.id
                == decision_book_project_person.build_decision_book_project_id,
            ).data
            if decision_book_project.is_completed:
                raise BuildDecisionBookProjects.raise_http_exception(
                    status_code="HTTP_403_FORBIDDEN",
                    error_case="NOT_ALLOWED",
                    message="Project decision book is closed. No modification is allowed",
                    data={},
                )
            data_dict = data.excluded_dump()
            data_dict["is_completed"] = True

            build_parts_list = BuildParts.filter_all(
                BuildParts.human_livable == True,
                BuildParts.build_id == token_dict.selected_occupant.build_id,
            ).data

            decision_book_project_item = BuildDecisionBookItems.filter_one(
                BuildDecisionBookItems.id
                == decision_book_project.build_decision_book_item_id
            ).data

            book_payment_dict = dict(
                build_decision_book_item_id=decision_book_project_item.id,
                build_decision_book_item_uu_id=str(decision_book_project_item.uu_id),
                currency=decision_book_project.currency,
            )
            payment_type = ApiEnumDropdown.get_debit_search(search_debit="DT-D")
            for final_price in data.final_price_list or []:
                for build_part_single in build_parts_list:
                    local_date = BuildDecisionBookPayments.client_arrow.get(
                        str(final_price["date"])
                    )
                    local_date = system_arrow.get(local_date)
                    payment_amount = abs(float(final_price["price"])) * -1
                    created_book_payment = BuildDecisionBookPayments.find_or_create(
                        build_parts_id=build_part_single.id,
                        build_parts_uu_id=str(build_part_single.uu_id),
                        payment_amount=payment_amount,
                        payment_types_id=payment_type.id,
                        payment_types_uu_id=str(payment_type.uu_id),
                        process_date=str(local_date),
                        process_date_m=int(local_date.month),
                        process_date_y=int(local_date.year),
                        payment_plan_time_periods=str(
                            decision_book_project.project_type
                        ),
                        period_time=f"{local_date.year}-{str(local_date.month).zfill(2)}",
                        decision_book_project_id=decision_book_project.id,
                        decision_book_project_uu_id=str(decision_book_project.uu_id),
                        **book_payment_dict,
                    )
                    created_book_payment.save_and_confirm()
            updated_decision_book_project = decision_book_project.update(**data_dict)
            updated_decision_book_project.save_and_confirm()
            return AlchemyJsonResponse(
                status_code="HTTP_200_OK",
                message="Project decision book created successfully",
                result=updated_decision_book_project,
            )


class ProjectDecisionBookPatchEventMethods(MethodToEvent):

    event_type = "PATCH"

    __event_keys__ = {
        "444d67a0-b3a8-4ca2-9d8d-f1acc75011e0": "project_decision_book_patch",
    }

    @classmethod
    def project_decision_book_patch(
        cls,
        data,
        token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        return


ProjectDecisionBookListEventMethod = ProjectDecisionBookListEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/project/list")
)
ProjectDecisionBookCreateEventMethod = ProjectDecisionBookCreateEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/project/create")
)
ProjectDecisionBookUpdateEventMethod = ProjectDecisionBookUpdateEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/project/update")
)
ProjectDecisionBookApprovalEventMethod = ProjectDecisionBookApprovalEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/project/approval")
)
ProjectDecisionBookPatchEventMethod = ProjectDecisionBookPatchEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/project/patch")
)
