"""Execute command: send request to service."""
import inspect
import os
import sys

import apiclient
import googleapiclient
import httplib2

from .. import auth
from .. import common
from .. import config
from .. import lib

def add_parser(main_parser, name):
    """Add specific execute command parser."""
    parser = main_parser.add_parser(name)

    parser.add_argument('-c', '--client-secret-file', metavar="PATH",
                        help="Use a client secret JSON file")
    parser.add_argument('-f', '--media-file', metavar="PATH",
                        help='File to use for media-related methods')
    parser.add_argument('--browser-auth', action="store_true",
                        help="Use a browser to authentify")
    parser.add_argument('--credentials-file',
                        metavar="PATH", help="Select credentials file to use")
    parser.add_argument('--credentials-profile', default="default",
                        metavar="NAME", help="Select credentials profile to use")
    parser.add_argument('api_path', metavar="API_PATH",
                        help="SERVICE:VERSION.RESOURCE.METHOD")
    parser.add_argument('json_request', metavar="JSON_FILE",
                        help="File containing the request JSON (use '-' to read from STDIN)")

def run(options):
    """Run command execute."""
    service_id, resource_name, method_name = lib.pad_list(options.api_path.split(".", 2), 3)
    request_fd = (sys.stdin if options.json_request == "-" else open(options.json_request))
    method_options = lib.load_json(request_fd.read())
    try:
        response = do_request(service_id, resource_name, method_name, method_options, options)
        lib.output(lib.pretty_json(response))
    except TypeError as error:
        frm = inspect.trace()[-1]
        mod = inspect.getmodule(frm[0])
        if mod.__name__ == 'googleapiclient.discovery':
            config.logger.error("googleapiclient.discovery: {}".format(error))
        else:
            raise

def execute_media_request(request):
    """Process a request containing a MediaFileUpload."""
    while 1:
        status, response = request.next_chunk()
        if status:
            config.logger.debug("MediaFileUpload status: {}".format(status))
        if response:
            return response

def build_service(service_id, credentials):
    """Return service object from ID and credentials."""
    base_http = httplib2.Http()
    http = (credentials.authorize(base_http) if credentials else base_http)
    service_name, version = service_id.split(":", 1)
    return googleapiclient.discovery.build(service_name, version, http=http)

def get_credentials(scopes, options):
    """Return path of the reusable credentials JSON file for given scopes."""
    if scopes and options.client_secret_file:
        if options.credentials_file:
            if os.path.exists(options.credentials_file):
                credentials_path = options.credentials_file
            else:
                msg = "Credentials file not found: {}".format(options.credentials_file)
                raise common.ShoogleException(msg)
        else:
            credentials_path = common.get_credentials_path(scopes, options.credentials_profile)
        if options.browser_auth:
            from shoogle.auth import browser
            get_code = auth.browser.get_code
        else:
            from shoogle.auth import console
            get_code = auth.console.get_code
        client_secret = options.client_secret_file
        return auth.get_credentials(client_secret, credentials_path, scopes, get_code)
    else:
        return None

def get_method_options_with_media(method_options, media_file):
    """Return options to send the method caller from base options and media file."""
    media_body = apiclient.http.MediaFileUpload(
        media_file,
        chunksize=-1,
        resumable=True,
        mimetype="application/octet-stream",
    )
    media_file_field = "MediaFileUpload({})".format(media_file)
    printable_request = lib.merge(method_options, {"media_body": media_file_field})
    config.logger.debug("Request: " + lib.pretty_json(printable_request))
    return lib.merge(method_options, {"media_body": media_body})

def do_request(service_id, resource_name, method_name, method_options, options):
    """Send request to API and return JSON response."""
    service = common.get_service(service_id)
    method = common.get_method(service, resource_name, method_name)

    if method.get("request") and "body" not in method_options:
        raise common.ShoogleException("This method need a body property in the request")
    elif method.get("supportsMediaUpload") and not options.media_file:
        raise common.ShoogleException("This method requires a media file (--media-file=PATH)")
    else:
        scopes = method.get("scopes", [])
        credentials = get_credentials(scopes, options)
        service_obj = build_service(service_id, credentials)
        resource_func = getattr(service_obj, resource_name)
        method_func = getattr(resource_func(), method_name)

        if options.media_file:
            method_options_with_media = \
                get_method_options_with_media(method_options, options.media_file)
            request = method_func(**method_options_with_media)
            return execute_media_request(request)
        else:
            config.logger.debug("Request: " + lib.pretty_json(method_options))
            request = method_func(**method_options)
            return request.execute()