import typing

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

from api_library.date_time_actions.date_functions import system_arrow
from databases import (
    Employees,
    BuildParts,
    BuildLivingSpace,
    Services,
    OccupantTypes,
    Event2Employee,
    Event2Occupant,
)
from api_validations.validations_request import (
    RegisterServices2Occupant,
    RegisterServices2Employee,
)
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject


class ServiceBindOccupantEventMethods(MethodToEvent):

    event_type = "UPDATE"
    __event_keys__ = {
        "0d2bc5c9-d4b1-4951-8305-69da4a687fdc": "bind_services_occupant",
    }
    __event_validation__ = {
        "0d2bc5c9-d4b1-4951-8305-69da4a687fdc": RegisterServices2Occupant
    }

    @classmethod
    def bind_services_occupant_system(
        cls, build_living_space_id: int, service_id: int, expires_at: str = None
    ):
        living_space = BuildLivingSpace.filter_one(
            BuildLivingSpace.id == build_living_space_id,
        ).data
        service = Services.filter_one(Services.id == service_id).data
        if not service:
            print("Service is not valid. Service can not be binded")
            return

        if not living_space:
            print("Living Space is not valid. Service is not binded")
            return

        if expires_at:
            expires_at = str(system_arrow.get(expires_at))
        else:
            expires_at = str(system_arrow.get(living_space.expiry_ends))

        occupants_event = Event2Occupant.find_or_create(
            event_service_id=service.id,
            event_service_uu_id=str(service.uu_id),
            build_living_space_id=living_space.id,
            build_living_space_uu_id=str(living_space.uu_id),
            expiry_ends=expires_at,
        )
        occupants_event.save_and_confirm()
        print(f"{service.service_name} is added to occupant {str(living_space.uu_id)}")

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

        if isinstance(token_dict, EmployeeTokenObject):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Employee is not authorized to add service to any occupant",
            )

        occupants_build_part = BuildParts.filter_one(
            BuildParts.uu_id == data.build_part_uu_id,
            BuildParts.build_id == token_dict.selected_occupant.build_id,
        ).data
        if not occupants_build_part:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "User is not authorized to add service to this occupant or flat",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        occupant_occupant_type = OccupantTypes.filter_by_one(
            uu_id=data.occupant_uu_id
        ).data
        if not occupant_occupant_type:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "Occupant Types is not found",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        service = Services.filter_one(Services.uu_id == data.service_uu_id).data
        if not service:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "Service is not found",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        # service_events = Service2Events.filter_all(
        #     Service2Events.service_id == service.id,
        # ).data
        # if not service_events:
        #     raise HTTPException(
        #         status_code=status.HTTP_404_NOT_FOUND,
        #         detail="Service has no events registered. Please contact with your manager",
        #     )

        living_space = BuildLivingSpace.filter_one(
            BuildLivingSpace.build_parts_id == occupants_build_part.id,
            BuildLivingSpace.occupant_types_id == occupant_occupant_type.id,
            BuildLivingSpace.person_id == token_dict.person_id,
        ).data
        if not living_space:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "Living Space is not found with given data. Please check flat and occupant type",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        # event_ids_list = [
        #     {
        #         "build_living_space_id": living_space.id,
        #         "build_living_space_uu_id": str(living_space.uu_id),
        #         "event_id": service_event.event_id,
        #         "event_uu_id": str(service_event.event_uu_id),
        #         "is_confirmed": True,
        #     }
        #     for service_event in service_events
        # ]
        #
        # session_execute = Services.session.execute(
        #     insert(Event2Occupant)
        #     .values(event_ids_list)
        #     .on_conflict_do_nothing(
        #         index_elements=["employee_id", "event_id"],
        #     )
        # )
        # count_row = session_execute.rowcount
        # print(f"{count_row} events are added to employee {str(living_space.uu_id)}")
        # Services.save()


