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

# %% auto 0
__all__ = ['get_user', 'GetUser', 'upsert_user', 'UpsertUserConfig', 'modify_is_force_regenerate_fn',
           'regenerate_user_accesstoken', 'RegenerateUserAccessToken']

# %% ../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(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, 
    
    email : str,
    display_name : str = None,
    domo_role : dmdr.DomoRole = None, # will use default role if no role provided
    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 = email,
            display_name = 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 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 = password, debug_api = debug_api)
        domo_user.password = password
        step.message = f"set password {password}"
        step.is_success = True
    
    except Exception as e:
        step.message = e
        step.is_success = False
    
    return res

@dataclass
class UpsertUserConfig(FactoryConfig):
    auth: dmda.DomoAuth
    session: httpx.AsyncClient
    display_name : str
    email: str
    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
async def modify_is_force_regenerate_fn(config, step, debug_api : bool = False ):
        # Stage 1 get all access tokens
    step.message = "get all access tokens"
    step.is_success = True
    
    try:
        domo_config = dmic.DomoInstanceConfig(auth = config.auth)
        access_tokens = await domo_config.get_access_tokens(debug_api = debug_api, session = config.session)

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

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


        days_till_expiration = match_token.expiration_date - dt.datetime.now()
        if days_till_expiration.days <= config.access_token_days_to_expiry_threshold:
            step.message = f"access token expires in {days_till_expiration.days} days <= { config.access_token_days_to_expiry_threshold}"
            config.is_force_regenerate = True
            return

        config.is_force_regenerate = False



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

    if not config.is_force_regenerate or config.is_force_regenerate is None:
        step.message ='config did not meet criteria for regeneneration'
        config.is_force_regenerate = False
        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,
    access_token_days_to_expiry_threshold: int = 7, # used by default modify_is_force_regenerate_fn to determine whether to reset the account
    duration_in_days: int = 90 ,

    # updated during execution
    domo_access_token: dmat.DomoAccessToken = None,
    modify_is_force_regenerate_fn : Callable = None,

    # used by wrapper
    session : httpx.AsyncClient = None,
    debug_api: bool = False,
    **kwargs
):
       
    res.location = auth.domo_instance

    step = FactoryMessage( stage="validate if is_force_regenerate")
    res.add_message( step )

    if is_force_regenerate is not None:
        step.message = f"default value {is_force_regenerate} passed to config"
        step.is_success = True

    if modify_is_force_regenerate_fn:
        step.message = "executing modify_is_force_regenerate_fn"
        await modify_is_force_regenerate_fn(config = config, step = step, debug_api = debug_api)
    
    is_force_regenerate = config.is_force_regenerate or False

    if not is_force_regenerate:
        # is success set in modify function
        res.response = "do not regenerate"
        return res
    
    step = FactoryMessage( stage="regenerate token")
    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(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_is_force_regenerate_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

