# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_FactoryUser.ipynb.

# %% auto 0
__all__ = ['get_user', 'GetUser_Config', 'upsert_user', 'UpsertUser_Config', 'delete_user', 'DeletetUser_Config',
           'modify_config_is_force_regenerate_based_on_expiration_threshold_fn', 'regenerate_user_accesstoken',
           'RegenerateUserAccessToken_Config']

# %% ../nbs/00_FactoryUser.ipynb 2
from .utils.factory import (
    factory_function,
    FactoryLogs, 
    FactoryResponse, FactoryConfig, FactoryMessage, FactoryFunction_Error)

# %% ../nbs/00_FactoryUser.ipynb 3
from typing import List, Callable
from dataclasses import dataclass, field
import httpx

import domolibrary.client.DomoAuth as dmda

import domolibrary.classes.DomoInstanceConfig as dmic
import domolibrary.classes.DomoRole as dmdr
import domolibrary.classes.DomoUser as dmdu
import domolibrary.classes.DomoGroup as dmgr
import domolibrary.classes.DomoAccount as dmac 
import domolibrary.classes.DomoAccessToken as dmat



# %% ../nbs/00_FactoryUser.ipynb 6
@factory_function
async def get_user(
    logs: FactoryLogs,
    res: FactoryResponse,
    config: FactoryConfig,
    auth: dmda.DomoAuth,

    user_email: str = None, # will retrieve role searched by name
    
    # updated during function execution
    domo_user: dmdu.DomoUser= None,

    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_factory: bool = False,
    
    **kwargs,
):

    res.response = False

    domo_user = None

    step = FactoryMessage(stage="retrieve user", is_success=False)
    res.add_message(step)

    try:
        domo_user = await dmdu.DomoUsers.by_email(auth=auth, email_ls = [user_email],
                                                  only_allow_one = True,
                                                  debug_api = debug_api,
                                                #   session = session
                                                  )
        step.is_success = True
        step.message = f"search user {user_email} retrieved {domo_user.display_name}"
        config.domo_user = domo_user

    except Exception as e:
        step.is_success = False
        step.message = f"unable to retrieve by_email {user_email} - {str(e)}"

    if not domo_user:
        res.response = "no domo role"
        return res

    res.response = domo_user

    return res


@dataclass
class GetUser_Config(FactoryConfig):
    auth: dmda.DomoAuth
    user_email: str = None
    domo_user : dmdr. DomoRole = None # will update during execution


# %% ../nbs/00_FactoryUser.ipynb 9
@factory_function
async def upsert_user(
    # base required
    logs: FactoryLogs,
    res : FactoryResponse,
    config : FactoryConfig,
    auth: dmda.DomoAuth, 
    
    user_email : str,
    user_display_name : str = None,
    domo_role : dmdr.DomoRole = None, # will use default role if no role provided
    user_password: str = None,
    
    # OUTPUT
    domo_user : dmdu.DomoUser = None, #will add domo user to config

    # DEBUG
    debug_api: bool = False,
    session : httpx.AsyncClient = None,
    **kwargs
    ):
    
    res.response = False    
    auth = auth
    
    # STAGE_1 = DOMO ROLE OR DEFAULT ROLE
    step = FactoryMessage( stage="retrieve domo role", is_success=False)
    res.add_message( step )
    
    if not domo_role:
        try:
            domo_role = await dmdr.DomoRoles.get_default_role(auth = auth,debug_api = debug_api, session = session)
            config.domo_role = domo_role
            
        except Exception as e:
            step.is_success = False
            step.message = str(e)

    if not domo_role:
        res.response = "no domo role"
        return res
    
    step.message = f"using role {domo_role.id} - {domo_role.name}"
    step.is_success = True
            
    # STAGE_2 = DOMO_USER
    step = FactoryMessage( stage="upsert user", is_success=False)
    res.add_message( step )
    
    try:
        domo_user  = await dmdu.DomoUsers.upsert_user(
            auth = auth,
            email_address = user_email,
            display_name = user_display_name,
            session = session,
            role_id = domo_role.id)      
                      
        config.domo_user = domo_user
        step.message = f"upsert user {domo_user.id} successful"
        step.is_success = True
    
    except Exception as e:
        step.message = e
        step.is_success = False
    
    if not domo_user:
        res.response = "no domo user"
        return res

    res.response = domo_user
    
    if not user_password:
        return res
    
    # STEP_3 = SET PASSWORD
    step = FactoryMessage( stage="set password", is_success=False)
    res.add_message( step )
        
    try:
        await domo_user.reset_password(new_password = user_password, debug_api = debug_api)
        domo_user.password = user_password
        step.message = f"set password {user_password}"
        step.is_success = True
    
    except Exception as e:
        step.message = e
        step.is_success = False
    
    return res

@dataclass
class UpsertUser_Config(FactoryConfig):
    auth: dmda.DomoAuth
    session: httpx.AsyncClient
    user_email: str
    user_display_name : str
    user_password :str = None # will not set password if not provided
    domo_role : dmdr.DomoRole = None # will use default not passed
    domo_user : dmdu.DomoUser = None # will assign after upsert

    # factory_fn_ls : List[Callable] = field(default_factory = lambda:[sync_user])


