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

# %% auto 0
__all__ = ['DEFAULT_SCOPE', 'GAuth_Required', 'GoogleAuth_NoEnv_Error', 'generate_creds_from_env', 'generate_creds_from_file',
           'GoogleAuth']

# %% ../../nbs/google/GAuth.ipynb 3
import os
import json
from dataclasses import dataclass, field
from typing import List

import dotenv

from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow

# %% ../../nbs/google/GAuth.ipynb 4
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import Resource

# %% ../../nbs/google/GAuth.ipynb 6
def str_to_obj(obj_to_test):
    if isinstance(obj_to_test, str):
        import orjson

        return orjson.loads(obj_to_test)
    return obj_to_test


def _output_creds(creds, output_path=None, env_path=None, token_env_key=None):
    if token_env_key:
        os.environ[token_env_key] = json.dumps(creds.to_json())

    if output_path:
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(creds.to_json())

    if env_path:
        dotenv.set_key(env_path, token_env_key, creds.to_json())

# %% ../../nbs/google/GAuth.ipynb 7
DEFAULT_SCOPE = [
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/drive.file",
    "https://www.googleapis.com/auth/drive.metadata.readonly",
    "https://www.googleapis.com/auth/drive.readonly",
]

# %% ../../nbs/google/GAuth.ipynb 8
class GAuth_Required(Exception):
    def __init__(self):
        message = f"must pass valid Google Auth or Service with `service_name = docs`"
        super().__init__(message)


class GoogleAuth_NoEnv_Error(Exception):
    def __init__(self, env_token):
        super().__init__(f"{env_token} not found in ENV")


def generate_creds_from_env(
    client_env_key,
    token_env_key=None,
    scope: List[str] = None,
    env_path=None,
):

    scope = scope or DEFAULT_SCOPE

    if env_path:
        print(f"reloading env - {env_path}")
        dotenv.load_dotenv(env_path, override=True)

    token_obj = json.loads(os.environ.get(token_env_key)) if token_env_key else None

    # print(
    #     token_env_key,
    #     "after should be dict",
    #     type(token_obj),
    #     token_obj,
    # )

    creds = (
        Credentials.from_authorized_user_info(token_obj, scope) if token_obj else None
    )

    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())

        else:
            if not os.getenv(client_env_key):
                raise GoogleAuth_NoEnv_Error(client_env_key)

            client_obj = str_to_obj(os.getenv(client_env_key))

            flow = InstalledAppFlow.from_client_config(client_obj, scope)
            creds = flow.run_local_server(port=0)

    _output_creds(creds, env_path=env_path, token_env_key=token_env_key)

    return creds

# %% ../../nbs/google/GAuth.ipynb 10
def generate_creds_from_file(client_path, token_path, scope: List[str] = None):

    scope = scope or DEFAULT_SCOPE

    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_file(token_path, scope)

    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(client_path, scope)
            creds = flow.run_local_server(port=0)

    _output_creds(creds, output_path=token_path)

    return creds

# %% ../../nbs/google/GAuth.ipynb 11
@dataclass
class GoogleAuth:
    creds: Credentials = None
    service: Resource = None

    scope: List[str] = field(default_factory=lambda: DEFAULT_SCOPE)

    def __post_init__(self):
        self.service = self.service or self.generate_service(
            service_name="drive", service_version="v3"
        )

    @classmethod
    def generate_from_env(
        cls,
        client_env_key,
        token_env_key=None,
        env_path=None,
        scope: List[str] = None,
    ):
        """
        credentials should be stored in the .env file as a json (NOT A STRING)
        token will be stored as a string (NOT A JSON OBJ)

        generate creds here, https://console.cloud.google.com/apis/credentials/oauthclient
        for a new implementation you'll need to
        1. set up a project at console.cloud.google.com,
        2. enable (turn on) the appropriate APIs for that project
        3. configure OAuth access (your python script will use an Oauth token to authenticate, so your project needs the OAuth screen)
        4. download the creds file and store it as env_creds.json
        """

        creds = generate_creds_from_env(
            client_env_key=client_env_key,
            token_env_key=token_env_key,
            scope=scope,
            env_path=env_path,
        )

        return cls(creds=creds)

    @classmethod
    def generate_from_file(
        cls, client_path: str, token_path: str, scope: List[str] = None
    ):
        """generate creds here, https://console.cloud.google.com/apis/credentials/oauthclient
        for a new implementation you'll need to
        1. set up a project at console.cloud.google.com,
        2. enable (turn on) the appropriate APIs for that project
        3. configure OAuth access (your python script will use an Oauth token to authenticate, so your project needs the OAuth screen)
        4. download the client json file and store it as env_creds.json
        """

        creds = generate_creds_from_file(
            client_path=client_path, token_path=token_path, scope=scope
        )

        return cls(creds=creds)

    def generate_service(self, service_name="drive", service_version="v3") -> Resource:
        """Generates a service object for the given service name and version"""

        print(
            f"generating {service_name} - {service_version} service object on {self.__class__.__name__}"
        )

        self.service = build(
            serviceName=service_name, version=service_version, credentials=self.creds
        )
        return self.service
