import typing

from fastapi import status, HTTPException
from fastapi.responses import JSONResponse

from databases import (
    BuildDecisionBook,
    BuildDecisionBookPerson,
    BuildDecisionBookInvitations,
    Users,
    BuildLivingSpace,
    BuildParts,
    BuildDecisionBookPersonOccupants,
    People,
    OccupantTypes,
)

from api_validations.validations_request import (
    ListOptions,
    RemoveDecisionBookPerson,
    DecisionBookDecisionBookInvitationsAttend,
    DecisionBookDecisionBookInvitationsUpdate,
    DecisionBookDecisionBookInvitationsAssign,
)

from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import AlchemyJsonResponse


class DecisionBookPersonListEventMethods(MethodToEvent):

    event_type = "SELECT"
    __event_keys__ = {
        "ea324dc0-3b08-4896-9040-7fa0401a176f": "building_decision_book_person_list",
    }

    @classmethod
    def building_decision_book_person_list(
        cls, data: ListOptions, token_dict: EmployeeTokenObject
    ):
        return


class DecisionBookPersonAddEventMethods(MethodToEvent):

    event_type = "CREATE"
    __event_keys__ = {
        "e346f720-880b-4b07-93d6-9ac76fbbaa33": "building_decision_book_person_add",
    }

    @classmethod
    def building_decision_book_person_add(
        cls,
        data: DecisionBookDecisionBookInvitationsUpdate,
        token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        if isinstance(token_dict, EmployeeTokenObject):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Employee cannot create decision book invitations",
            )
        elif isinstance(token_dict, OccupantTokenObject):
            decision_book = BuildDecisionBook.filter_one(
                BuildDecisionBook.uu_id == data.build_decision_book_uu_id,
            ).data
            if not decision_book:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"No Decision Book is match with given UUID {data.build_decision_book_uu_id}",
                )
            manager_occupant_type = OccupantTypes.filter_by_one(
                system=True, occupant_code="BU-MNG", occupant_category_type="BU"
            ).data
            if (
                not manager_occupant_type.uu_id
                == token_dict.selected_occupant.occupant_type_uu_id
            ):
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Only Build Manager can update the invitation",
                )

            assign_occupant_type = OccupantTypes.filter_by_one(
                system=True,
                uu_id=data.occupant_type_uu_id,
                occupant_category_type="MT",
            ).data
            if not assign_occupant_type:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Occupant type must be a Meeting Type {data.occupant_type_uu_id} is not a MT type occupant",
                )

            manger_book_person = BuildDecisionBookPerson.filter_one(
                BuildDecisionBookPerson.token == data.token,
                BuildDecisionBookPerson.build_decision_book_uu_id
                == data.build_decision_book_uu_id,
            ).data
            if not manger_book_person:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="Manager person not found. Please check token",
                )
            book_invite = BuildDecisionBookInvitations.filter_one(
                BuildDecisionBookInvitations.id == manger_book_person.invite_id,
                BuildDecisionBookInvitations.build_id
                == token_dict.selected_occupant.build_id,
            ).data
            if not book_invite:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="Invitation not found. Please check token",
                )
            selected_book_person = BuildDecisionBookPerson.filter_one(
                BuildDecisionBookPerson.invite_id == book_invite.id,
                BuildDecisionBookPerson.person_uu_id == data.person_uu_id,
            ).data
            if not selected_book_person:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Selected person is not in the invitation list. Please check {data.person_uu_id}",
                )
            selected_book_person.update(
                occupant_type=assign_occupant_type.id,
                occupant_type_uu_id=str(assign_occupant_type.uu_id),
            )
            BuildDecisionBookPerson.save()
            return JSONResponse(
                status_code=status.HTTP_200_OK,
                content={
                    "completed": True,
                    "message": "Invitation is updated",
                    "data": selected_book_person.get_dict(),
                },
            )


class DecisionBookPersonRemoveEventMethods(MethodToEvent):

    event_type = "DELETE"
    __event_keys__ = {
        "30588869-04cd-48ea-ad00-0e4f8dd7f735": "building_decision_book_people_remove",
    }

    @classmethod
    def building_decision_book_people_remove(
        cls, data: RemoveDecisionBookPerson, token_dict: EmployeeTokenObject
    ):
        return