# %% ../nbs/00_FactoryUser.ipynb 11
@factory_function
async def delete_user(
    # base required
    logs: FactoryLogs,
    res : FactoryResponse,
    config : FactoryConfig,
    auth: dmda.DomoAuth, 
    
    # INPUT
    domo_user : dmdu.DomoUser,

    # DEBUG
    debug_api: bool = False,
    session : httpx.AsyncClient = None,
    **kwargs
    ):
    
    print(domo_user)
    res.response = False    
    auth = auth
    
    step = FactoryMessage( stage="delete user", is_success=False)
    res.add_message( step )
        
    try:
        stg= await domo_user.delete_user(
            auth = auth,
            session = session,
            debug_api = debug_api,
            )      
                      
        step.message = f"delete user {domo_user.id} successful"
        step.is_success = True
    
    except Exception as e:
        step.message = e
        step.is_success = False
    
    res.response = step.message
    
    return res

@dataclass
class DeletetUser_Config(FactoryConfig):
    auth: dmda.DomoAuth
    session: httpx.AsyncClient
    domo_user : dmdu.DomoUser


# %% ../nbs/00_FactoryUser.ipynb 12
async def modify_config_is_force_regenerate_based_on_expiration_threshold_fn(
    config: FactoryConfig,
    step: FactoryMessage,
    auth: dmda.DomoAuth,
    domo_user: dmdu.DomoUser,
    access_token_name: str,
    access_token_days_to_expiry_threshold=90,
    session: httpx.AsyncClient = None,
    debug_api: bool = False,
    **kwargs,
):
    # Stage 1 get all access tokens
    step.is_success = False

    try:
        step.message = "get all access tokens"
        domo_config = dmic.DomoInstanceConfig(auth=auth)
        access_tokens = await domo_config.get_access_tokens(
            debug_api=debug_api, session=session
        )

        match_token = next(
            (
                token
                for token in access_tokens
                if token.owner == domo_user and token.name == access_token_name
            ),
            None,
        )

        if not match_token:
            step.message = f"no access token exists for this user {domo_user.display_name} and account name {access_token_name}"
            config.is_force_regenerate = True
            step.is_success = True
            return step

        days_till_expiration = match_token.expiration_date - dt.datetime.now()

        if days_till_expiration.days <= access_token_days_to_expiry_threshold:
            step.message = f"access token expires in {days_till_expiration.days} days <= { access_token_days_to_expiry_threshold}"
            config.is_force_regenerate = True
            step.is_success = True
            return step

        config.is_force_regenerate = False
        step.is_success = True
        return step

    except Exception as e:
        step.message = f"error retrieving all access tokens {e}"
        config.is_force_regenerate = True
        step.is_success = True
        return


@factory_function
async def regenerate_user_accesstoken(
    config: FactoryConfig,
    logs: FactoryLogs,
    res: FactoryResponse,
    auth: dmda.DomoAuth,
    domo_user: dmdu.DomoUser,
    access_token_name: str,
    is_force_regenerate: bool = False,
    duration_in_days: int = 90,
    # updated during execution
    domo_access_token: dmat.DomoAccessToken = None,
    modify_config_fn: Callable = None,
    # used by wrapper
    session: httpx.AsyncClient = None,
    debug_api: bool = False,
    **kwargs,
):
    res.location = auth.domo_instance

    # step 1
    if modify_config_fn:
        step = FactoryMessage(stage="executing modify_config_fn", 
                              stage_num=1)
        step.is_success = False
        res.add_message(step)
        await modify_config_fn(
            **config.__dict__, config=config, step=step, debug_api=debug_api
        )

    # step 2
    step = FactoryMessage(stage="set is_force_regenerate", stage_num=2)
    res.add_message(step)

    is_force_regenerate = config.is_force_regenerate or False
    config.is_force_regenerate = is_force_regenerate
    step.message = f"is_force_regenerate = {str(is_force_regenerate)} passed to config"
    step.is_success = True

    if not is_force_regenerate:
        # is success set in modify function
        res.response = "do not regenerate"
        return res

    step = FactoryMessage(stage="regenerate token", stage_num=3)
    res.add_message(step)

    domo_instance = dmic.DomoInstanceConfig(auth=auth)
    domo_access_token = None
    try:
        domo_access_token = await domo_instance.regenerate_access_token(
            domo_user=domo_user,
            token_name=access_token_name,
            session=session,
            duration_in_days=duration_in_days,
            debug_api=debug_api,
        )

        step.is_success = True
        step.message = "access token regenerated"
        config.domo_access_token = domo_access_token.token

    except Exception as e:
        step.message = f"failed to regenerate access token {access_token_name} for user {domo_user.display_name} - {e}"
        step.is_success = False

    if not domo_access_token:
        res.response = "no access_token"
        return res

    res.response = "successfully regenerated access_token"
    return res


@dataclass
class RegenerateUserAccessToken_Config(FactoryConfig):
    auth: dmda.DomoAuth
    session: httpx.AsyncClient

    domo_user: dmdu.DomoUser
    session: httpx.AsyncClient

    access_token_name: str

    duration_in_days: int = 90

    # updated during execution
    modify_config_fn: Callable = None
    is_force_regenerate: bool = False
    access_token_days_to_expiry_threshold: int = (
        7  # used by default modify_is_force_regenerate_fn to determine whether to reset the account
    )

    domo_access_token: dmat.DomoAccessToken = None

    # # used by wrapper
    # debug_api: bool = False
