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

# %% auto 0
__all__ = ['App_API_Exception', 'get_design_by_id', 'get_design_versions', 'Design_GET_Assets',
           'get_design_source_code_by_version', 'get_all_designs', 'get_design_permissions', 'set_design_admins',
           'add_design_admin', 'DomoAppVersion', 'DomoEnterpriseApp', 'App_DownloadSourceCode', 'DomoEnterpriseApps']

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 2
from dataclasses import dataclass, field
import datetime as dt

from typing import List, Any
import os
from enum import Enum

import mbison.client.core as dmda
import mbison.client.utils as dmut
import mbison.feature.users as dmdu

import time
import zipfile

from nbdev.showdoc import patch_to

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 6
class App_API_Exception(dmda.API_Exception):
    def __init__(self, res, message=None):
        super().__init__(res=res, message=message)


def get_design_by_id(auth: dmda.DomoAuth, design_id: str, parts = 'owners,cards,versions,creator', debug_api: bool = False):

    endpoint = f"/api/apps/v1/designs/{design_id}"

    res = dmda.domo_api_request(
        endpoint=endpoint,
        request_type="get",
        params = {'parts': parts},
        auth=auth,
        debug_api=debug_api,
    )

    if not res.is_success:
        raise App_API_Exception(
            res=res, message="is app shared with authenticated user?"
        )

    return res

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 8
def get_design_versions(auth: dmda.DomoAuth, design_id, debug_api: bool = False):

    endpoint = f"/domoapps/designs/{design_id}/versions"

    return dmda.domo_api_request(
        endpoint=endpoint, auth=auth, request_type="get", debug_api=debug_api
    )

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 11
class Design_GET_Assets(dmda.API_Exception):
    def __init__(self, res, design_id):
        
        message = f"unable to download assets for {design_id}"
        super().__init__(
            res=res, 
            message=message
        )


def get_design_source_code_by_version(
    auth: dmda.DomoAuth,
    download_path,
    design_id,
    version,
    debug_api: bool = False,
):

    download_path = dmut.change_suffix(download_path, ".zip")

    endpoint = f"/domoapps/designs/{design_id}/versions/{version}/assets"

    res = dmda.domo_api_stream_request(
        endpoint=endpoint,
        request_type="get",
        auth=auth,
        debug_api=debug_api,
        download_path=download_path,
    )

    if not res.is_success:
        raise Design_GET_Assets(
            res=res, 
            design_id = design_id
        )

    return res

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 13
def get_all_designs(auth: dmda.DomoAuth, debug_api: bool = False, parts : str = 'owners,creator,thumbnail,versions,cards'):

    endpoint = "/api/apps/v1/designs"

    params = {
        "checkAdminAuthority": True,
        "deleted": False,
        "direction": "desc",
        "parts" : parts,
        "search": "",
        "withPermission": "ADMIN",
    }

    res = dmda.domo_api_request(
        endpoint=endpoint,
        request_type="get",
        params=params,
        auth=auth,
        debug_api=debug_api,
        timeout= 10
    )

    return res

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 15
def get_design_permissions(
    design_id: str, auth: dmda.DomoAuth, debug_api: bool = False
):

    res = get_design_by_id(
        auth=auth, design_id=design_id, parts="owners", debug_api=debug_api
    )
    res.response = res.response["owners"]
    return res


def set_design_admins(
    design_id,
    auth: dmda.DomoAuth,
    user_ids: List[str],
    debug_api: bool = False,
    return_raw: bool = False,
):
    endpoint = f"/api/apps/v1/designs/{design_id}/permissions/ADMIN"

    res = dmda.domo_api_request(
        endpoint=endpoint,

        request_type="POST",

        auth=auth,
        debug_api=debug_api,
        body=user_ids,
    )
    if return_raw:
        return res

    if not res.is_success:
        raise App_API_Exception(res=res)

    res.response = f"successfully set design_id {design_id} admins to {user_ids}"

    return res


