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

# %% ../../nbs/classes/50_DomoStream.ipynb 3
from __future__ import annotations

from dataclasses import dataclass, field, asdict
from typing import List, Optional, Union
from enum import Enum

import dateutil.parser as dtut
import re
import httpx
from sqlglot import parse_one, exp

from nbdev.showdoc import patch_to

import domolibrary.client.DomoAuth as dmda
import domolibrary.classes.DomoDataset as dmds

import domolibrary.routes.stream as stream_routes

import domolibrary.classes.DomoDatacenter as dmdc

import domolibrary.utils.DictDot as util_dd

# %% auto 0
__all__ = ['StreamConfig_Mapping_amazon_s3_assumerole', 'StreamConfig_Mapping_snowflake_unload_v2',
           'StreamConfig_Mapping_snowflake_federated', 'StreamConfig_Mapping_snowflake', 'StreamConfig_Mapping_default',
           'StreamConfig_Mapping', 'StreamConfig_Mappings', 'StreamConfig', 'DomoStream']

# %% ../../nbs/classes/50_DomoStream.ipynb 7
@dataclass
class StreamConfig_Mapping:
    data_provider_type: str
    sql: str = None
    warehouse: str = None
    database_name: str = None
    s3_bucket_category: str = None

    is_default: bool = False

    def search_keys_by_value(
        self, value_to_search: str,
    ) -> Union[StreamConfigMapping, None]:
        
        if self.is_default:
            if value_to_search in ["enteredCustomQuery", "query", "customQuery"]:
                return 'sql'
        
        return next(
            (key for key, value in asdict(self).items() if value == value_to_search),
            None,
        )


StreamConfig_Mapping_amazon_s3_assumerole = StreamConfig_Mapping(
    data_provider_type="amazon_s3_assumerole", s3_bucket_category="filesDiscovery"
)

StreamConfig_Mapping_snowflake_unload_v2 = StreamConfig_Mapping(
    data_provider_type="snowflake_unload_v2",
    sql="query",
    warehouse="warehouseName",
    database_name="databaseName",
)

StreamConfig_Mapping_snowflake_federated = StreamConfig_Mapping(
    data_provider_type="snowflake_federated", sql=None
)

StreamConfig_Mapping_snowflake = StreamConfig_Mapping(
    data_provider_type="snowflake",
    sql="query",
    warehouse="warehouseName",
    database_name="databaseName",
    s3_bucket_category=None,
)

StreamConfig_Mapping_default = StreamConfig_Mapping(
    data_provider_type="default",
    is_default = True
)

class StreamConfig_Mappings(Enum):
    amazon_s3_assumerole = StreamConfig_Mapping_amazon_s3_assumerole
    snowflake_unload_v2 = StreamConfig_Mapping_snowflake_unload_v2
    snowflake_federated = StreamConfig_Mapping_snowflake_federated
    snowflake = StreamConfig_Mapping_snowflake

    default = StreamConfig_Mapping_default

    @classmethod
    def search(cls, value, debug_api : bool = False) -> Union[StreamConfig_Mappings, None] :
        try:
            return cls[value]
        
        except KeyError as e:
            if debug_api:
                print(f"{value} has not been added to enum config, must implement")
            return cls.default

# %% ../../nbs/classes/50_DomoStream.ipynb 9
@dataclass
class StreamConfig:
    stream_category: str 
    name: str
    type: str
    value: str
    value_clean: str = None
    
    def __post_init__(self):
        
        # self.value_clean = self.value.replace("\n", " ")
        # sc.value_clean = re.sub(" +", " ", sc.value_clean)
        
        if self.stream_category =='sql':
            self.process_sql()
            pass
        
    def process_sql(self):
        
        self.parent_stream.configuration_query = self.value
        
        for table in parse_one(self.value).find_all(exp.Table):
            
            self.parent_stream.configuration_tables.append(table.name.lower())  
            self.parent_stream.configuration_tables = sorted(list(set(self.parent_stream.configuration_tables)))
            
        return self.parent_stream.configuration_tables
    
    
    @classmethod
    def from_json(cls, 
                  obj : dict ,
                  data_provider_type : str,
                 parent_stream : DomoStream = None):
                
                       
        config_name = obj['name']
        
        mapping_enum = StreamConfig_Mappings.search( data_provider_type.lower().replace('-', '_'))
        stream_category = 'default'

        if mapping_enum:
            stream_category = mapping_enum.value.search_keys_by_value( config_name)
            
            if parent_stream: 
                parent_stream.has_mapping = True
        
        return cls(
            stream_category = stream_category,
            name=config_name, 
            type=obj['type'],
            value=obj['value'],
            parent_stream=parent_stream
        )
    
    def to_json(self):
        return {"field" : self.stream_category, "key": self.name, "value" : self.value}

