# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/classes/50_DomoMembership.ipynb.

# %% ../../nbs/classes/50_DomoMembership.ipynb 2
from __future__ import annotations
from typing import List, Any
from dataclasses import dataclass, field

import httpx

from abc import abstractmethod

from nbdev.showdoc import patch_to

import domolibrary.utils.chunk_execution as dmce

import domolibrary.client.DomoAuth as dmda
import domolibrary.client.DomoError as dmde
from domolibrary.client.DomoEntity import DomoSubEntity, Entity_Relation

import domolibrary.routes.group as group_routes


# %% auto 0
__all__ = ['UpdateMembership', 'Membership_Entity', 'Membership', 'GroupMembership']

# %% ../../nbs/classes/50_DomoMembership.ipynb 5
class UpdateMembership(dmde.ClassError):
    def __init__(self, cls_instance, member_name=None, entity_id=None):
        super().__init__(
            entity_id=entity_id,
            cls_instance=cls_instance,
            message=f"unable to alter mmbership {member_name if member_name else ''}",
        )


@dataclass
class Membership_Entity(Entity_Relation):

    def to_json(self):
        import domolibrary.classes.DomoUser as dmdu
        import domolibrary.classes.DomoGroup as dmdg

        if isinstance(self.entity, dmdu.DomoUser):
            return {"type": "USER", "id": str(self.entity.id)}

        if isinstance(self.entity, dmdg.DomoGroup):
            return {"type": "GROUP", "id": int(self.entity.id)}

        raise UpdateMembership(cls_instance=self, entity_id=self.entity.id)


@dataclass
class Membership(DomoSubEntity):

    owners: List[str] = field(default_factory=lambda: [])
    members: List[str] = field(default_factory=lambda: [])

    _add_member_ls: List[str] = field(default_factory=lambda: [])
    _remove_member_ls: List[str] = field(default_factory=lambda: [])

    _add_owner_ls: List[str] = field(default_factory=lambda: [])
    _remove_owner_ls: List[str] = field(default_factory=lambda: [])


    @abstractmethod
    async def get_owners(self):
        pass

    @abstractmethod
    async def get_members(self):
        pass

    @abstractmethod
    async def update(self):
        pass

    def _add_to_list(self, member, list_to_update, relation_type):

        if not isinstance(
            member,
            Membership_Entity,
        ):

            member = Membership_Entity(entity=member, relation_type=relation_type, auth = member.auth)

        if member not in list_to_update:
            list_to_update.append(member)

    def _add_member(self, member):
        return self._add_to_list(member, self._add_member_ls, relation_type = "member")

    def _remove_member(self, member, is_keep_system_group=True):
        """does not remove system groups"""
        import domolibrary.classes.DomoGroup as dmg

        if is_keep_system_group and isinstance(member, dmg.DomoGroup) and member.type == "system":
            return

        return self._add_to_list(member, self._remove_member_ls, relation_type = "member")

    def _add_owner(self, member):
        return self._add_to_list(member, self._add_owner_ls, relation_type = "owner")

    def _remove_owner(self, member, is_keep_system_group=True):
        """does not remove system groups"""
        import domolibrary.classes.DomoGroup as dmg

        if is_keep_system_group and isinstance(member, dmg.DomoGroup) and member.type == "system":
            return  

        return self._add_to_list(member, self._remove_owner_ls, relation_type = "owner")

    def _reset_obj(self):
        self._add_member_ls = []
        self._remove_member_ls = []

        self._add_owner_ls = []
        self._remove_owner_ls = []

    async def _extract_domo_groups_from_list(
        self, entity_ls, session: httpx.AsyncClient
    ):
        import domolibrary.classes.DomoGroup as dmg

        return await dmce.gather_with_concurrency(
            *[
                dmg.DomoGroup.get_by_id(
                    group_id=obj.get("groupId") or obj.get("id"),
                    auth=self.auth,
                    session=session,
                )
                for obj in entity_ls
                if obj.get("type") == "GROUP" or obj.get("groupId")
            ],
            n=60,
        )

    async def _extract_domo_users_from_list(
        self, entity_ls, session: httpx.AsyncClient = None
    ):
        import domolibrary.classes.DomoUser as dmu

        return await dmce.gather_with_concurrency(
            *[
                dmu.DomoUser.get_by_id(
                    user_id=obj.get("userId") or obj.get("id"),
                    session=session,
                    auth=self.auth,
                )
                for obj in entity_ls
                if obj.get("type") == "USER" or obj.get("userId")
            ],
            n=10,
        )

    async def _extract_domo_entities_from_list(
        self,
        entity_ls, 
        relation_type,
        session: httpx.AsyncClient = None
    ):
        session = session or httpx.AsyncClient()

        domo_groups = await self._extract_domo_groups_from_list(
            entity_ls, session=session
        )
        domo_users = await self._extract_domo_users_from_list(
            entity_ls, session=session
        )

        return [Membership_Entity(entity=entity, relation_type=relation_type, auth = entity.auth) for entity in domo_groups + domo_users]

    @staticmethod
    def _list_to_json(entity_ls):
        return [parent.to_json() for parent in entity_ls]