def add_design_admin(
    design_id: str, auth: dmda.DomoAuth, user_ids: List[int], debug_api: bool = False
):
    user_ids = user_ids if isinstance(user_ids, list) else [user_ids]

    res = get_design_permissions(design_id=design_id, auth=auth, debug_api=debug_api)

    user_ids = list(set([owner["id"] for owner in res.response] + user_ids))

    return set_design_admins(
        design_id=design_id, auth=auth, debug_api=debug_api, user_ids=user_ids
    )

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 18
@dataclass
class DomoAppVersion:
    auth: dmda.DomoAuth = field(repr=False)
    id: str
    design_id: str
    version: str
    draft: bool
    is_client_code_enabled: bool = False
    collection_mapping: List[Any] = None

    @classmethod
    def _from_api(cls, obj, auth):
        return cls(
            auth=auth,
            id=obj["id"],
            design_id=obj["designId"],
            version=obj["version"],
            draft=obj["draft"],
            is_client_code_enabled=obj.get("flags", {}).get(
                "client-code-enabled", False
            ),
            collection_mapping=obj["collectionsMapping"],
        )


@dataclass
class DomoEnterpriseApp:
    auth: dmda.DomoAuth = field(repr=False)
    id: str
    name: str
    owner: dmdu.DomoUser
    created_dt: dt.datetime
    lastmodified_dt: dt.datetime
    versions: List[str]
    current_version: str

    is_client_code_enabled: bool = False

    referencing_cards: List[dict] = None
    ddx_collection_source_code: Any = None

    @classmethod
    def _from_json(cls, obj, auth: dmda.DomoAuth, debug_api: bool = False):

        domo_user = None

        try:
            if obj.get("owner"):
                domo_user = dmdu.DomoUser.get_by_id(
                    user_id=obj["owner"], auth=auth, debug_api=debug_api
                )

        except dmdu.User_API_Exception as e:
            print(e)

        ea = cls(
            auth=auth,
            id=obj["id"],
            name=obj["name"],
            owner=domo_user,
            created_dt=obj["createdDate"],
            lastmodified_dt=obj["updatedDate"],
            versions=obj["versions"],
            current_version=obj["latestVersion"],
        )

        if obj.get("referencingCards", None):
            import mbison.feature.cards as dmac

            ea.referencing_cards = [
                dmac.DomoCard.get_by_id(card_id=card["id"], auth=auth)
                for card in obj.get("referencingCards")
            ]

        if obj.get("versions"):
            ea.versions = [
                DomoAppVersion._from_api(obj, auth) for obj in obj.get("versions")
            ]
            ea.is_client_code_enabled = all(
                version.is_client_code_enabled for version in ea.versions
            )

        return ea

    @classmethod
    def get_by_id(
        cls,
        design_id,
        auth: dmda.DomoAuth,
        debug_api: bool = False,
        return_raw: bool = False,
    ):
        res = get_design_by_id(auth=auth, design_id=design_id, debug_api=debug_api)

        if return_raw:
            return res

        return cls._from_json(obj=res.response, auth=auth, debug_api=debug_api)

    
    def get_versions(self, debug_api: bool = False, return_raw: bool = False):

        res = get_design_versions(
            auth=self.auth, design_id=self.id, debug_api=debug_api
        )
        if return_raw:
            return res

        self.versions = res.response
        return self.versions

    def share(self, domo_users: List[Any] = None, debug_api: bool = False):
        user_ids = (
            [domo_user.id for domo_user in domo_users]
            if domo_users
            else [self.auth.who_am_i()["id"]]
        )
        return add_design_admin(design_id=self.id, auth=self.auth, user_ids=user_ids, debug_api= debug_api)

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 20
class App_DownloadSourceCode(dmda.Class_Exception):
    def __init__(self, cls, auth, message):
        super().__init__(cls = cls, auth = auth, message = message)

@patch_to(DomoEnterpriseApp)
def _get_source_code_enterprise_app(
        self,
        version: str = None,
        debug_api: bool = False,
        download_folder="./EXPORT/",
        file_name=None,
        try_auto_share: bool = False,
        is_unzip = True
    ):
        version = version or self.current_version

        file_path = f"{self.id}-{dmut.convert_str_to_snake_case(self.name, is_only_alphanumeric = True)}/{version }/"

        file_name = file_name or os.path.join(file_path, "archive.zip")
    
        file_name = dmut.change_suffix(file_name, ".zip")

        download_path = os.path.join(download_folder, file_name)

        loop_retry = 0
        res = None
        while loop_retry<=1 and not res:
            try:
                res =  get_design_source_code_by_version(
                    auth=self.auth,
                    download_path=download_path,
                    design_id=self.id,
                    version=version,
                    debug_api=debug_api,
                )
            
            except Design_GET_Assets as e:
                if try_auto_share:
                    self.share(debug_api = debug_api)
                    time.sleep(2)

                loop_retry += 1

                if loop_retry > 1:
                    raise App_DownloadSourceCode(cls = self, auth = self.auth, message = e)
        
        if not is_unzip:
            return res
        
        
        with zipfile.ZipFile(download_path, 'r') as f:
            f.extractall(os.path.join(download_folder,file_path))

        