class DecisionBookPersonAttendEventMethods(MethodToEvent):

    event_type = "UPDATE"
    __event_keys__ = {
        "bdcba521-0116-441c-ace1-84c5b68c86c7": "decision_book_invitations_attend",
    }

    @classmethod
    def decision_book_invitations_attend(
        cls,
        data: DecisionBookDecisionBookInvitationsAttend,
        token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
    ):

        if isinstance(token_dict, EmployeeTokenObject):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Employee cannot create decision book invitations",
            )

        token_user = Users.filter_one(Users.id == token_dict.user_id).data
        invitation_person = BuildDecisionBookPerson.filter_one(
            BuildDecisionBookPerson.token == data.token,
        ).data
        if not invitation_person:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Invitation for the this specific user is not found. Please check token",
            )

        # is_token_valid = token_dict.person_id == invitation_person.person_id
        # if not invitation_person.vicarious_person_id:
        #     if not invitation_person or not is_token_valid:
        #         raise HTTPException(
        #             status_code=status.HTTP_404_NOT_FOUND,
        #             detail=f"Invitation for the user {token_user.email} is not valid. Please check token",
        #         )
        # todo check if vicarious person is valid
        invitation = BuildDecisionBookInvitations.filter_one(
            BuildDecisionBookInvitations.id == invitation_person.invite_id,
            BuildDecisionBookInvitations.build_id
            == token_dict.selected_occupant.build_id,
        ).data
        if not invitation:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Invitation not found. Please check invitation uuid : {invitation.uu_id}",
            )
        invitation_person.update(
            is_attending=bool(data.is_attend), vicarious_person_id=None
        )
        BuildDecisionBookInvitations.save()
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content={
                "completed": True,
                "message": "Attendance is updated. Thank you for your response",
                "data": {
                    "is_attending": bool(data.is_attend),
                    "user_uuid": str(token_user.uu_id),
                    "invitation_uuid": str(invitation.uu_id),
                },
            },
        )


class DecisionBookPersonAssignOccupantEventMethods(MethodToEvent):

    event_type = "UPDATE"
    __event_keys__ = {
        "c0b65098-9c79-4212-b1d0-c7e7836cf141": "decision_book_invitations_assign_occupant",
    }

    @classmethod
    def decision_book_invitations_assign_occupant(
        cls,
        data: DecisionBookDecisionBookInvitationsAssign,
        token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
    ):

        if isinstance(token_dict, EmployeeTokenObject):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Employee cannot create decision book invitations",
            )

        book_person_manager = BuildDecisionBookPerson.filter_one(
            BuildDecisionBookPerson.token == data.token,
            BuildDecisionBookPerson.build_living_space_id
            == token_dict.selected_occupant.living_space_id,
        ).data
        manager_occupant_type = OccupantTypes.filter_by_one(
            system=True, occupant_code="BU-MNG", occupant_category_type="BU"
        ).data
        book_person_manager.check_occupant_type(manager_occupant_type)

        # supervisor_occupant_type = OccupantTypes.find_or_abort(occupant_code="BU-SPV", occupant_category_type="BU")
        # book_person_supervisor.check_occupant_type(supervisor_occupant_type)

        invitation = BuildDecisionBookInvitations.filter_one(
            BuildDecisionBookInvitations.id == book_person_manager.invite_id,
            BuildDecisionBookInvitations.build_id
            == token_dict.selected_occupant.build_id,
        ).data
        if not invitation:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Invitation not found. Please check token",
            )

        assign_occupant_type = OccupantTypes.filter_by_one(
            system=True,
            uu_id=data.occupant_type_uu_id,
        ).data
        if not assign_occupant_type:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Occupant type must be a Meeting Type {data.occupant_type_uu_id} is not a MT type occupant",
            )

        build_parts_of_token = BuildParts.filter_all(
            BuildParts.build_id == token_dict.selected_occupant.build_id,
        ).data
        selected_living_space = BuildLivingSpace.filter_one(
            BuildLivingSpace.uu_id == data.build_living_space_uu_id,
            BuildLivingSpace.build_parts_id.in_(
                [build.id for build in build_parts_of_token]
            ),
        ).data
        if not selected_living_space:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Person not found. Please check person uuid",
            )

        book_person_to_assign: BuildDecisionBookPerson = (
            BuildDecisionBookPerson.filter_one(
                BuildDecisionBookPerson.build_living_space_id
                == selected_living_space.id,
                BuildDecisionBookPerson.invite_id == invitation.id,
            ).data
        )
        if not book_person_to_assign:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Person not found in the invitation list. Please check person uuid: {data.build_living_space_uu_id}",
            )
        if not book_person_to_assign.is_attending:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Person is declined the invitation. This person is not attending the meeting. "
                f"Please check person uuid: {data.build_living_space_uu_id}. Invite UUID: {invitation.uu_id}",
            )

        if assign_occupant_type.occupant_code in ("MT-PRS", "MT-WRT"):
            occupant_type_unique = OccupantTypes.filter_by_one(
                system=True,
                occupant_code=assign_occupant_type.occupant_code,
                occupant_category_type="MT",
            ).data
            if assigned_book_person_occupant := BuildDecisionBookPersonOccupants.filter_one(
                BuildDecisionBookPersonOccupants.invite_id == invitation.id,
                BuildDecisionBookPersonOccupants.occupant_type_id
                == occupant_type_unique.id,
            ).data:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Only one person can be assigned to {assign_occupant_type.occupant_code} type"
                    f" {assigned_book_person_occupant.uu_id} is already assigned",
                )

        if assign_occupant_type.occupant_code == "BU-MNG":
            person_occupant_manager = BuildDecisionBookPersonOccupants.filter_one(
                BuildDecisionBookPersonOccupants.invite_id == invitation.id,
                BuildDecisionBookPersonOccupants.occupant_type_id
                == manager_occupant_type.id,
            )
            person_occupant_manager.query.delete()

        book_person_to_assign.add_occupant_type(
            occupant_type=assign_occupant_type,
            build_living_space_id=selected_living_space.id,
        )
        BuildDecisionBookPersonOccupants.save()
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content={
                "completed": True,
                "message": "Invitation is updated",
                "data": book_person_to_assign.get_dict(),
            },
        )


