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

# %% ../nbs/client/client.ipynb 3
from __future__ import annotations

import os
from typing import Any, Union
from dataclasses import dataclass, field
from abc import abstractmethod, ABC

from urllib.parse import urlparse

import httpx
import json

from pprint import pprint

import domolibrary_extensions.utils as ut

# %% auto 0
__all__ = ['Auth', 'ResponseGetData', 'get_cache', 'update_cache', 'get_data', 'looper']

# %% ../nbs/client/client.ipynb 5
@dataclass
class Auth(ABC):
    """Base class for authentication"""

    @abstractmethod
    def generate_auth_header(self) -> dict:
        """Get the headers for the authentication"""
        pass

# %% ../nbs/client/client.ipynb 6
@dataclass
class ResponseGetData:
    """class for returning data from any route"""

    is_from_cache: bool
    is_success: bool

    status: int
    response: Any
    auth: Any = field(repr=False, default=None)

    def __post_init__(self):
        self.is_success = True if self.status >= 200 and self.status <= 399 else False

    @classmethod
    def _from_httpx(cls, res: httpx.Response, auth: Any = None):
        return cls(
            status=res.status_code,
            response=res.json(),
            is_success=res.is_success,
            is_from_cache=False,
            auth=auth,
        )

    @classmethod
    def _from_cache(cls, data: dict = None, auth: Any = None):
        return cls(
            status=200,
            response=data,
            is_success=True,
            is_from_cache=True,
            auth=auth,
        )

# %% ../nbs/client/client.ipynb 11
def get_cache(json_cache_path: str, debug_prn: bool = False) -> Union[dict, None]:
    """function for getting cached data from json file"""

    json_data = None
    ut.upsert_folder(folder_path=json_cache_path, debug_prn=debug_prn)

    try:
        with open(json_cache_path, "r", encoding="utf-8") as file:
            json_data = json.load(file)

    except (FileNotFoundError, json.JSONDecodeError) as e:
        with open(json_cache_path, "w+", encoding="utf-8") as file:
            pass
        json_data = None

    if json_data:
        if debug_prn:
            print(f"🚀 Using cached data in {json_cache_path}")

    return json_data


def update_cache(json_cache_path: str, json_data: dict):
    ut.upsert_folder(json_cache_path)

    with open(json_cache_path, "w", encoding="utf-8") as file:
        json.dump(json_data, file)

    return True

# %% ../nbs/client/client.ipynb 15
def prepare_fetch(
    url: str,
    params: dict = None,
    auth: Auth = None,
    headers: dict = None,
    body: dict = None,
):
    """base function to prepare a fetch operation"""

    headers = headers or {"Accept": "application/json"}

    if auth:
        headers = {**headers, **auth.generate_auth_header()}

    return headers, url, params, body

# %% ../nbs/client/client.ipynb 16
def _generate_cache_name(url):
    uparse = urlparse(url)

    return f"./CACHE/{''.join([uparse.netloc.replace('.', '_'), uparse.path.replace('.', '_')])}.json"

# %% ../nbs/client/client.ipynb 18
async def get_data(
    url: str,
    method: str,
    json_cache_path: str = None,
    is_ignore_cache: bool = False,
    headers: dict = None,
    params: dict = None,
    body=None,
    auth: Auth = None,
    parent_class: str = None,
    debug_api: bool = False,
    debug_prn: bool = False,
    client: httpx.AsyncClient = None,
    is_verify_ssl: bool = False,
) -> ResponseGetData:
    """wrapper for httpx Request library, always use with jiralibrary class"""

    json_cache_path = json_cache_path or _generate_cache_name(url)

    if not is_ignore_cache and json_cache_path:
        json_data = get_cache(json_cache_path=json_cache_path, debug_prn=debug_prn)

        if json_data:
            return ResponseGetData._from_cache(data=json_data, auth=auth)

    is_close_session = False if client else True
    client = client or httpx.AsyncClient()

    headers, url, params, body = prepare_fetch(
        url=url,
        params=params,
        auth=auth,
        headers=headers,
        body=body,
    )

    if debug_api:
        pprint(
            {
                "headers": headers,
                "url": url,
                "params": params,
                "body": body,
                "cache_file_path": json_cache_path,
                "debug_api": debug_api,
                "parent_class": parent_class,
            }
        )

    if method.upper() == "GET":
        res = await client.get(
            url=url,
            headers=headers,
            params=params,
            follow_redirects=True,
            verify=is_verify_ssl,
        )
    else:
        res = await getattr(client, method)(
            url=url, headers=headers, params=params, data=body, verify=is_verify_ssl
        )

    if is_close_session:
        await client.aclose()

    rgd = ResponseGetData._from_httpx(res, auth=auth)

    if rgd.is_success:
        update_cache(json_cache_path=json_cache_path, json_data=rgd.response)

    return rgd

# %% ../nbs/client/client.ipynb 21
async def looper(
    url,
    client: httpx.AsyncClient,
    auth: Auth,
    arr_fn,
    params: dict = None,
    offset=0,
    limit=50,
    debug_loop: bool = False,
    debug_api: bool = False,
    debug_prn: bool = False,
    method="GET",
    is_ignore_cache: bool = False,
    json_cache_path: str = None,
    **kwargs
):
    json_cache_path = json_cache_path or _generate_cache_name(url)

    if not is_ignore_cache and json_cache_path:
        json_data = get_cache(json_cache_path=json_cache_path, debug_prn=debug_prn)

        if json_data:
            return ResponseGetData._from_cache(data=json_data, auth=auth)

    final_array = []
    keep_looping = True

    while keep_looping:
        new_params = params.copy() if params else {}

        new_params = {**new_params, "startAt": offset, "maxResults": limit}

        if debug_loop:
            print({"startAt": offset, "maxResults": limit, **new_params})

        res = await get_data(
            is_ignore_cache=True,
            auth=auth,
            url=url,
            method=method,
            params=new_params,
            debug_api=debug_api,
            debug_prn=debug_prn,
            client=client,
            **kwargs
        )

        new_array = arr_fn(res)

        if not new_array or len(new_array) == 0:
            keep_looping = False

        final_array += new_array
        offset += limit

    res.response = final_array

    if res.is_success:
        update_cache(json_cache_path=json_cache_path, json_data=res.response)

    return res
