from core.get_value import get_value
from core.logger import DBLogger
from data.abc.data_seeder_abc import DataSeederABC
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 data.schemas.permission.permission import Permission
from data.schemas.permission.permission_dao import permissionDao
from data.schemas.permission.role import Role
from data.schemas.permission.role_dao import roleDao
from data.schemas.permission.role_permission import RolePermission
from data.schemas.permission.role_permission_dao import rolePermissionDao
from service.permission.permissions_enum import Permissions, PERMISSION_DESCRIPTIONS

logger = DBLogger(__name__)


class PermissionSeeder(DataSeederABC):
    def __init__(self):
        DataSeederABC.__init__(self)

    async def seed(self):
        permissions = await permissionDao.get_all()
        possible_permissions = [permission.value for permission in Permissions]

        if len(permissions) == len(possible_permissions):
            logger.info("Permissions already existing")
            await self._update_missing_descriptions()
            return

        logger.warning("Permissions incomplete")
        permission_names = [permission.name for permission in permissions]
        await permissionDao.create_many(
            [
                Permission(
                    0,
                    permission,
                    get_value(PERMISSION_DESCRIPTIONS, permission, str),
                )
                for permission in possible_permissions
                if permission not in permission_names
            ]
        )
        await self._update_missing_descriptions()

        await self._add_missing_to_role()
        await self._add_missing_to_api_key()

    @staticmethod
    async def _add_missing_to_role():
        admin_role = await roleDao.find_single_by([{Role.id: 1}, {Role.name: "admin"}])
        if admin_role is None:
            return

        admin_permissions = await rolePermissionDao.get_by_role_id(
            admin_role.id, with_deleted=True
        )
        to_assign = [
            RolePermission(0, admin_role.id, permission.id)
            for permission in await permissionDao.get_all()
            if permission.id not in [x.permission_id for x in admin_permissions]
        ]
        await rolePermissionDao.create_many(to_assign)

    @staticmethod
    async def _add_missing_to_api_key():
        admin_api_key = await apiKeyDao.find_single_by(
            [{ApiKey.id: 1}, {ApiKey.identifier: "admin"}]
        )
        if admin_api_key is None:
            return

        admin_permissions = await apiKeyPermissionDao.find_by_api_key_id(
            admin_api_key.id, with_deleted=True
        )
        to_assign = [
            ApiKeyPermission(0, admin_api_key.id, permission.id)
            for permission in await permissionDao.get_all()
            if permission.id not in [x.permission_id for x in admin_permissions]
        ]
        await apiKeyPermissionDao.create_many(to_assign)

    @staticmethod
    async def _update_missing_descriptions():
        permissions = {
            permission.name: permission
            for permission in await permissionDao.find_by(
                [{Permission.description: None}]
            )
        }
        to_update = []

        if len(permissions) == 0:
            return

        for key in PERMISSION_DESCRIPTIONS:
            if key.value not in permissions:
                continue

            permissions[key.value].description = PERMISSION_DESCRIPTIONS[key]
            to_update.append(permissions[key.value])

        if len(to_update) == 0:
            return

        await permissionDao.update_many(to_update)
