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

# %% auto 0
__all__ = ['Account_Access_Share']

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 3
from dataclasses import dataclass, field
from typing import Any, List

import datetime as dt
import re

import httpx

from fastcore.basics import patch_to

import domolibrary.utils.convert as cd
import domolibrary.utils.DictDot as util_dd
import domolibrary.client.DomoAuth as dmda
import domolibrary.client.DomoError as de
import domolibrary.routes.account as account_routes

import domolibrary.utils.chunk_execution as ce

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 4
from domolibrary.routes.account import (
    ShareAccount_V1_AccessLevel,
    ShareAccount_V2_AccessLevel,
    ShareAccount,
    GetAccount_NoMatch,
    ShareAccount_Error,
    ShareAccount_Error_AlreadyShared,
    DeleteAccount_Error,
)

from domolibrary.classes.DomoAccount_Config import (
    AccountConfig_UsesOauth,
    AccountConfig_ProviderTypeNotDefined,
    DomoAccount_Config,
    AccountConfig,
)


# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 5
@dataclass
class Account_Access_Share:
    entity: Any
    access_level: ShareAccount
    auth: dmda.DomoAuth
    domo_group: Any = None

    @staticmethod
    async def _get_entity(obj, is_suppress_get_error: bool = True):
        if obj["type"] == "USER":
            import domolibrary.classes.DomoUser as dmu

            return await dmu.DomoUser.get_by_id(user_id=obj["id"], auth=auth)

        if obj["type"] == "GROUP":
            import domolibrary.classes.DomoGroup as dmg
            try:
                return await dmg.DomoGroup.get_by_id(group_id=obj["id"], auth=auth)
            
            except dmg.SearchGroups_Error as e:
                if not is_suppress_get_error:
                    raise e
                
                print(f"unable to retieve group {obj['id']} from {auth.domo_instance} -- may be a system group")
                return { 'id' : obj['id'], 'type': 'GROUP'}

            

        return None

    @staticmethod
    def _get_access_level(access_level, is_v2: int):
        if is_v2 == 1:
            return ShareAccount_V2_AccessLevel[access_level]

        else:
            return ShareAccount_V1_AccessLevel[access_level]

    @classmethod
    async def _from_json(
        cls: ShareAccount, obj, auth: dmda.DomoAuth, is_v2: bool = False
    ):
        return cls(
            entity=await cls._get_entity(obj),
            auth=auth,
            access_level=cls._get_access_level(obj['accessLevel'], is_v2),
        )
    
    def __eq__(self, other):
        return self.entity.id == other.entity.id and self.entity.__class__.__name__ == other.entity.__class__.__name__


    

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 7
@patch_to(Account_Access)
async def is_feature_accountsv2_enabled(
    self: Account_Access, auth: dmda.DomoFullAuth = None, return_raw: bool = False,
    account_id : int = None
):
    """uses bootstrap class to test if the auth object refers to an instancce that has the account-v2 feature switch enabled"""
    import domolibrary.classes.DomoBootstrap as dmbs

    auth = auth or self.auth or self.account.auth
    account_id = account_id or self.account.id

    domo_bsr = dmbs.DomoBootstrap(auth=auth)

    try:
        is_v2 = await domo_bsr.is_feature_accountsv2_enabled()
        self.is_v2=  1 if is_v2 else 0

    except dmbs.InvalidAuthTypeError as e:
        print(
            f"Warning - unable to test if accounts_v2 feature is enabled in {auth.domo_instance}, recommend pass FullAuth"
        )
        self.is_v2 =  -1
        
    return self.is_v2

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 10
@patch_to(Account_Access)
async def expand_accesslist(self, auth: dmda.DomoAuth = None, account_id: int = None):
    import domolibrary.classes.DomoGroup as dmg
    
    for access in self.accesslist:
        if isinstance(access.entity, dmg.DomoGroup):
            domo_users = await access.entity.Membership.get_members()
            [
                self.accesslist.append(
                    ShareAccount(
                        entity=domo_user,
                        auth=auth,
                        access_level=access.access_level,
                        domo_group=access.entity,
                    )
                )
                for domo_user in domo_users
            ]
    
    return self.accesslist


