"""Fill in a module description here"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/LocalAccount/ConfigInstanceAccount.ipynb.

# %% auto 0
__all__ = ['LocalAccount', 'generate_config_instance_accounts_from_df', 'LocalAccountConfig']

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 2
from dataclasses import dataclass, field
from typing import List

import domolibrary.client.DomoAuth as dmda
import domolibrary.classes.DomoAccount as dmac

import domolocal.DJ_Workspace as dlws
import domolocal.DJ_WorkspaceAccount as dlac
import domolocal.core as dlcr
import pandas as pd

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 4
@dataclass
class LocalAccount(dlac.DJ_WorkspaceAccount):
    target_account_name: str = None

    local_auth: dmda.DomoAuth = field(repr=False, default=None)

    dj_account: dlac.DJ_WorkspaceAccount = field(repr=False, default=None)

    is_shared_with_workspace: bool = False
    is_backup_account_valid: bool = False

    testing_err: str = None

    def to_json(self):

        obj = {
            "target_instance": self.target_instance,
            "remote_account_name": self.local_account_name,
            "is_domo_account_exists": self.is_domo_account_exists,
            "is_shared_with_workspace": self.is_shared_with_workspace,
            "is_backup_account_valid": self.is_backup_account_valid,
            "is_valid_full_auth": self.is_valid_full_auth,
            "is_valid_token_auth": self.is_valid_token_auth,
            "err": self.testing_err,
            "target_account_name": self.target_account_name,
        }

        return obj

    @staticmethod
    def _match_domo_account(account_name, domo_accounts: List[dmac.DomoAccount]):
        return next(
            (
                domo_account
                for domo_account in domo_accounts
                if domo_account.name == account_name
            ),
            None,
        )

    @classmethod
    def generate_config_admin_from_instance(
        cls,
        target_instance: str,
        local_auth: dmda.DomoAuth,
        existing_domo_accounts: List[dmac.DomoAccounts],
    ):

        remote_account_name = f"sdk_{target_instance}"
        target_account_name = remote_account_name

        domo_account = cls._match_domo_account(
            account_name=remote_account_name, domo_accounts=existing_domo_accounts
        )

        config = cls(
            remote_account_name=remote_account_name,
            target_account_name=target_account_name,
            target_instance=target_instance,
            local_auth=local_auth,
        )

        config.set_domo_account(domo_account)

        return config

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 5
async def generate_config_instance_accounts_from_df(
    config_df: pd.DataFrame, local_auth: dmda.DomoAuth
) -> List[LocalAccount]:

    existing_domo_accounts = await dmac.DomoAccounts.get_accounts(local_auth)

    return [
        LocalAccount.generate_config_admin_from_instance(
            local_auth=local_auth,
            target_instance=obj["domo_instance"],
            existing_domo_accounts=existing_domo_accounts,
        )
        for obj in config_df.to_dict(orient="records")
    ]

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 7
@dataclass
class LocalAccountConfig(DJA_WorkspaceAccount):
    pass


#     target_instance: str
#     dj_workspace: Any = field(repr=False) #DJ_Workspace

#     local_account_name: str

#     local_creds: dict = field(repr=False, default=None)

#     is_valid_full_auth: bool = None
#     is_valid_token_auth: bool = None

#     _target_token_auth: dmda.DomoAuth = None
#     _target_full_auth: dmda.DomoAuth = None
#     target_auth : dmda.DomoAuth = None


#     domo_username: str = None
#     domo_password: str = field(repr=False, default=None)
#     domo_access_token: str = field(repr=False, default=None)

#     account_config : dmacc.AccountConfig = None

#     def __eq__(self, other):
#         if self.__class__.__name__ != other.__class__.__name__:
#             return False

#         return self.local_account_name == other.local_account_name and self.dj_workspace == other.dj_workspace

#     def __lt__(self, other):
#          return self.local_account_name < other.local_account_names

#     def to_json(self):
#         return {
#             "target_instance": self.target_instance,
#             "local_account_name": self.local_account_name,
#             "is_valid_full_auth": self.is_valid_full_auth,
#             "is_valid_token_auth": self.is_valid_token_auth,
#         }

# @classmethod
# def from_workspace_by_account_name(
#     cls,
#     domo_instance: str,
#     local_account_name : str,
#     dj_workspace: Any,
# ):
#     return cls(
#         target_instance=domo_instance,
#         local_account_name=local_account_name,
#         dj_workspace=dj_workspace,
#     )

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 9
@patch_to(LocalAccountConfig)
async def get_local_creds(self, is_abstract=False):
    """
    attemps to read local creds from workspace by account_name.
    will attempt to share account with workspace
    will throw an exception if account cannot be read from workspace
    """

    retry = 0

    while retry <= 1 and not self.local_creds:
        try:
            creds = dlcr.read_domo_jupyter_account(
                self.local_account_name,
                domojupyter_fn=self.dj_workspace.domojupyter_fn,
                is_abstract=is_abstract,
            )
            self.local_creds = creds
            return self.local_creds

        except (dmde.DomoError, dlcr.DJ_Workspace_NoAuth) as e:
            if retry >= 1:
                self.dj_workspace.fix_no_account.append(self)
                self.is_valid_token_auth = False
                self.is_valid_full_auth = False
                return None

            await self.dj_workspace.add_account_to_jupyter_workspace(
                account_name=self.local_account_name,
                share_group_name=self.dj_workspace.default_share_group_name,
                config=self,
            )
            retry += 1


@patch_to(LocalAccountConfig)
async def test_local_creds_full_auth(
    self,
    skip_retrieve_creds: bool = True,
    debug_prn: bool = False,
):
    """
    validates local_creds have valid credentials
    will update "fix_reset_password" task list in dj_workspace if invalid creds
    """

    if not skip_retrieve_creds or not self.domo_username or not self.domo_password:
        await self.get_local_creds()

        self.domo_username = self.local_creds.get("username")
        self.domo_password = self.local_creds.get("password")

    if not self.domo_username:
        raise LA_NoUsername(self.local_account_name)

    if not self.domo_password:
        raise LA_NoPassword(self.local_account_name)

    target_full_auth = dmda.DomoFullAuth(
        domo_instance=self.target_instance,
        domo_username=self.domo_username,
        domo_password=self.domo_password,
    )

    self._target_full_auth = target_full_auth

    try:
        await target_full_auth.print_is_token()
        self.is_valid_full_auth = True

    except dmde.DomoError as e:
        dlut.printmd(
            f"🤯 test_full_auth for: ***{self.local_account_name}*** returned {e}"
        )

        self.is_valid_full_auth = False
        self.dj_workspace.fix_reset_password.append(self)

    return self.is_valid_full_auth

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 11
@patch_to(LocalAccountConfig)
async def test_local_creds_token_auth(
    self,
    skip_retrieve_creds: bool = False,
    debug_prn: bool = False,
):

    if not skip_retrieve_creds or not self.domo_access_token:
        await self.get_local_creds()

        self.domo_access_token = self.local_creds.get("domoAccessToken")

    if not self.domo_access_token:
        raise LA_NoAccessToken(self.local_account_name)

    target_token_auth = dmda.DomoTokenAuth(
        domo_instance=self.target_instance, domo_access_token=self.domo_access_token
    )

    self._target_token_auth = target_token_auth

    try:
        await target_token_auth.print_is_token()
        self.is_valid_token_auth = True

    except dmde.DomoError as e:
        dlut.printmd(
            f"🤯 test_token_auth for: ***{self.local_account_name}*** returned {e}"
        )
        self.is_valid_token_auth = False
        self.dj_workspace.fix_regenerate_access_token.append(self)

    return self.is_valid_token_auth


@patch_to(LocalAccountConfig)
async def test_local_creds(self):
    """test local token auth and local full auth"""
    return all(
        (
            await dmce.gather_with_concurrency(
                *[
                    self.test_local_creds_full_auth(),
                    self.test_local_creds_token_auth(),
                ],
                n=10,
            )
        )
    )


@patch_to(LocalAccountConfig)
async def generate_target_auth(self, backup_auth: dmda.DomoAuth = None):
    target_auth = None

    if self.is_valid_token_auth:
        target_auth = self._target_token_auth

    if self.is_valid_full_auth:
        target_auth = self._target_full_auth

    if not target_auth:
        target_auth = backup_auth

    if not target_auth:
        raise LA_ValidAuth(
            domo_instance=self.target_instance, account_name=self.local_account_name
        )

    await target_auth.print_is_token()

    self.target_auth = target_auth

    return self.target_auth

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 13
@patch_to(LocalAccountConfig)
async def _search_domo_groups_by_name(
    self, auth: dmda.DomoAuth, group_names: List[str]
):
    await dmdg.DomoGroups.toggle_system_group_visibility(
        is_hide_system_groups=False, auth=auth
    )

    await asyncio.sleep(1)

    domo_groups = await dmce.gather_with_concurrency(
        *[
            dmdg.DomoGroup.search_by_name(group_name=group_name, auth=auth)
            for group_name in group_names
        ],
        n=10,
    )
    await dmdg.DomoGroups.toggle_system_group_visibility(
        is_hide_system_groups=True, auth=auth
    )
    return domo_groups


@patch_to(LocalAccountConfig)
async def _upsert_group(
    self,
    auth: dmda.DomoAuth,
    group_name: str,
    group_owner_names: list[str] = [
        "Role: sie_instance_admin",
        "Role: sie_dde_instance_admin",
    ],
    debug_api: bool = False,
):

    domo_group = await dmdg.DomoGroup.upsert(
        group_name=group_name,
        group_type=dmdg.GroupType_Enum["CLOSED"].value,
        description=f"updated via {dt.date.today()}",
        auth=auth,
        debug_api=debug_api,
    )

    domo_group_owners = await self._search_domo_groups_by_name(
        auth=auth, group_names=group_owner_names
    )

    await domo_group.Membership.add_owners(add_owner_ls=domo_group_owners)

    return domo_group


@patch_to(LocalAccountConfig)
def _generate_account_config(self):
    account_config = dmacc.AccountConfig.domo_access_token.value(
        username=self.domo_username,
        password=self.domo_password,
        domo_access_token=self.domo_access_token,
    )

    return account_config


@patch_to(LocalAccountConfig)
async def _upsert_account_and_share_with_default_group(
    self,
    account_config: dmacc.AccountConfig,
    account_name: str,
    auth: dmda.DomoAuth,
    debug_api: bool = False,
    default_share_group: dmdg.DomoGroup = None,
):

    domo_account = await dmacc.DomoAccounts.upsert_account(
        account_config=account_config,
        account_name=account_name,
        auth=auth,
        debug_api=debug_api,
    )

    if default_share_group:
        share_res = await domo_account._share_v2(group_id=default_share_group.id)

    return domo_account


@patch_to(LocalAccountConfig)
async def _upsert_account(
    self,
    auth: dmda.DomoAuth,
    account_name: str,
    default_share_group_name: str = None,
    default_share_group_owner_names=[
        "Role: sie_instance_admin",
        "Role: sie_dde_instance_admin",
    ],
    debug_api: bool = False,
    is_test: bool = False,
):
    account_config = self._generate_account_config()

    group_name = default_share_group_name or self.dj_workspace.default_share_group_name

    default_share_group = None

    if group_name:
        default_share_group = await self._upsert_group(
            auth=auth,
            group_name=group_name,
            group_owner_names=default_share_group_owner_names
            or self.default_share_group_name,
            debug_api=debug_api,
        )

    if is_test:
        return account_config, default_share_group

    domo_account = await self._upsert_account_and_share_with_default_group(
        auth=auth,
        account_config=account_config,
        account_name=account_name,
        default_share_group=default_share_group,
        debug_api=debug_api,
    )

    return domo_account


@patch_to(LocalAccountConfig)
async def upsert_local_account(self, is_test=True, debug_api: bool = False):
    try:
        return await self._upsert_account(
            auth=self.dj_workspace.local_auth,
            account_name=self.local_account_name,
            is_test=is_test,
            debug_api=debug_api,
        )
    except dmde.DomoError as e:
        print(e)
        return None


@patch_to(LocalAccountConfig)
async def upsert_target_account(
    self,
    target_account_name: str,
    backup_auth: dmda.DomoAuth = None,
    is_test=True,
    debug_api: bool = False,
):
    try:
        target_auth = await self.generate_target_auth(backup_auth=backup_auth)
    except LA_ValidAuth as e:
        raise LA_UpdateCredsError(
            cred_type="target_account",
            local_account_name=target_account_name,
            additional_message=str(e),
        ) from e

        return await self._upsert_account(
            auth=target_auth,
            account_name=target_account_name,
            is_test=is_test,
            debug_api=debug_api,
        )

    except dmde.DomoError as e:
        print(e)
        return None


@patch_to(LocalAccountConfig)
async def upsert_remote_account(
    self,
    remote_account_name: str,
    auth: dmda.DomoAuth,
    is_test=True,
    debug_api: bool = False,
):
    try:
        return await self._upsert_account(
            auth=auth,
            account_name=remote_account_name,
            is_test=is_test,
            debug_api=debug_api,
        )
    except dmde.DomoError as e:
        print(e)
        return None


@patch_to(LocalAccountConfig)
async def upsert_accounts(
    self,
    target_account_name: str = None,
    target_auth_backup: dmda.DomoAuth = None,
    remote_account_name: str = None,
    remote_auth_ls: List[dmda.DomoAuth] = None,
    debug_api: bool = False,
):
    """attempts to upsert account in target instance AND deploy account to remote instances"""
    target_acc = await self.upsert_target_account(
        target_account_name=target_account_name or self.local_account_name,
        backup_auth=target_auth_backup,
        is_test=False,
        debug_api=debug_api,
    )

    local_acc = await self.upsert_local_account(is_test=False, debug_api=debug_api)

    remote_accs = None
    if remote_auth_ls:
        remote_accs = await dmce.gather_with_concurrency(
            *[
                self.upsert_remote_account(
                    remote_account_name=remote_account_name or self.local_account_name,
                    auth=r_auth,
                )
                for r_auth in remote_auth_ls
            ],
            n=10,
        )

    return {"target": target_acc, "local": local_acc, "remotes": remote_accs}

# %% ../nbs/LocalAccount/ConfigInstanceAccount.ipynb 16
class AccessToken_NotExpired(dlcr.ClassError):
    def __init__(self, token_name):
        super().__init__(f"token {token_name} is not expired")


@patch_to(LocalAccountConfig)
async def regenerate_access_token(
    self,
    domo_user: dmdu.DomoUser,
    auth: dmda.DomoAuth,
    token_name: str,
    duration_in_days: int = 90,
    access_token_days_to_expiry_threshold: int = 7,
    is_force_regeneration: bool = False,
    raise_exception: bool = True,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
):

    domo_config = dmic.DomoInstanceConfig(auth=auth)

    if not is_force_regeneration:
        domo_access_tokens = await domo_config.get_access_tokens(
            debug_api=debug_api, session=session
        )

        domo_access_token = next(
            (
                token
                for token in domo_access_tokens
                if token.owner == domo_user and token.name == token_name
            ),
            None,
        )

        if (
            domo_access_token
            and access_token_days_to_expiry_threshold
            <= (domo_access_token.expiration_date - dt.datetime.now()).days
        ):
            if raise_exception:
                raise AccessToken_NotExpired(token_name)

            return domo_access_token

    domo_access_token = await domo_config.regenerate_access_token(
        domo_user=domo_user,
        token_name=token_name,
        session=session,
        duration_in_days=duration_in_days,
        debug_api=debug_api,
    )
    self.domo_access_token = domo_access_token.token

    return domo_access_token


@patch_to(LocalAccountConfig)
async def regenerate_target_access_token(
    self,
    token_name: str = None,
    duration_in_days: int = 90,
    access_token_days_to_expiry_threshold: int = 7,
    is_force_regeneration: bool = False,
    debug_api: bool = False,
    trigger_upsert_accounts: bool = True,
    target_account_name: str = None,
    target_auth_backup: dmda.DomoAuth = None,
    remote_account_name: str = None,
    remote_auth_ls: List[dmda.DomoAuth] = None,
):

    if not self.is_valid_token_auth:
        is_force_regeneration = True

    target_auth = await self.generate_target_auth(backup_auth=target_auth_backup)

    domo_user = await dmdu.DomoUsers.by_email(
        email_ls=[self.domo_username], auth=target_auth
    )

    domo_access_token = await self.regenerate_access_token(
        domo_user=domo_user,
        auth=target_auth,
        token_name=token_name or self.local_account_name,
        duration_in_days=duration_in_days,
        access_token_days_to_expiry_threshold=access_token_days_to_expiry_threshold,
        is_force_regeneration=is_force_regeneration,
        raise_exception=False,
        debug_api=debug_api,
    )

    if trigger_upsert_accounts:
        return await self.upsert_accounts(
            target_account_name=target_account_name or self.local_account_name,
            target_auth_backup=target_auth_backup,
            remote_account_name=remote_account_name or self.local_account_name,
            remote_auth_ls=remote_auth_ls,
            debug_api=debug_api,
        )

    return domo_access_token
