from uuid import uuid4

from api_graphql.abc.mutation_abc import MutationABC
from api_graphql.input.api_key_create_input import ApiKeyCreateInput
from api_graphql.input.api_key_update_input import ApiKeyUpdateInput
from core.logger import APILogger
from data.schemas.administration.api_key import ApiKey
from data.schemas.administration.api_key_dao import apiKeyDao
from data.schemas.permission.api_key_permission import ApiKeyPermission
from data.schemas.permission.api_key_permission_dao import apiKeyPermissionDao
from service.permission.permissions_enum import Permissions

logger = APILogger(__name__)


class APIKeyMutation(MutationABC):
    def __init__(self):
        MutationABC.__init__(self, "ApiKey")

        self.mutation(
            "create",
            self.resolve_create,
            ApiKeyCreateInput,
            require_any_permission=[Permissions.api_keys_create],
        )
        self.mutation(
            "update",
            self.resolve_update,
            ApiKeyUpdateInput,
            require_any_permission=[Permissions.api_keys_update],
        )
        self.mutation(
            "delete",
            self.resolve_delete,
            require_any_permission=[Permissions.api_keys_delete],
        )
        self.mutation(
            "restore",
            self.resolve_restore,
            require_any_permission=[Permissions.api_keys_delete],
        )

    @staticmethod
    async def resolve_create(obj: ApiKeyCreateInput, *_):
        logger.debug(f"create api key: {obj.__dict__}")

        api_key = ApiKey(
            0,
            obj.identifier,
            str(uuid4()),
        )
        await apiKeyDao.create(api_key)
        api_key = await apiKeyDao.get_by_identifier(api_key.identifier)
        await apiKeyPermissionDao.create_many(
            [ApiKeyPermission(0, api_key.id, x) for x in obj.permissions]
        )
        return api_key

    @staticmethod
    async def resolve_update(obj: ApiKeyUpdateInput, *_):
        logger.debug(f"update api key: {input}")
        api_key = await apiKeyDao.get_by_id(obj.id)

        if obj.permissions is not None:
            permissions = [
                x for x in await apiKeyPermissionDao.get_by_role_id(api_key.id)
            ]

            to_delete = (
                permissions
                if len(obj.permissions) == 0
                else await apiKeyPermissionDao.find_by(
                    [
                        {ApiKeyPermission.api_key_id: api_key.id},
                        {
                            ApiKeyPermission.permission_id: {
                                "notIn": obj.get("permissions", [])
                            }
                        },
                    ]
                )
            )
            permission_ids = [x.permission_id for x in permissions]
            deleted_permission_ids = [
                x.permission_id
                for x in await apiKeyPermissionDao.find_by(
                    [
                        {ApiKeyPermission.api_key_id: api_key.id},
                        {ApiKeyPermission.deleted: True},
                    ]
                )
            ]

            to_create = [
                ApiKeyPermission(0, api_key.id, x)
                for x in obj.permissions
                if x not in permission_ids and x not in deleted_permission_ids
            ]
            to_restore = [
                await apiKeyPermissionDao.get_single_by(
                    [
                        {ApiKeyPermission.api_key_id: api_key.id},
                        {ApiKeyPermission.permission_id: x},
                    ]
                )
                for x in obj.permissions
                if x not in permission_ids and x in deleted_permission_ids
            ]

            if len(to_delete) > 0:
                await apiKeyPermissionDao.delete_many(to_delete)

            if len(to_create) > 0:
                await apiKeyPermissionDao.create_many(to_create)

            if len(to_restore) > 0:
                await apiKeyPermissionDao.restore_many(to_restore)

        return api_key

    @staticmethod
    async def resolve_delete(*_, id: str):
        logger.debug(f"delete api key: {id}")
        api_key = await apiKeyDao.get_by_id(id)
        await apiKeyDao.delete(api_key)
        return True

    @staticmethod
    async def resolve_restore(*_, id: str):
        logger.debug(f"restore api key: {id}")
        api_key = await apiKeyDao.get_by_id(id)
        await apiKeyDao.restore(api_key)
        return True
