from api.auth.keycloak_client import Keycloak
from api.route import Route
from api_graphql.abc.query_abc import QueryABC
from api_graphql.abc.sort_abc import Sort
from api_graphql.field.collection_field_builder import CollectionFieldBuilder
from api_graphql.field.dao_field_builder import DaoFieldBuilder
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
from api_graphql.filter.api_key_filter import ApiKeyFilter
from api_graphql.filter.domain_filter import DomainFilter
from api_graphql.filter.group_filter import GroupFilter
from api_graphql.filter.permission_filter import PermissionFilter
from api_graphql.filter.role_filter import RoleFilter
from api_graphql.filter.short_url_filter import ShortUrlFilter
from api_graphql.filter.user_filter import UserFilter
from api_graphql.filter.user_space_filter import UserSpaceFilter
from api_graphql.require_any_resolvers import (
    by_group_assignment_resolver,
    by_user_space_assignment_resolver,
    has_assigned_user_spaces,
)
from data.schemas.administration.api_key import ApiKey
from data.schemas.administration.api_key_dao import apiKeyDao
from data.schemas.administration.user import User
from data.schemas.administration.user_dao import userDao
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.public.domain import Domain
from data.schemas.public.domain_dao import domainDao
from data.schemas.public.group import Group
from data.schemas.public.group_dao import groupDao
from data.schemas.public.short_url import ShortUrl
from data.schemas.public.short_url_dao import shortUrlDao
from data.schemas.public.user_setting import UserSetting
from data.schemas.public.user_setting_dao import userSettingDao
from data.schemas.public.user_space import UserSpace
from data.schemas.public.user_space_dao import userSpaceDao
from data.schemas.system.feature_flag_dao import featureFlagDao
from data.schemas.system.setting_dao import settingDao
from service.permission.permissions_enum import Permissions


