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

# %% auto 0
__all__ = ['to_json', 'AppDbDocument', 'AppDbCollection', 'AppDbCollections']

# %% ../../nbs/classes/50_DomoAppDb.ipynb 2
from typing import List, Callable
import httpx

import numbers
import asyncio

from dataclasses import field, dataclass
import domolibrary.client.DomoAuth as dmda
import datetime as dt
import domolibrary.utils.convert as dlcv
import domolibrary.utils.chunk_execution as ce

from nbdev.showdoc import patch_to

import domolibrary.routes.appdb as appdb_routes

# %% ../../nbs/classes/50_DomoAppDb.ipynb 6
def to_json(value):
    if hasattr(value, "to_json"):
        return value.to_json()

    if isinstance(value, dict):
        return {key: to_json(v) for key, v in value.items()}

    if isinstance(value, list):
        return [to_json(v) for v in value]

    if isinstance(value, numbers.Number):
        return value

    return str(value)


@dataclass
class AppDbDocument:

    auth: dmda.DomoAuth = field(repr=False)
    _collection_id: str

    _identity_columns: List[str]

    _id: str = None

    _created_on_dt: dt.datetime = None

    _updated_on_dt: dt.datetime = None

    content: dict = None

    def to_json(self, custom_content_to_json_fn: Callable = None):
        self.update_config()

        s = {"id": self._id, "collectionId": self._collection_id}

        if custom_content_to_json_fn:
            s.update({"content": custom_content_to_json_fn(self.content)})

        else:
            for key, value in self.__dict__.items():

                if key.startswith("_") or key in ["auth"]:
                    continue

                s.update({key: to_json(value)})

        return s

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

        if self._identity_columns:
            return all(
                getattr(self, col) == getattr(other.col)
                for col in self._identity_columns
            )

        return self._id == other._id

    @classmethod
    def _from_json(
        cls,
        auth: dmda.DomoAuth,
        content,
        new_cls,
        identity_columns,
        collection_id=None,
        document_id=None,
        metadata=None,
        created_on_dt=None,
        updated_on_dt=None,
    ):

        if metadata:
            collection_id = metadata.pop("collectionId")

            created_on_dt = dlcv.convert_string_to_datetime(metadata.pop("createdOn"))

            updated_on_dt = dlcv.convert_string_to_datetime(metadata.pop("updatedOn"))
            document_id = metadata["id"]

        return new_cls(
            auth=auth,
            _id=document_id,
            _identity_columns=identity_columns,
            _collection_id=collection_id,
            _created_on_dt=created_on_dt,
            _updated_on_dt=updated_on_dt,
            content=content,
            **(content if cls.__name__ != "AppDbDocument" else {})
        )

    @classmethod
    def _from_api(
        cls,
        auth: dmda.DomoAuth,
        obj,
        identity_columns: List[str] = None,
    ):
        content = obj.pop("content")

        return cls._from_json(
            auth=auth,
            content=content,
            new_cls=cls,
            identity_columns=identity_columns,
            metadata=obj,
        )

    @classmethod
    def from_json(
        cls,
        auth: dmda.DomoAuth,
        collection_id: str,
        content: dict,
        identity_columns: List[str] = None,
    ):
        return cls._from_json(
            auth=auth,
            content=content,
            new_cls=cls,
            identity_columns=identity_columns,
            collection_id=collection_id,
        )

    def update_config(self):
        self.content = {
            key: value
            for key, value in self.__dict__.items()
            if key not in ["auth", "content"] and not key.startswith("_")
        }
        return self.content

    @classmethod
    async def get_by_id(
        cls,
        collection_id: str,
        document_id: str,
        auth: dmda.DomoAuth,
        identity_columns=None,
        debug_api: bool = False,
        session: httpx.AsyncClient = None,
        debug_num_stacks_to_drop=1,
        return_raw: bool = False,
    ):
        res = await appdb_routes.get_collection_document_by_id(
            auth=auth,
            collection_id=collection_id,
            document_id=document_id,
            parent_class=cls.__name__,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw:
            return res

        return cls._from_api(
            auth=auth,
            obj=res.response,
            identity_columns=identity_columns or [],
        )

# %% ../../nbs/classes/50_DomoAppDb.ipynb 9
@patch_to(AppDbDocument, cls_method = True)
async def create_document(
    cls,
    content : dict,
    collection_id : str,
    auth : dmda.DomoAuth,
    session: httpx.AsyncClient,
    debug_api: bool = False,
    debug_num_stacks_to_drop=2,
    return_raw : bool = False
):
    res = await appdb_routes.create_document(
        auth=auth,
        collection_id= collection_id,
        content = content,
        session=session,
        debug_api=debug_api,
        parent_class= cls.__name__,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    if return_raw:
        return res

    return await cls.get_by_id(collection_id = collection_id, document_id = res.response['id'], auth = auth)

# %% ../../nbs/classes/50_DomoAppDb.ipynb 10
@patch_to(AppDbDocument)
async def update_document(
    self,
    content : dict = None,
    debug_api: bool = False,
    session: httpx.AsyncClient = None,
    debug_num_stacks_to_drop=1,
    return_raw: bool = False
    
):
    res = await appdb_routes.update_document(
        auth=auth,
        collection_id=self._collection_id,
        document_id=self._id,
        content = content or self.to_json()['content'],
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        session=session,
        parent_class=self.__class__.__name__,
    )

    if return_raw:    
        return res
        
    return res

# %% ../../nbs/classes/50_DomoAppDb.ipynb 15
@dataclass
class AppDbCollection:
    auth: dmda.DomoAuth = field(repr=False)
    id: str
    name: str

    created_on_dt: dt.datetime
    updated_on_dt: dt.datetime

    schema: dict

    domo_documents : List[AppDbDocument] = None

    @classmethod
    def _from_json(cls, auth, obj):

        return cls(
            auth=auth,
            id=obj["id"],
            name=obj["name"],
            created_on_dt=dlcv.convert_string_to_datetime(obj["createdOn"]),
            updated_on_dt=dlcv.convert_string_to_datetime(obj["updatedOn"]),
            schema=obj["schema"],
        )
        

    @classmethod
    async def get_by_id(
        cls,
        auth: dmda.DomoAuth,
        collection_id,
        debug_api: bool = False,
        session: httpx.AsyncClient = None,
        debug_num_stacks_to_drop=2,
        return_raw: bool = False,
    ):

        res = await appdb_routes.get_collection_by_id(
            auth=auth,
            collection_id=collection_id,
            parent_class=cls.__name__,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw:
            return res

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


    def __eq__(self, other):
        if not isinstance(other, AppDbCollection):
            return False

        return self.id == other.id

# %% ../../nbs/classes/50_DomoAppDb.ipynb 16
@patch_to(AppDbCollection)
async def share_collection(
    self,
    domo_user=None,
    domo_group=None,
    permission: appdb_routes.Collection_Permission_Enum = appdb_routes.Collection_Permission_Enum.READ_CONTENT,
    debug_api: bool = False,
    debug_num_stacks_to_drop: int = 2,
    session: httpx.AsyncClient = None,
):
    return await appdb_routes.modify_collection_permissions(
        collection_id=self.id,
        user_id=(domo_user and domo_user.id) or (await self.auth.who_am_i()).response["id"],
        group_id=domo_group and domo_group.id,
        permission=permission,
        auth=self.auth,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        session=session,
        parent_class=self.__class__.__name__,
    )


@patch_to(AppDbCollection)
async def query_documents(
    self,
    query: dict = None,
    return_raw: bool = False,
    try_auto_share=False,
    debug_api: bool = False,
    debug_num_stacks_to_drop: int = 2,
    session: httpx.AsyncClient = None,
):

    documents = []
    loop_retry = 0
    while loop_retry <= 1 and not documents:
        try:
            res = await appdb_routes.get_documents_from_collection(
                auth=self.auth,
                collection_id=self.id,
                query=query,
                debug_api=debug_api,
                debug_num_stacks_to_drop=debug_num_stacks_to_drop,
                session=session,
            )

            documents = res.response

        except appdb_routes.AppDb_GET_Exception as e:
            if try_auto_share:
                await self.share_collection(debug_api=debug_api)
                await asyncio.sleep(2)

            loop_retry += 1

            if loop_retry > 1:
                raise e

        if return_raw:
            return res

        self.domo_documents = await ce.gather_with_concurrency(
            *[
                AppDbDocument.get_by_id(
                    collection_id=self.id, document_id=doc["id"], auth=self.auth
                )
                for doc in documents
            ],
            n=60
        )

        return self.domo_documents

# %% ../../nbs/classes/50_DomoAppDb.ipynb 21
@patch_to(AppDbDocument, cls_method=True)
async def upsert(
    cls,
    auth: dmda.DomoAuth,
    collection_id,
    content: dict,
    identity_columns: List[str],
    session: httpx.AsyncClient = None,
    debug_api=False,
    debug_num_stacks_to_drop=3,
    return_raw: bool = False,
):
    domo_doc = None

    query = {f"content.{col}": content[col] for col in identity_columns}

    domo_collection = await AppDbCollection.get_by_id(
        auth=auth, collection_id=collection_id, return_raw=False
    )

    res = await domo_collection.query_documents(query=query, debug_api=debug_api)
    
    if res:
        domo_doc = res[0]

    if not domo_doc:
        return await cls.create_document(
            content=content,
            collection_id=collection_id,
            auth=auth,
            session=session,
            debug_api=debug_api,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            return_raw=return_raw,
        )

    return await domo_doc.update_document(
        session=session, content=content, debug_api=debug_api, return_raw=return_raw
    )

# %% ../../nbs/classes/50_DomoAppDb.ipynb 25
@dataclass
class AppDbCollections:

    @classmethod
    async def get_collections(
        cls,
        auth: dmda.DomoAuth,
        datastore_id : str = None, 
        debug_api: bool = False,
        session: httpx.AsyncClient = None,
        debug_num_stacks_to_drop=1,
        return_raw: bool = False 
    ):

        res = await appdb_routes.get_collections(
            auth=auth,
            datastore_id= datastore_id,
            parent_class=cls.__class__.__name__,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        )

        if return_raw :
            return res
        
        return await ce.gather_with_concurrency(*[ AppDbCollection.get_by_id(collection_id = obj['id'], auth = auth) for obj in res.response], n= 10)
