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

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

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

from dotenv import load_dotenv, set_key

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

from nbdev.showdoc import patch_to

# %% ../../nbs/google/00_auth.ipynb 5
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import Resource

# %% ../../nbs/google/00_auth.ipynb 6
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/00_auth.ipynb 7
class GoogleAuth_NoEnv_Error(Exception):
    def __init__(self, env_token):
        super().__init__(f"{env_token} not found in ENV")

# %% ../../nbs/google/00_auth.ipynb 8
@dataclass
class GoogleAuth:
    creds: Credentials = None
    service: Resource = None

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

    def _refresh_token(self) -> Credentials:
        """Refreshes the token or creates a new one if it does not exist"""

        if (
            self.creds
            and isinstance(self.creds, Credentials)
            and self.creds.expired
            and self.creds.refresh_token
        ):
            # if creds token can be refreshed, do it.
            try:
                print("refreshing creds using saved token")
                self.creds.refresh(Request())

            except RefreshError as e:
                print(e)
                self.creds.refresh(Request())

        self.generate_service(service_name="drive", service_version="v3")

        return self.creds

    def _new_token_via_auth_flow(self, flow):
        # If there are no (valid) credentials available, run the Oauth flow
        print("generating a brand new token cred via auth flow")

        self.creds = flow.run_local_server(port=0)

        self._refresh_token()

        return self.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 object on {self.__class__.__name__}")

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

        return self.service

# %% ../../nbs/google/00_auth.ipynb 10
@patch_to(GoogleAuth, cls_method=True)
def get_creds_from_env(
    cls: GoogleAuth,
    scope=None,
    credentials_env_key: str = "GDOC_CLIENT",
    token_env_key: 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
    """

    scope = scope or DEFAULT_SCOPE

    credentials_str = os.getenv(credentials_env_key)

    if not credentials_str:
        raise GoogleAuth_NoEnv_Error(credentials_env_key)

    credentials_json = (
        json.loads(credentials_str)
        if credentials_str and isinstance(credentials_str, str)
        else credentials_str
    )

    if token_env_key:
        token_str = os.getenv(token_env_key)

        if not token_str:
            raise GoogleAuth_NoEnv_Error(token_env_key)

        token_json = (
            json.loads(token_str)
            if token_str and isinstance(token_str, str)
            else token_str
        )

        print("using saved token")
        ga_auth = cls(
            creds=Credentials.from_authorized_user_info(token_json, scope), scope=scope
        )
        ga_auth._refresh_token()
        return ga_auth

    flow = InstalledAppFlow.from_client_config(credentials_json, scope)

    ga_auth = cls(scope=scope)
    ga_auth._new_token_via_auth_flow(flow)

    return ga_auth

# %% ../../nbs/google/00_auth.ipynb 16
@patch_to(GoogleAuth, cls_method=True)
def get_creds_from_file(
    cls: GoogleAuth,
    credentials_file_path="env_creds.json",
    token_file_path=None,
    scope=None,
) -> Credentials:
    """
    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
    """

    scope = scope or DEFAULT_SCOPE

    if token_file_path and os.path.exists(token_file_path):
        print(f"using saved token {token_file_path}")
        ga_auth = cls(
            creds=Credentials.from_authorized_user_file(token_file_path, scope),
            scope=scope,
        )
        ga_auth._refresh_token()
        return ga_auth

    flow = InstalledAppFlow.from_client_secrets_file(credentials_file_path, scope)

    ga_auth = cls(scope=scope)
    ga_auth._new_token_via_auth_flow(flow)
    return ga_auth