@patch_to(Account_Access)
async def get_accesslist(
    self: Account_Access,
    auth: dmda.DomoAuth = None,
    account_id: int = None,
    debug_api: bool = False,
    return_raw: bool = False,
    session: httpx.AsyncClient = None,
    is_v2 : int = None
):
    auth = auth or self.auth or (self.account and self.account.auth)
    account_id = account_id or (self.account and self.account.id)

    res = await account_routes.get_account_accesslist(
        auth=auth, account_id=account_id, debug_api=debug_api, session=session
    )

    if return_raw:
        return res

    is_v2 = is_v2 or self.is_v2 or await self.is_feature_accountsv2_enabled(
        account_id=account_id
    )

    self.accesslist = await ce.gather_with_concurrency(
        *[
            Account_Access_Share._from_json(obj=obj, auth=auth, is_v2=is_v2)
            for obj in res.response["list"]
        ],
        n=10
    )
    return self.accesslist

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 14
@patch_to(DomoAccount)
async def _share_v2(
    self: DomoAccount,
    auth: dmda.DomoAuth = None,
    user_id=None,
    group_id=None,
    access_level: ShareAccount = ShareAccount_V2_AccessLevel.CAN_VIEW,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop=2,
    return_raw: bool = False,
    is_suppress_already_shared : bool = True
    
):
    auth = auth or self.auth

    is_v2 = await self.is_feature_accountsv2_enabled(auth=auth)

    if is_v2 == 0:
        raise account_routes.ShareAccount_Error(
            account_id=self.id,
            response="accounts_v2 feature not enabled, use v1 share method",
            domo_instance=auth.domo_instance,
            function_name="_share_v2",
            parent_class=self.__class__.__name__,
            status=None,
        )

    share_payload = account_routes.generate_share_account_payload_v2(
        user_id=user_id,
        group_id=group_id,
        access_level=access_level,
    )
    try:
        res = await account_routes.share_account_v2(
            auth=auth,
            account_id=self.id,
            share_payload=share_payload,
            debug_api=debug_api,
            session=session,
            parent_class=self.__class__.__name__,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw:
            return res

        return f"shared {self.id} - {self.name} with {group_id or user_id}"
    
    except ShareAccount_Error_AlreadyShared as e:
        if not is_suppress_already_shared:
            raise e

        return f"already shared {self.id} - {self.name} with {group_id or user_id}"
        


# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 17
@patch_to(DomoAccount)
async def _share_v1(
    self: DomoAccount,
    auth: dmda.DomoAuth = None,
    user_id=None,
    access_level: ShareAccount = ShareAccount_V1_AccessLevel.CAN_VIEW,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop=2,
    return_raw: bool = False,
    is_suppress_already_shared: bool = True
):
    auth = auth or self.auth

    is_v2 = await self.is_feature_accountsv2_enabled(auth=auth)

    if is_v2 == 1:
        raise account_routes.ShareAccount_Error(
            account_id=self.id,
            response="accounts_v2 feature enabled, use v2 share method",
            domo_instance=auth.domo_instance,
            function_name="_share_v2",
            parent_class=self.__class__.__name__,
            status=None,
        )

    share_payload = account_routes.generate_share_account_payload_v1(
        user_id=user_id,
        access_level=access_level,
    )
    try:
        res = await account_routes.share_account_v1(
            auth=auth,
            account_id=self.id,
            share_payload=share_payload,
            debug_api=debug_api,
            session=session,
            parent_class=self.__class__.__name__,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw:
            return res

        return f"shared {self.id} - {self.name} with { user_id}"
    
    except ShareAccount_Error_AlreadyShared as e:
        if is_suppress_already_shared:
            return f"already shared {self.id} - {self.name} with { user_id}"
        
        else:
            raise e


# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 20
@patch_to(DomoAccount)
async def share(
    self: DomoAccount,
    user_id=None,
    group_id=None,
    domo_user=None,
    domo_group=None,
    auth: dmda.DomoAuth = None,
    access_level: ShareAccount = None,  # will default to Read
    is_suppress_already_shared : bool = True,
    debug_api: bool = False,
    debug_num_stacks_to_drop: int = 3,
    debug_prn: bool = False,
    session: httpx.AsyncClient = None,
):
    auth = auth or self.auth
    is_v2 = await self.is_feature_accountsv2_enabled(auth=auth)

    user_id = user_id or (domo_user and domo_user.id)

    debug = {'is_accounts_v2' : is_v2}


    res = None

    if is_v2 == 1:
        group_id = group_id or (domo_group and domo_group.id)

        debug.update({
            'user_id*' : user_id,
            'group_id' : group_id,
        })

        if debug_prn:
            print(debug)


        res = await self._share_v2(
            auth=auth,
            user_id=user_id,
            group_id=group_id,
            debug_api=debug_api,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            session=session,
            is_suppress_already_shared = is_suppress_already_shared
        )

    elif is_v2 == 0:
        user_ids = [user_id]

        if group_id:
            import domolibrary.classes.DomoGroup as dmdg
            domo_group = await dmdg.DomoGroup.get_by_id(group_id=group_id, auth=auth)
        
        group_id = group_id or domo_group.id
        domo_users = await domo_group.Membership.get_members()
        user_ids = [domo_user.id for domo_user in domo_users]
        
        debug.update({
            'group_id' : group_id,
            'user_ids' : user_ids
        })

        if debug_prn:
            print(debug)


        res = await ce.gather_with_concurrency(
            *[
                self._share_v1(
                    auth=auth,
                    user_id=user_id,
                    debug_api=debug_api,
                    debug_num_stacks_to_drop=debug_num_stacks_to_drop,
                    session=session,
                    is_suppress_already_shared = is_suppress_already_shared
                )
                for user_id in user_ids
            ],
            n=10,
        )

    return res

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 25
@patch_to(DomoAccounts, cls_method=True)
async def upsert_account(
    cls: DomoAccounts,
    auth: dmda.DomoAuth,
    account_config: AccountConfig = None,
    account_name: str = None,
    account_id: str = None,
    debug_api: bool = False,
    debug_prn: bool = False,
    return_raw: bool = False,
    session: httpx.AsyncClient = None,
):
    """search for an account and upsert it"""

    if not account_name and not account_id:
        raise UpsertAccount_MatchCriteria(domo_instance=auth.domo_instance)

    acc = None
    res = None

    if account_id:
        acc = await DomoAccounts.get_accounts(account_id=account_id, auth=auth)

        if acc and account_name:
            if debug_prn:
                print(f"upsertting {acc.id}:  updating account_name")
            res = await acc.update_name(
                account_name=account_name, debug_api=debug_api, return_raw=return_raw
            )

    if account_name and acc is None:
        acc = await DomoAccounts.get_accounts(
            account_name=account_name,
            auth=auth,
            account_type_str=(account_config and account_config.data_provider_type)
            or None,
            # is_suppress_undefined_provider_type = True
        )

        if isinstance(acc, list) and len(acc) > 0 and isinstance(acc[0], DomoAccount):
            acc = acc[0]

        else:
            acc = None

    if acc and account_config:  # upsert account
        acc.config = account_config

        if debug_prn:
            print(f"upsertting {acc.id}:  updating config")

        res = await acc.update_config(debug_api=debug_api, return_raw=return_raw)

    if return_raw and acc:
        return res

    if not acc:
        if debug_prn:
            print(f"creating account {account_name} in {auth.domo_instance}")

        acc = await DomoAccount.create_account(
            account_name=account_name,
            config=account_config,
            auth=auth,
            debug_api=debug_api,
            return_raw=return_raw,
        )

    return acc

# %% ../../nbs/classes/50_DomoAccount_Access.ipynb 28
@patch_to(DomoAccount)
async def upsert_share_account_user(
    self: DomoAccount,
    domo_user,
    auth: dmda.DomoAuth = None,
    is_v2: bool = None,
    access_level: ShareAccount = None,  # will default to Read
    debug_api: bool = False,
    debug_prn: bool = False,
    session: httpx.AsyncClient = None,
):
    auth = auth or self.auth

    ls_share = await account_routes.get_account_accesslist(
        auth=auth, account_id=self.id
    )
    res = None



    if domo_user:
        user_id = domo_user.id
        found_user = next(
            (
                obj
                for obj in ls_share.response["list"]
                if obj["id"] == user_id and obj["type"] == "USER"
            ),
            None,
        )
        if not found_user:
            res = await self.share(
                domo_user=domo_user,
                auth=auth,
                access_level=access_level,
                debug_api=debug_api,
                debug_prn=debug_prn,
                session=session,
            )

    return res


@patch_to(DomoAccount)
async def upsert_share_account_group(
    self: DomoAccount,
    domo_group,
    auth: dmda.DomoAuth = None,
    is_v2: bool = None,
    access_level: ShareAccount = None,  # will default to Read
    debug_api: bool = False,
    debug_prn: bool = False,
    session: httpx.AsyncClient = None,
):
    auth = auth or self.auth

    ls_share = await account_routes.get_account_accesslist_for_v2(
        auth=auth, account_id=self.id
    )
    res = None

    if domo_group:
        group_id = domo_group.id
        found_group = next(
            (
                obj
                for obj in ls_share.response["list"]
                if obj["id"] == group_id and obj["type"] == "GROUP"
            ),
            None,
        )
        if not found_group:
            res = await self.share(
                domo_group=domo_group,
                auth=auth,
                access_level=access_level,
                debug_api=debug_api,
                debug_prn=debug_prn,
                session=session,
            )

    return res