@patch_to(DomoEnterpriseApp)
def _get_ddx_source_code(
    self,
    debug_api: bool = False,
    try_auto_share: bool = False,
    file_name : str = None,
    download_folder: str = "./EXPORT/",
):
    """
    identifies the collection that stores the source code for a ddx brick

    note card metadata includes "domoapps" which identifies the datastore that feeds an app.
    the one or many collections can be associated with a datastore / app
    for ddx, the source code is stored in the collection 'ddx_app_client_code'
    """

    import mbison.feature.cards as dmdc

    if not self.referencing_cards:
        raise App_DownloadSourceCode(cls = self, auth = self.auth, message= f"unable to download source code for design {self.id} - no referencing_cards attached to this object, rerun with referencing_cards")
    
    try:
        return [
                domo_card.download_source_code(
                    debug_api=debug_api,
                    try_auto_share=try_auto_share,
                    download_folder=download_folder,
                    file_name = file_name
                )
                for domo_card in self.referencing_cards
            ]

    except dmdc.Card_DownloadSourceCode as e: 
        if not try_auto_share:
            raise App_DownloadSourceCode(cls = self, auth = self.auth, message= f"unable to download source code for design {self.id} - {e} try rerunning with try_auto_share = True")
   
        raise App_DownloadSourceCode(cls = self, auth = self.auth, message= f"unable to download source code for design {self.id} - {e}")


@patch_to(DomoEnterpriseApp)
def download_source_code(
    self,
    debug_api: bool = False,
    try_auto_share: bool = False,
    download_folder: str = "./EXPORT/",
    version: str = None,
    file_name: str = None,
):

    try:
        return self._get_source_code_enterprise_app(
            version=version,
            debug_api=debug_api,
            download_folder=download_folder,
            file_name=file_name,
            try_auto_share = try_auto_share
        )

    except App_DownloadSourceCode as e:
        if not try_auto_share:
            message = f"unble to download design {self.id} try rerunning with try_auto_share = True to share the app with authenticated user"
            raise App_DownloadSourceCode(cls = self,
                                        auth = self.auth, 
                                        message = message)

        if not self.is_client_code_enabled:
            message = "is_client_code_enabled (code storage in appdb) is False AND unable to download design {self.id} -- this may be a published app (and therefore unable to access source code)"
            raise App_DownloadSourceCode(cls = self,
                                        auth = self.auth, 
                                        message = message)

        
        print(e)

        return self._get_ddx_source_code(
                debug_api=debug_api,
                try_auto_share=try_auto_share,
                download_folder=download_folder,
                file_name=file_name,
            )

# %% ../../nbs/classes/50_DomoEnterpriseApps.ipynb 22
@dataclass
class DomoEnterpriseApps:
    auth: dmda.DomoAuth = field(repr=False)
    enterprise_apps: List[DomoEnterpriseApp] = None

    failed_downloads: List[DomoEnterpriseApp] = None
    success_downloads : List[DomoEnterpriseApp] = None


    def get_apps(self, debug_api: bool = False, return_raw: bool = False):
        res = get_all_designs(auth=self.auth, debug_api=debug_api)

        if return_raw:
            return res

        self.enterprise_apps = [
            DomoEnterpriseApp._from_json(auth=self.auth, obj=obj)
            for obj in res.response
        ]
        return self.enterprise_apps
    
    def get_all_app_source_code(self, try_auto_share : bool = False, debug_api: bool = False, download_folder= './EXPORTS/'):

        self.failed_downloads = []
        self.success_downloads = []

        if not self.enterprise_apps:
            self.get_apps(debug_api = debug_api)

        for app in self.enterprise_apps:
            try:
                app.download_source_code(try_auto_share= try_auto_share, debug_api = debug_api, download_folder = download_folder)
                self.success_downloads.append(app)
            
            except App_DownloadSourceCode as e:
                print(e)
                self.failed_downloads.append({'error' : e, 'app': app})