class ServiceBindEmployeeEventMethods(MethodToEvent):

    event_type = "UPDATE"
    __event_keys__ = {
        "50f84023-d8ec-4257-bfce-08ddf077c101": "bind_services_employee_super_user",
    }
    __event_validation__ = {"50f84023-d8ec-4257-bfce-08ddf077c101": None}

    @classmethod
    def bind_services_employee(cls, service_id: int, employee_id: int):
        employee = Employees.filter_by_one(
            id=employee_id, **Employees.valid_record_dict
        ).data
        service = Services.filter_by_one(
            id=service_id, **Services.valid_record_dict
        ).data
        if not service:
            print("Service is not valid. Service can not be binded")
            return

        if not employee:
            print("Employee is not valid. Service is not binded")
            return

        # service_events = Service2Events.filter_all(
        #     Service2Events.service_id == service.id,
        # ).data
        # if not service_events:
        #     raise Exception(
        #         "Service has no events registered. Please contact with your manager"
        #     )

        # event_ids_list = [
        #     {
        #         "employee_id": employee_id,
        #         "employee_uu_id": str(employee.uu_id),
        #         "event_id": service_event.event_id,
        #         "event_uu_id": str(service_event.event_uu_id),
        #         "is_confirmed": True,
        #     }
        #     for service_event in service_events
        # ]
        #
        # session_execute = Services.session.execute(
        #     insert(Event2Employee)
        #     .values(event_ids_list)
        #     .on_conflict_do_nothing(
        #         index_elements=["employee_id", "event_id"],
        #     )
        # )
        # count_row = session_execute.rowcount
        # print(f"{count_row} events are added to employee {employee.uu_id}")
        # for service_event in service_events:
        #     service_event.save_and_confirm()

    @classmethod
    def bind_services_employee_super_user(
        cls,
        data: RegisterServices2Employee,
        token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
    ):
        if isinstance(token_dict, OccupantTokenObject):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Occupant is not authorized to add service to any employee",
            )

        employee = Employees.filter_by_one(
            uu_id=data.employee_uu_id, **Employees.valid_record_dict
        ).data
        if not employee:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "This employee is not authorized to add event to this employee",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        service = Services.filter_by_one(
            uu_id=data.service_uu_id, **Services.valid_record_dict
        ).data
        if not service:
            return JSONResponse(
                content={
                    "completed": False,
                    "message": "Service is not found",
                    "data": {},
                },
                status_code=status.HTTP_404_NOT_FOUND,
            )

        event_of_employee = Event2Employee.find_or_create(
            event_service_id=service.id,
            event_service_uu_id=str(service.uu_id),
            employee_id=employee.id,
            employee_uu_id=str(employee.uu_id),
        )
        event_of_employee.save_and_confirm()
        print(f"{service.service_name} is added to employee {str(employee.uu_id)}")

        # service_events = Service2Events.filter_all(
        #     Service2Events.service_id == service.id,
        # ).data
        # if not service_events:
        #     raise HTTPException(
        #         status_code=status.HTTP_404_NOT_FOUND,
        #         detail="Service has no events registered. Please contact with your manager",
        #     )
        #
        # event_ids_list = [
        #     {
        #         "employee_id": employee.id,
        #         "employee_uu_id": employee.uu_id,
        #         "event_id": service_event.event_id,
        #         "event_uu_id": service_event.event_uu_id,
        #         "is_confirmed": True,
        #     }
        #     for service_event in service_events
        # ]
        #
        # session_execute = Services.session.execute(
        #     insert(Event2Employee)
        #     .values(event_ids_list)
        #     .on_conflict_do_nothing(
        #         index_elements=["employee_id", "event_id"],
        #     )
        # )
        # count_row = session_execute.rowcount
        # if not count_row:
        #     Services.save()
        #     return JSONResponse(
        #         content={
        #             "completed": False,
        #             "message": "No events are added to employee",
        #             "data": {},
        #         },
        #         status_code=status.HTTP_200_OK,
        #     )
        # return JSONResponse(
        #     content={
        #         "completed": True,
        #         "message": f"{count_row} events are added to employee",
        #         "data": {},
        #     },
        #     status_code=status.HTTP_200_OK,
        # )


ServiceBindOccupantEventMethod = ServiceBindOccupantEventMethods(
    action=ActionsSchema(endpoint="/bind/services/occupant")
)
ServiceBindEmployeeEventMethod = ServiceBindEmployeeEventMethods(
    action=ActionsSchema(endpoint="/bind/services/employee")
)