DecisionBookPersonListEventMethod = DecisionBookPersonListEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/people/list")
)
DecisionBookPersonAddEventMethod = DecisionBookPersonAddEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/people/add")
)
DecisionBookPersonRemoveEventMethod = DecisionBookPersonRemoveEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/people/remove")
)
DecisionBookPersonAttendEventMethod = DecisionBookPersonAttendEventMethods(
    action=ActionsSchema(endpoint="/build/decision_book/invitations/attend")
)
DecisionBookPersonAssignOccupantEventMethod = (
    DecisionBookPersonAssignOccupantEventMethods(
        action=ActionsSchema(endpoint="/build/decision_book/invitations/assign")
    )
)

# Build.pre_query = Build.select_action(
#     employee_id=token_dict.selected_company.employee_id,
#     filter_expr=[
#         Build.id == decision_book.build_id,
#     ],
# )
# reachable_building = Build.filter_all()
# if not reachable_building.data:
#     raise HTTPException(
#         status_code=status.HTTP_404_NOT_FOUND,
#         detail=f"No building is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
#     )
#
# Companies.pre_query = Companies.select_action(
#     duty_id_list=[
#         token_dict.selected_company.duty_id,
#         token_dict.selected_company.bulk_duties_id,
#     ],
#     filter_expr=[Companies.id == decision_book.resp_company_id],
# )
# reachable_companies = Companies.filter_all()
# if not reachable_companies.data:
#     raise HTTPException(
#         status_code=status.HTTP_404_NOT_FOUND,
#         detail=f"No company is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
#     )
# person_to_add = People.find_one(uu_id=data.person_uu_id)
# if not person_to_add:
#     raise HTTPException(
#         status_code=status.HTTP_404_NOT_FOUND,
#         detail=f"No person is match with given UUID {data.person_uu_id}",
#     )
# management_typecode = ApiEnumDropdown.filter_all(
#     ApiEnumDropdown.uu_id == data.management_typecode_uu_id,
#     ApiEnumDropdown.enum_class.in_("BuildManagementType", "BuildDuesTypes"),
# )
# if not management_typecode.data:
#     raise HTTPException(
#         status_code=status.HTTP_404_NOT_FOUND,
#         detail=f"No management type is match with given UUID {data.management_typecode_uu_id}",
#     )
# condition_to_met = (
#     data.management_typecode_uu_id
#     in ApiEnumDropdown.get_management_type_codes_list()
# )
# any_type_already = BuildDecisionBookPerson.find_one(
#     build_decision_book_id=decision_book.id,
#     management_typecode=data.management_typecode_uu_id,
# )
# if condition_to_met and any_type_already:
#     raise HTTPException(
#         status_code=status.HTTP_400_BAD_REQUEST,
#         detail=f"Management type is already exist in given Decision Book UUID {data.build_decision_book_uu_id}.",
#     )
#
# data_dict.pop("build_decision_book_uu_id", None)
# data_dict.pop("person_uu_id", None)
# data_dict["management_typecode"] = data_dict.pop(
#     "management_typecode_uu_id", None
# )
# data_dict["build_decision_book_id"] = decision_book.id
# data_dict["person_id"] = person_to_add.id
#
# created = BuildDecisionBookPerson.find_or_create(**data_dict)
# if created.is_found:
#     raise HTTPException(
#         status_code=status.HTTP_400_BAD_REQUEST,
#         detail=f"Decision Book Person is already exist in given Decision Book UUID {data.build_decision_book_uu_id}.",
#     )
# return JSONResponse(
#     status_code=status.HTTP_200_OK,
#     content=dict(
#         message=f"Decision Book Person has added for given Decision Book UUID {data.build_decision_book_uu_id}",
#         completed=True,
#         data=created.get_dict(),
#     ),
# )