# %% ../../nbs/classes/50_DomoMembership.ipynb 7
@dataclass
class GroupMembership(Membership):

    async def get_owners(
        self,
        return_raw: bool = False,
        debug_api: bool = False,
        session: httpx.AsyncClient = None,
        debug_num_stacks_to_drop: int = 2,
    ):

        res = await group_routes.get_group_owners(
            group_id=self.parent_id,
            auth=self.auth,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )
        if return_raw:
            return res

        owners = await self._extract_domo_entities_from_list(res.response, relation_type = 'OWNER', session = session)
        self.owners = owners

        if self.parent:
            self.parent.owner_id_ls = [owner.entity.id for owner in self.owners]
            self.parent.owner_ls = [owner.entity for owner in self.owners]

        return self.owners

    async def update(
        self,
        update_payload : dict = None,
        debug_api: bool = False,
        session: httpx.AsyncClient = None,
        debug_num_stacks_to_drop: int = 2,
    ):
        res = await group_routes.update_group_membership(
            auth=self.auth,
            update_payload=update_payload,
            group_id=self.parent_id,
            add_member_arr=self._list_to_json(self._add_member_ls),
            remove_member_arr=self._list_to_json(self._remove_member_ls),
            add_owner_arr=self._list_to_json(self._add_owner_ls),
            remove_owner_arr=self._list_to_json(self._remove_owner_ls),
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        self._reset_obj()

        return res

    async def get_members(
        self,
        return_raw: bool = False,
        session: httpx.AsyncClient = None,
        debug_api: bool = False,
        debug_num_stacks_to_drop: int = 2,
    ):

        res = await group_routes.get_group_membership(
            group_id=self.parent_id,
            auth=self.auth,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw:
            return res

        members = await self._extract_domo_entities_from_list(res.response, relation_type = 'MEMBER', session = session)
        self.members = members

        if self.parent:
            self.parent.members_id_ls = [member.entity.id for member in self.members]
            self.parent.members_ls = [member.entity for member in self.members]

        return self.members

# %% ../../nbs/classes/50_DomoMembership.ipynb 14
@patch_to(GroupMembership)
async def add_members(
    self: GroupMembership,
    add_user_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop: int = 2,
):
    self._reset_obj()

    for domo_user in add_user_ls:    
        self._add_member(domo_user)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )

    if return_raw:
        return res

    return await self.get_members(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )


@patch_to(GroupMembership)
async def remove_members(
    self: GroupMembership,
    remove_user_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop: int = 2,
):
    self._reset_obj()

    for domo_user in remove_user_ls:
        self._remove_member(domo_user)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )

    if return_raw:
        return res

    return await self.get_members(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )


@patch_to(GroupMembership)
async def set_members(
    self: GroupMembership,
    user_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop: int = 2,
):
    self._reset_obj()

    user_ls = [
        Membership_Entity(entity=user, relation_type = 'MEMBER',
                          auth = self.auth) for user in user_ls
    ]

    memberships = await self.get_members(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )

    for domo_user in user_ls:
        self._add_member(domo_user)

    for me in memberships:
        if me not in user_ls:
            self._remove_member(me)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )
    if return_raw:
        return res

    return await self.get_members(
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
        session=session,
    )

# %% ../../nbs/classes/50_DomoMembership.ipynb 16
@patch_to(GroupMembership)
async def add_owners(
    self: GroupMembership,
    add_owner_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop: int = 2,
):
    self._reset_obj()

    for domo_user in add_owner_ls:
        self._add_owner(domo_user)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    if return_raw:
        return res

    return await self.get_owners(
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )


@patch_to(GroupMembership)
async def remove_owners(
    self: GroupMembership,
    remove_owner_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop: int = 2,
):
    self._reset_obj()

    for domo_user in remove_owner_ls:
        self._remove_owner(domo_user)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )

    if return_raw:
        return res

    return await self.get_owners(
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )


@patch_to(GroupMembership)
async def set_owners(
    self: GroupMembership,
    owner_ls: List[Any],
    return_raw: bool = False,
    debug_api: bool = False,
    debug_num_stacks_to_drop: int = 2,
    session: httpx.AsyncClient = None,
):
    import domolibrary.classes.DomoGroup as dmdg

    self._reset_obj()
    
    owner_ls = [
        Membership_Entity(entity=owner,
                          relation_type='OWNER',
                          auth=self.auth) for owner in owner_ls
    ]
    
    membership = await self.get_owners()

    for domo_entity in owner_ls:
        self._add_owner(domo_entity)

    for oe in membership:

        # open accounts must have themselves as an owner
        if (
            self.parent
            and self.parent.type == "open"
            and self.parent.id == oe.entity.id
            and isinstance(oe.entity, dmdg.DomoGroup)
        ):
            self._add_owner(oe)
            continue

        if isinstance(oe.entity, dmdg.DomoGroup) and oe.entity.is_system:
            self._add_owner(oe)
            continue

        if oe.entity not in owner_ls:
            self._remove_owner(oe)

    res = await self.update(
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )

    if return_raw:
        return res

    return await self.get_owners(
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop + 1,
    )


@patch_to(GroupMembership)
async def add_owner_manage_all_groups_role(
    self: GroupMembership, debug_api: bool = False, session: httpx.AsyncClient = None
):
    import domolibrary.classes.DomoGroup as dmg

    domo_groups = dmg.DomoGroups(auth=self.auth)

    grant_group = await domo_groups.search_by_name(
        group_name="Grant: Manage all groups",
        is_hide_system_groups=False,
        session=session,
        debug_api=debug_api,
    )

    await self.add_owners(
        add_owner_ls=[grant_group], session=session, debug_api=debug_api
    )

    return await self.get_owners(session = session)
