"""gdrive.py

Interface to Google Drive using the Python API.
"""

#================================================================
# Dependencies:
#
# This module assumes the availability of the Google Drive API and credentials
# for a Google Cloud Project.
#
# General references:
#   https://developers.google.com/drive/api/v3/quickstart/python
#   https://developers.google.com/workspace/guides/create-project
#   https://developers.google.com/workspace/guides/create-credentials
#
# Library installation:
#   pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
#
# The associated project will need the Google Drive API enabled and an OAuth 2.0
# client ID.
#
#================================================================
import logging, io, os

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from apiclient.http import MediaFileUpload, MediaIoBaseDownload

# If modifying these scopes, delete the file token.json.

# SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
SCOPES = ['https://www.googleapis.com/auth/drive']

class GDrive(object):
    def __init__(self, config):
        logging.debug("Entering GDrive.__init__ and initializing superclasses")
        super(GDrive, self).__init__()
        self.creds = None
        self.service = None
        self.ready = False

        # this should be stored in a config file and not submitted to git
        self.submissions_folder_id = config.gdrive_submissions_folder_id
        self.previews_folder_id    = config.gdrive_previews_folder_id
        self.approved_folder_id    = config.gdrive_approved_folder_id
        self.token_path            = config.gdrive_token_file
        self.credentials_path      = config.gdrive_credentials_file
        return

    def authenticate_and_connect(self):
        try:
            # The file token.json stores the user's access and refresh tokens, and is
            # created automatically when the authorization flow completes for the first
            # time.
            if os.path.exists(self.token_path):
                self.creds = Credentials.from_authorized_user_file(self.token_path, SCOPES)

                # If there are no (valid) credentials available, let the user log in.
                if not self.creds or not self.creds.valid:
                    if self.creds and self.creds.expired and self.creds.refresh_token:
                        self.creds.refresh(Request())
                    else:
                        flow = InstalledAppFlow.from_client_secrets_file(self.credentials_path, SCOPES)
                        self.creds = flow.run_local_server(port=0)
                        # Save the credentials for the next run
                        with open(self.token_path, 'w') as token:
                            token.write(self.creds.to_json())
        except:
            logging.error('Failed to authenticate for Google Drive.')
            self.creds = None
            self.service = None
            return

        try:
            self.service = build('drive', 'v3', credentials=self.creds)
            self.ready = True

        except:
            logging.error('Failed to connect to Google Drive.')
            self.creds = None
            self.service = None
        return


    def _upload_file(self, path, mimetype, folder_id):
        filename = os.path.basename(path)
        file_metadata = {'name': filename, 'parents' : [folder_id] }
        try:
            media = MediaFileUpload(path, mimetype=mimetype)
            file = self.service.files().create(body=file_metadata, media_body=media, fields='id').execute()
            new_id = file.get('id')
            logging.info("Uploaded %s as ID %s", path, new_id)
            return new_id

        except Exception as exp:
            logging.error('Failed to upload %s to Google Drive: %s', path, exp)


    def upload_approved_image(self, path):
        return self._upload_file(path, 'image/png', self.approved_folder_id)

    def upload_preview_video(self, path):
        return self._upload_file(path, 'video/mp4', self.previews_folder_id)

    def download_submitted_image(self, file_id):
        try:
            request = self.service.files().get_media(fileId=file_id)
            fh = io.BytesIO()
            downloader = MediaIoBaseDownload(fh, request)
            done = False
            while done is False:
                status, done = downloader.next_chunk()
            return fh.getvalue()

        except Exception as exp:
            logging.error('Failed to download image %s from Google Drive: %s', file_id, exp)
            return None
