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

# %% auto 0
__all__ = ['get_user', 'GetUser_Config', 'modify_config_calc_domo_role', 'modify_config_user_email_for_upsert_user_fn',
           'upsert_user', 'UpsertUser_Config', 'delete_user', 'DeletetUser_Config',
           'modify_config_is_force_regenerate_based_on_expiration_threshold_fn', 'post_config_fail_if_no_token',
           '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 datetime as dt
from copy import deepcopy

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.DomoAccessToken as dmat

import domolibrary.integrations.RoleHierarchy as dmrh


# %% ../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, stage_num= 1)
    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
async def modify_config_calc_domo_role(
    config: FactoryConfig,
    step: FactoryMessage,
    user_email : str, 
    auth: dmda.DomoAuth,
    domo_role: dmdr.DomoRole,
    domo_user: dmdu.DomoUser = None,
    debug_api: bool = False,
    **kwargs,
):
    step.is_success = False
    
    if not domo_user:
        try:
            domo_user = await dmdu.DomoUsers.by_email(auth=auth, email_ls = [user_email],
                                                  only_allow_one = True,
                                                  debug_api = debug_api,
                                                #   session = session
                                                  )
        
        except dmdu.GetUser_Error as e:    
            step.message = f'no domo user - {user_email}, ignore hierarchy check and proceed to upsert'
            step.is_success = True
            return
        
    try:
        domo_role = await dmrh.calc_role(current_role_id = domo_user.role_id, new_role_name = domo_role.name, auth = config.auth, debug_prn = True)
        config.domo_user = deepcopy(domo_user)
        config.domo_role = domo_role

        step.message = f"will upsert with role - {domo_role.name}"
        step.is_success = True
    
    except Exception as e:
        step.message = f"error caclulating role hierrchy - {e}"
        step.is_success = False
    return

async def modify_config_user_email_for_upsert_user_fn(
    config: FactoryConfig,
    step: FactoryMessage,
    domo_user: dmdu.DomoUser,
    new_email: str = None,
    prefix_email: str = None,
    suffix_email: str = None,
    **kwargs,
):
    # Stage 1 get all access tokens
    step.is_success = False

    try:
        step.message = "rename username"

        email_base, email_end = domo_user.email_address.split("@")

        new_email = (
            new_email
            or f"{prefix_email if prefix_email else ''}{email_base}{suffix_email if suffix_email else ''}@{email_end}"
        )

        config.user_email = new_email

        step.is_success = True
        step.message = f"will upsert {domo_user.email_address} to {new_email}"
        print(step)
        return step

    except Exception as e:
        step.message = (
            f"error generating new email.  Upsert may not work as expected - {e}"
        )
        step.is_success = False
        return


@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,
    modify_config_fn: Callable = 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

    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.asdict(), config=config, step=step, debug_api=debug_api
        )

        domo_role = config.domo_role
        user_email = config.user_email
        user_display_name = config.user_display_name
        user_password = config.user_password
    
    # STAGE_1 = DOMO ROLE OR DEFAULT ROLE
    step = FactoryMessage(stage="retrieve domo role", is_success=False, stage_num = 2)
    res.add_message(step)
    
    if not domo_role:
        try:
            step.message = 'getting default role'
            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, stage_num=3)
    res.add_message(step)

    domo_user = config.domo_user

    if domo_user:
        try:
            property_ls = [
                dmdu.UserProperty(dmdu.UserProperty_Type.display_name, user_display_name),
                dmdu.UserProperty(dmdu.UserProperty_Type.email_address, user_email),
                dmdu.UserProperty(dmdu.UserProperty_Type.role_id, domo_role.id),
            ]

            await domo_user.update_properties(property_ls=property_ls, session = session, debug_api = debug_api)
            
            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:
        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 12
@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 13
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}"
        print(step.message)
        config.is_force_regenerate = True
        step.is_success = True
        return

async def post_config_fail_if_no_token(config: FactoryConfig, step: FactoryMessage, **kwargs):
    
    if not config.domo_access_token:
        step.message = "No access token on config.  Exit early"
        step.is_success = False
        return step
    
    step.message = "Access token on config.  Success"
    step.is_success = True
    return step


@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,
    post_config_fn: Callable = None,
    # used by wrapper
    session: httpx.AsyncClient = None,
    debug_api: bool = False,
    **kwargs,
):
    try:
        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.asdict(), 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"

    finally:
        if post_config_fn:
            step = FactoryMessage(stage="executing post_config_fn", 
                                stage_num=4, is_success = False)

            res.add_message(step)
            
            await post_config_fn(
                **config.asdict(), config=config, step=step, debug_api=debug_api
            )

        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
