import typing

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

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

from api_validations.validations_request import (
    DecisionBookDecisionBookInvitationsUpdate,
    DecisionBookDecisionBookInvitations,
)
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
from api_library.date_time_actions.date_functions import system_arrow


class BuildDecisionBookInvitationsListEventMethods(MethodToEvent):

    event_type = "SELECT"
    __event_keys__ = {
        "e2277528-8c9c-4c0c-ae64-3ce80cae664b": "decision_book_invitations_list",
    }

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


class BuildDecisionBookInvitationsCreateEventMethods(MethodToEvent):

    event_type = "CREATE"
    __event_keys__ = {
        "d0bfa20c-841d-421c-98e6-d308f938d16a": "decision_book_invitations_create",
    }

    @classmethod
    def decision_book_invitations_create(
        cls,
        data: DecisionBookDecisionBookInvitations,
        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",
            )
        # Check token posses the occupant type of Build Manager
        occupant_manager = OccupantTypes.filter_by_one(
            system=True, occupant_category_type="BU", occupant_code="BU-MNG"
        ).data
        if not token_dict.selected_occupant.occupant_type_id == occupant_manager.id:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Only Build Manager can create decision book",
            )

        # Check decision book is valid for this token and building
        decision_book = BuildDecisionBook.filter_one(
            BuildDecisionBook.uu_id == data.build_decision_book_uu_id,
            BuildDecisionBook.build_id == token_dict.selected_occupant.build_id,
        ).data
        if not decision_book:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Decision book not found. Please create decision book first",
            )
        occupant_building = Build.filter_one(
            Build.id == token_dict.selected_occupant.build_id
        ).data

        # Check planned decision book date is valid
        if (
            not system_arrow.get(data.planned_date).date()
            >= system_arrow.now().shift(days=1).date()
        ):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Planned date must be greater than today",
            )

        # Create an invitation for specific invitation type to start invite sending process
        planned_date_expires = str(
            system_arrow.get(data.planned_date).shift(days=15).date()
        )
        book_invitation = BuildDecisionBookInvitations.find_or_create(
            build_id=token_dict.selected_occupant.build_id,
            build_uu_id=token_dict.selected_occupant.build_uuid,
            decision_book_id=decision_book.id,
            decision_book_uu_id=str(decision_book.uu_id),
            invitation_type=str(decision_book.decision_type),
            living_part_count=occupant_building.livable_part_count,
            living_part_percentage=0.51,
            message=data.message,
            planned_date=str(system_arrow.get(data.planned_date)),
            planned_date_expires=planned_date_expires,
            expiry_ends=str(system_arrow.get(data.planned_date).shift(days=15).date()),
        )
        book_invitation.save_and_confirm()

        # Get all the parts of the building that is occupant in token
        build_parts = BuildParts.filter_all(
            BuildParts.build_id == occupant_building.id,
        )

        # Get all build living spaces that is found in building with distinct person id
        occupants = OccupantTypes.filter_all(system=True)
        BuildLivingSpace.filter_attr = None
        build_living_spaces_people = (
            BuildLivingSpace.filter_all(
                BuildLivingSpace.build_parts_id.in_(
                    [build_part.id for build_part in build_parts.data]
                ),
                BuildLivingSpace.occupant_type.in_(
                    [occupant.id for occupant in occupants.data]
                ),
            )
            .query.distinct(BuildLivingSpace.person_id)
            .all()
        )
        if not build_living_spaces_people:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="No living spaces found for the building",
            )
        print(
            f"Tnvite UUID : {book_invitation.uu_id} Message : {data.message} Planned date : {data.planned_date}"
        )

        # Send invitation to all the users as attend and update the manager as build manager
        attendance_occupant_type = OccupantTypes.filter_by_one(
            system=True, occupant_code="MT-ATT", occupant_category_type="MT"
        ).data
        manager_occupant_type = OccupantTypes.filter_by_one(
            system=True, occupant_code="BU-MNG", occupant_category_type="BU"
        ).data

        build_decision_book_person_dict = dict(
            build_decision_book_id=decision_book.id,
            build_decision_book_uu_id=str(decision_book.uu_id),
            invite_id=book_invitation.id,
            invite_uu_id=str(book_invitation.uu_id),
            send_date=str(system_arrow.now().date()),
            expiry_starts=decision_book.expiry_starts,
            expiry_ends=decision_book.expiry_ends,
        )
        # Check if the invitation is already created at database
        for build_living_spaces_user in build_living_spaces_people:
            if invite := BuildDecisionBookPerson.filter_one(
                BuildDecisionBookPerson.invite_id == book_invitation.id,
                BuildDecisionBookPerson.build_living_space_id
                == build_living_spaces_user.id,
            ).data:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Invitation already send to {build_living_spaces_user.email} "
                    f"for invite {invite.uu_id} date : {str(book_invitation.planned_date)}",
                )

            invitations_person = BuildDecisionBookPerson.find_or_create(
                **build_decision_book_person_dict,
                build_living_space_id=build_living_spaces_user.id,
                build_living_space_uu_id=str(build_living_spaces_user.uu_id),
                person_id=build_living_spaces_user.person_id,
                token=Users.generate_token(40),
            )
            invitations_person.save_and_confirm()

            invitations_person.add_occupant_type(occupant_type=attendance_occupant_type)
            if invitations_person:
                print(f'"{invitations_person.token}",')
                spaces_user = Users.filter_one(
                    Users.person_id == build_living_spaces_user.person_id,
                ).data
                # print(
                #     f"Invitation is send : {spaces_user.email} "
                #     f"Token : {invitations_person.token} "
                #     f"Send Date : {str(invitations_person.send_date.date())}"
                # )

        manager_living_spaces = BuildLivingSpace.filter_all(
            BuildLivingSpace.person_id == token_dict.person_id,
        )
        manager_people = BuildDecisionBookPerson.filter_all(
            BuildDecisionBookPerson.invite_id == book_invitation.id,
            BuildDecisionBookPerson.build_living_space_id.in_(
                [
                    manager_living_space.id
                    for manager_living_space in manager_living_spaces.data
                ]
            ),
            system=True,
        )
        manager_people_occupants = BuildDecisionBookPersonOccupants.filter_all(
            BuildDecisionBookPersonOccupants.build_decision_book_person_id
            == manager_people.get(1).id,
            system=True,
        )
        if manager_people_occupants.count:
            manager_people_occupants.query.delete()
            BuildDecisionBookPersonOccupants.save()
        if manager_people.count:
            manager_people.query.delete()
            BuildDecisionBookPerson.save()

        if book_person_manager := BuildDecisionBookPerson.find_or_create(
            **build_decision_book_person_dict,
            build_living_space_id=token_dict.selected_occupant.living_space_id,
            build_living_space_uu_id=str(
                token_dict.selected_occupant.living_space_uu_id
            ),
            person_id=token_dict.person_id,
            token=Users.generate_token(40),
        ):
            book_person_manager.save_and_confirm()
            book_person_manager.add_occupant_type(occupant_type=manager_occupant_type)
            print(f"Manager Token : {book_person_manager.token}")
        BuildDecisionBookPerson.save()
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content={
                "completed": True,
                "message": "Invitation are send to people related with building",
                "data": book_invitation.get_dict(),
            },
        )


class BuildDecisionBookInvitationsUpdateEventMethods(MethodToEvent):

    event_type = "UPDATE"
    __event_keys__ = {
        "92413636-53a8-4a05-842c-1485a64e00d1": "decision_book_invitations_attend",
    }

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


BuildDecisionBookInvitationsListEventMethod = (
    BuildDecisionBookInvitationsListEventMethods(
        action=ActionsSchema(endpoint="/build/decision_book/invite/list")
    )
)
BuildDecisionBookInvitationsCreateEventMethod = (
    BuildDecisionBookInvitationsCreateEventMethods(
        action=ActionsSchema(endpoint="/build/decision_book/invite/create")
    )
)
BuildDecisionBookInvitationsUpdateEventMethod = (
    BuildDecisionBookInvitationsUpdateEventMethods(
        action=ActionsSchema(endpoint="/build/decision_book/invite/update")
    )
)