class Query(QueryABC):
    def __init__(self):
        QueryABC.__init__(self, "Query")

        self.field(
            DaoFieldBuilder("apiKeys")
            .with_dao(apiKeyDao)
            .with_filter(ApiKeyFilter)
            .with_sort(Sort[ApiKey])
            .with_require_any_permission([Permissions.api_keys])
        )

        self.field(
            DaoFieldBuilder("permissions")
            .with_dao(permissionDao)
            .with_filter(PermissionFilter)
            .with_sort(Sort[Permission])
        )

        self.field(
            DaoFieldBuilder("roles")
            .with_dao(roleDao)
            .with_filter(RoleFilter)
            .with_sort(Sort[Role])
            .with_require_any(
                [
                    Permissions.roles,
                    Permissions.users_create,
                    Permissions.users_update,
                    Permissions.groups_create,
                    Permissions.groups_update,
                ],
                [has_assigned_user_spaces],
            )
        )

        self.field(
            DaoFieldBuilder("users")
            .with_dao(userDao)
            .with_filter(UserFilter)
            .with_sort(Sort[User])
            .with_require_any_permission([Permissions.users])
        )

        self.field(
            ResolverFieldBuilder("ping").with_resolver(lambda *_: "Pong").with_public()
        )

        self.field(ResolverFieldBuilder("user").with_resolver(self._get_user))
        self.field(
            ResolverFieldBuilder("userHasPermission").with_resolver(
                self._has_permission
            )
        )
        self.field(
            ResolverFieldBuilder("userHasAnyPermission").with_resolver(
                self._has_any_permission
            )
        )
        self.field(
            ResolverFieldBuilder("notExistingUsersFromKeycloak")
            .with_resolver(self._get_non_users_from_keycloak)
            .with_require_any_permission([Permissions.users_create])
        )

        self.field(
            DaoFieldBuilder("domains")
            .with_dao(domainDao)
            .with_filter(DomainFilter)
            .with_sort(Sort[Domain])
            .with_require_any(
                [
                    Permissions.domains,
                    Permissions.domains_create,
                    Permissions.domains_update,
                ],
                [has_assigned_user_spaces],
            )
        )

        self.field(
            CollectionFieldBuilder("assignedUserSpaces").with_resolver(
                self._resolve_assigned_user_spaces
            )
        )

        self.field(
            DaoFieldBuilder("userSpaces")
            .with_dao(userSpaceDao)
            .with_filter(UserSpaceFilter)
            .with_sort(Sort[UserSpace])
            .with_require_any(
                [
                    Permissions.user_spaces,
                ],
                [lambda ctx: all(x.owner_id == ctx.user.id for x in ctx.data.nodes)],
            )
        )

        self.field(
            DaoFieldBuilder("groups")
            .with_dao(groupDao)
            .with_filter(GroupFilter)
            .with_sort(Sort[Group])
            .with_require_any(
                [
                    Permissions.groups,
                    Permissions.short_urls_create,
                    Permissions.short_urls_update,
                ],
                [by_user_space_assignment_resolver, by_group_assignment_resolver],
            )
        )

        self.field(
            DaoFieldBuilder("shortUrls")
            .with_dao(shortUrlDao)
            .with_filter(ShortUrlFilter)
            .with_sort(Sort[ShortUrl])
            .with_require_any(
                [Permissions.short_urls],
                [by_user_space_assignment_resolver, by_group_assignment_resolver],
            )
        )

        self.field(
            ResolverFieldBuilder("settings")
            .with_resolver(self._resolve_settings)
            .with_direct_result()
            .with_public(True)
        )
        self.field(
            ResolverFieldBuilder("userSettings")
            .with_resolver(self._resolve_user_settings)
            .with_direct_result()
        )
        self.field(
            ResolverFieldBuilder("featureFlags")
            .with_resolver(self._resolve_feature_flags)
            .with_direct_result()
        )

    @staticmethod
    async def _get_user(*_):
        return await Route.get_user()

    @staticmethod
    async def _has_permission(*_, **kwargs):
        if "permission" not in kwargs:
            raise ValueError("Missing permission argument")

        user = await Route.get_user()
        return await user.has_permission(kwargs["permission"])

    @staticmethod
    async def _has_any_permission(*_, **kwargs):
        if "permissions" not in kwargs:
            raise ValueError("Missing permissions argument")

        user = await Route.get_user()
        return any([await user.has_permission(x) for x in kwargs["permissions"]])

    @staticmethod
    async def _get_non_users_from_keycloak(*_):
        kc_users = [x for x in Keycloak.admin.get_users({})]
        existing_user_keycloak_ids = [x.keycloak_id for x in await userDao.get_all()]

        return [
            {"keycloakId": x["id"], "username": x["username"]}
            for x in kc_users
            if x["id"] not in existing_user_keycloak_ids
        ]

    @staticmethod
    async def _resolve_settings(*args, **kwargs):
        if "key" in kwargs:
            return [await settingDao.find_by_key(kwargs["key"])]
        return await settingDao.get_all()

    @staticmethod
    async def _resolve_user_settings(*args, **kwargs):
        user = await Route.get_user()
        if user is None:
            return None

        if "key" in kwargs:
            return await userSettingDao.find_by(
                [{UserSetting.user_id: user.id}, {UserSetting.key: kwargs["key"]}]
            )
        return await userSettingDao.find_by({UserSetting.user_id: user.id})

    @staticmethod
    async def _resolve_feature_flags(*args, **kwargs):
        if "key" in kwargs:
            return [await featureFlagDao.find_by_key(kwargs["key"])]
        return await featureFlagDao.get_all()

    @staticmethod
    async def _resolve_default_user_filter(*args, **kwargs) -> dict:
        return {"user": {"id": {"equal": (await Route.get_user()).id}}}

    @staticmethod
    async def _resolve_assigned_user_spaces(*args, **kwargs):
        user = await Route.get_user()
        if user is None:
            return []

        return await userSpaceDao.get_assigned_by_user_id(user.id)
