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

# %% auto 0
__all__ = ['get_file_by_id', 'get_folder_by_id', 'walk_folder', 'GDrive_MimeType_Enum', 'ExportAs_Enum', 'GDrive_ExportError',
           'export_file_as', 'GDrive_ServiceRequired', 'GDrive_File', 'GDrive_Files']

# %% ../../nbs/google/GDrive.ipynb 2
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Any, Optional

import datetime as dt


from googleapiclient.errors import HttpError

from domolibrary_extensions.utils.utils import (
    upsert_folder,
    convert_str_to_date,
    convert_str_file_name,
    download_zip,
    download_pptx,
)

import domolibrary_extensions.google.GAuth as ga

from nbdev.showdoc import patch_to

# %% ../../nbs/google/GDrive.ipynb 5
def get_file_by_id(service, file_id, fields: List[str] = None, parent_ls=None):
    parent_ls = parent_ls or []

    fields = fields or [
        "id",
        "webViewLink",
        "name",
        "mimeType",
        " modifiedTime",
        "parents",
    ]

    file = service.files().get(fileId=file_id, fields=", ".join(fields)).execute()

    parent_ls += [
        parent for parent in file.get("parents", []) if parent not in parent_ls
    ]

    file.update({"parent_ls": parent_ls})
    return file

# %% ../../nbs/google/GDrive.ipynb 6
def get_folder_by_id(
    service,
    folder_id,
    parent_ls: Optional[
        List[str]
    ] = None,  # used by recursive query to build path to file
):
    parent_ls = parent_ls or []
    file_ls = []
    page_token = None

    while True:
        res = (
            service.files()
            .list(
                q=f"'{folder_id}' in parents",
                pageSize=10,
                fields="nextPageToken, files(id,webViewLink, name, mimeType,modifiedTime, parents )",
                pageToken=page_token,
            )
            .execute()
        )

        file_ls.extend(res.get("files", []))

        page_token = res.get("nextPageToken", None)
        if not page_token:
            break

    for file in file_ls:
        parent_ls += [
            parent for parent in file.get("parents", []) if parent not in parent_ls
        ]
        file.update({"parent_ls": parent_ls})

    return file_ls

# %% ../../nbs/google/GDrive.ipynb 7
def walk_folder(
    service,
    folder_id: str,
    file_ls: List[dict],
    is_recursive: bool = True,
    parent_ls: Optional[List[str]] = None,
):  # recursive function that retrieves all files in subfolders

    parent_ls = parent_ls or [folder_id]
    new_files = get_folder_by_id(service, folder_id, parent_ls=parent_ls)
    file_ls.extend(new_files)

    if new_files and is_recursive:
        [
            walk_folder(
                service=service,
                folder_id=file_obj["id"],
                file_ls=file_ls,
                parent_ls=[
                    *parent_ls,
                    *[
                        parent
                        for parent in file_obj["parent_ls"]
                        if parent not in parent_ls
                    ],
                ],
            )
            for file_obj in new_files
            if file_obj.get("mimeType") == "application/vnd.google-apps.folder"
        ]

# %% ../../nbs/google/GDrive.ipynb 9
class GDrive_MimeType_Enum(Enum):
    """for translating Google Drive types into 'exports as'"""

    docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation"

    odt = "application/vnd.oasis.opendocument.text"
    rtf = "application/rtf"
    pdf = "application/pdf"
    txt = "text/plain"

    zip_file = "application/zip"
    epub = "application/epub+zip"

    folder = "application/vnd.google-apps.folder"
    gdoc = "application/vnd.google-apps.document"
    gsheet = "application/vnd.google-apps.spreadsheet"
    gshortcut = "application/vnd.google-apps.shortcut"
    gslides = "application/vnd.google-apps.presentation"

    # html = 'application/zip'
    # 'tex'  = 'application/zip'
    # 'html.zip'= 'application/zip'