# %% ../../nbs/classes/50_DomoStream.ipynb 11
@dataclass
class DomoStream:
    auth: dmda.DomoAuth = field(repr=False)
    id: str
    dataset_id: str
    transport_description: str
    transport_version: int
    update_method: str
    data_provider_name: str
    data_provider_key: str
    account_id: str = None
    account_display_name: str = None
    account_userid: str = None

    has_mapping: bool = False
    configuration: List[StreamConfig] = field(default_factory=list)
    configuration_tables: List[str] = field(default_factory=list)
    configuration_query: str = None

    def process_configs():
        pass

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

        data_provider = obj.get("dataProvider", {})
        transport = obj.get("transport", {})
        datasource = obj.get("dataSource", {})

        account = obj.get("account", {})

        sd = cls(
            auth=auth,
            id=obj["id"],
            transport_description=transport["description"],
            transport_version=transport["version"],
            update_method=obj.get("updateMethod"),
            data_provider_name=data_provider["name"],
            data_provider_key=data_provider["key"],
            dataset_id=datasource["id"],
        )

        if account:
            sd.account_id = account.get("id")
            sd.account_display_name = account.get("displayName")
            sd.account_userid = account.get("userId")

        sd.configuration = [
            StreamConfig.from_json(
                obj=c_obj, data_provider_type=data_provider.get("key"), parent_stream=sd
            )
            for c_obj in obj["configuration"]
        ]

        return sd

    def generate_config_rpt(self):
        res = {}

        for config in self.configuration:
            if config.stream_category != "default" and config.stream_category:
                obj = config.to_json()
                res.update({obj["field"]: obj["value"]})

        return res

    @classmethod
    async def get_stream_by_id(
        cls,
        auth: dmda.DomoAuth,
        stream_id: str,
        debug_num_stacks_to_drop=2,
        debug_api: bool = False,
        return_raw : bool = False,
        session: Optional[httpx.AsyncClient] = None,
    ):

        res = await stream_routes.get_stream_by_id(
            auth=auth,
            stream_id=stream_id,
            session=session,
            parent_class=cls.__name__,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            debug_api=debug_api,
        )

        if return_raw:
            return res

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


    @classmethod
    async def create_stream(
        cls,
        cnfg_body,
        auth: dmda.DomoAuth = None,
        session: Optional[httpx.AsyncClient] = None,
        debug_api: bool = False,
    ):
        return await stream_routes.create_stream(
            auth=auth, body=cnfg_body, session=session, debug_api=debug_api
        )

    @classmethod
    async def update_stream(
        cls,
        cnfg_body,
        stream_id,
        auth: dmda.DomoAuth = None,
        session: Optional[httpx.AsyncClient] = None,
        debug_api: bool = False,
    ):

        return await stream_routes.update_stream(
            auth=auth,
            stream_id=stream_id,
            body=cnfg_body,
            session=session,
            debug_api=debug_api,
        )

    @classmethod
    async def upsert_connector(
        cls,
        cnfg_body,
        match_name=None,
        auth: dmda.DomoAuth = None,
        session: Optional[httpx.AsyncClient] = None,
        debug_api: bool = False,
    ):
        search_body = dmdc.DomoDatacenter.generate_search_datacenter_body_by_name(
            entity_name=match_name
        )

        search_res = await dmdc.DomoDatacenter.search_datacenter(
            auth=auth, body=search_body, session=session, debug_api=debug_api
        )

        existing_ds_obj = next(
            (ds for ds in search_res if ds.get("name").lower() == match_name.lower()),
            None,
        )

        # if debug_api:
        #     print(
        #         f"existing_ds - {existing_ds.id if existing_ds else ' not found '}")

        if existing_ds_obj:
            existing_ds = await dmds.DomoDataset.get_from_id(
                dataset_id=existing_ds.get("databaseId"), auth=auth
            )
            return await cls.update_stream(
                cnfg_body,
                stream_id=existing_ds.stream_id,
                auth=auth,
                session=session,
                debug_api=False,
            )
        else:
            return await cls.create_stream(
                cnfg_body, auth=auth, session=session, debug_api=debug_api
            )