class ExportAs_Enum(Enum):
    pptx = {
        "download_fn": download_pptx,
        "export_mime_type": GDrive_MimeType_Enum.pptx,
    }
    default = {
        "download_fn": download_zip,
        "export_mime_type": GDrive_MimeType_Enum.zip_file,
    }

    @classmethod
    def _missing_(
        cls, value
    ):  # if export as not defined, will return default of export as zip
        return cls.default

# %% ../../nbs/google/GDrive.ipynb 10
class GDrive_ExportError(Exception):
    def __init__(self, doc_url):
        message = f"failure to download content for {doc_url}"
        super().__init__(message)


def export_file_as(service, file_id, export_as: ExportAs_Enum, output_path):

    export_mime_type, download_fn = (
        export_as.value["export_mime_type"].value,
        export_as.value["download_fn"],
    )

    loop = 0
    content = None
    while loop < 3 and content is None:
        loop += 1
        try:
            content = (
                service.files()
                .export(fileId=file_id, mimeType=export_mime_type)
                .execute()
            )

        except HttpError as err:
            print(err)

        except Exception as e:
            print(e)

    if not content:
        raise GDrive_ExportError(file_id)

    upsert_folder(output_path)

    return download_fn(content, output_path)

# %% ../../nbs/google/GDrive.ipynb 13
class GDrive_ServiceRequired(Exception):
    def __init__(self, doc_url=None, doc_id=None):
        message = f"service (googleclientapi.discovery.Resource) required to download {doc_url or doc_id}"
        super().__init__(message)


@dataclass
class GDrive_File:
    id: str
    name: str

    modified_time: dt.datetime
    url: str

    auth: ga.GoogleAuth = field(repr=False)
    service: Any = field(repr=False)

    mime_str: str

    parent_ls: List[str] = field(default=None)

    gdrive_mime_type: GDrive_MimeType_Enum = None
    gdrive_parents: List[str] = field(default=None, repr=False)

    file: Any = field(repr=False, default=None)

    def __post_init__(self):
        try:
            self.gdrive_mime_type = GDrive_MimeType_Enum(self.mime_str)

        except Exception as e:
            print(e)

        if self.parent_ls:
            self.gdrive_parents = [
                GDrive_File.get_by_id(
                    document_id=parent, auth=self.auth, service=self.service
                )
                for parent in self.parent_ls
            ]

    @classmethod
    def _from_json(cls, obj: dict, auth: ga.GoogleAuth, service: None):
        return cls(
            auth=auth,
            service=service,
            file=obj,
            mime_str=obj["mimeType"],
            url=str(obj["webViewLink"]),
            id=obj["id"],
            name=obj["name"],
            parent_ls=obj["parent_ls"],
            modified_time=convert_str_to_date(obj["modifiedTime"]),
        )

    @classmethod
    def get_by_id(
        cls,
        document_id: str,
        auth: ga.GoogleAuth = None,
        service=None,
        return_raw: bool = False,
    ):
        service = service or auth.generate_service("drive", "v3")

        file = get_file_by_id(file_id=document_id, service=service)

        if return_raw:
            return file

        return cls._from_json(obj=file, auth=auth, service=service)

    def export(self, output_path, export_as: ExportAs_Enum = ExportAs_Enum.default):

        return export_file_as(
            service=self.service,
            file_id=self.id,
            export_as=export_as,
            output_path=output_path,
        )

# %% ../../nbs/google/GDrive.ipynb 18
@dataclass
class GDrive_Files:

    auth: ga.GoogleAuth = field(repr=False)

    folder_id: str = None

    gdrive_files: List[GDrive_File] = field(default_factory=lambda: [])

    service: ga.Resource = field(default=None, repr=False)

    def __post_init__(self):

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

    def get_files(self, folder_id, is_recursive: bool = True, return_raw: bool = False):
        file_ls = []

        walk_folder(
            service=self.service,
            folder_id=folder_id,
            file_ls=file_ls,
            is_recursive=is_recursive,
        )

        if return_raw:
            return file_ls

        self.gdrive_files = [
            GDrive_File._from_json(obj=obj, auth=self.auth, service=self.service)
            for obj in file_ls
        ]

        return self.gdrive_files
