#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright (c) 2015 Red Hat
# Licensed under The MIT License (MIT)
# http://opensource.org/licenses/MIT
#
# It's better to copy this script to $PATH.
try:
    import json
except ImportError:
    import simplejson as json

import optparse
import sys

from beanbag import BeanBagException
import pdc_client
from pdc_client import PDCClient

__version__ = pdc_client.get_version()

def debug_request(func):
    def wrap(*args, **kwargs):
        self = args[0]
        if self.debug:
            print("> ", func.__name__.upper(), self.client[self.resource]._)
            for key, value in list(self.client.session.headers.items()):
                print("> ", key, ": ", value)
        return func(*args, **kwargs)

    return wrap


class RequestMethod(object):
    def __init__(self, client, resource, traceback=False, debug=False):
        self.client = client
        self.resource = resource
        self.traceback = traceback
        self.debug = debug

    def dispatch(self, method, request_data):
        func = getattr(self, method.lower(), None)

        if func is None:
            raise ValueError(
                "Request method %s not support in pdc client." % options.request)

        return func(request_data)

    @debug_request
    def get(self, data):
        if not isinstance(data, dict):
            print('For GET request, the data must be a JSON object, not a list.')
            sys.exit(1)
        return self.client[self.resource]._(**data)

    @debug_request
    def post(self, data):
        return self.client[self.resource]._(data)

    @debug_request
    def put(self, data):
        return self.client[self.resource]._("PUT", data)

    @debug_request
    def patch(self, data):
        return self.client[self.resource]._("PATCH", data)

    @debug_request
    def delete(self, data):
        response = self.client[self.resource]._("DELETE", data)
        if response:
            return response
        return {"Response": "No content"}

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if all((exc_type, exc_val, exc_tb)):
            if exc_type is BeanBagException:
                print("%d %s" % (
                    exc_val.response.status_code, exc_val.response.content))

            if self.traceback:
                import traceback
                traceback.print_exception(exc_type, exc_val, exc_tb)

        return True


def load_data(options):
    """
    If --file was specified, load data from file or stdin, otherwise return
    what was given to --data option.

    This function returns Python data structure. If parsing JSON fails, the
    client will abort and print an error message.
    """
    if options.file:
        if options.file == '-':
            data = sys.stdin.read()
        else:
            with open(options.file, "r") as f:
                data = f.read()
    elif options.data:
        data = options.data
    else:
        return {}
    try:
        return json.loads(data)
    except ValueError as err:
        print("Request data is not a valid JSON input.")
        print(err)
        sys.exit(1)


if __name__ == "__main__":
    parser = optparse.OptionParser(version=__version__)
    parser.add_option("-s", "--server", help="PDC instance url or shortcut.")
    parser.add_option("-x", "--request", default="GET", help="Request method.")
    parser.add_option("-r", "--resource",
                      help=("Resource name.\n" +
                            "NOTE: All available resources can be got by a request " +
                            "to the API Root. Since API Root does not belong to any " +
                            "resources, while requesting, please omit the `-r` option.\n" +
                            "The response of API Root request is a dict with resource names " +
                            "as keys and resource URIs as values."),
                      default="")
    parser.add_option("-d", "--data", help="Request data to server.")
    parser.add_option("-f", "--file",
                      help="File to load request data from (or - for stdin).")
    parser.add_option("-t", "--traceback",
                      help="Show the traceback when an error occurs",
                      action="store_true", default=False)
    parser.add_option("--debug", help="Show request headers and path for "
                                      "debugging.",
                      action="store_true", default=False)
    parser.add_option("-c", "--comment", help="Reasons for the PDC change.")
    options, args = parser.parse_args()

    if len(args):
        print("Can not parse the positional arguments: %s" % ", ".join(args))
        parser.print_help()
        sys.exit(1)

    if not options.server:
        print("Error: -s is required.\n")
        parser.print_help()
        sys.exit(1)

    if options.data and options.file:
        print("Error: can not load data from file and command line at the same time.")
        sys.exit(1)

    data = load_data(options)

    if isinstance(data, dict):
        for key, value in data.items():
            if options.request.upper() == 'GET' and not value and value is not False:
                # empty string won't be lost
                data[key] = ''

    try:
        client = PDCClient(options.server)
        if options.comment:
            client.set_comment(options.comment)
    except BeanBagException as e:
        print("%d %s" % (e.response.status_code, e.response.content))
    except Exception as e:
        print(str(e))
    else:
        with RequestMethod(client, options.resource,
                           options.traceback, options.debug) as request:
            print(json.dumps(request.dispatch(options.request, data),
                             indent=4, sort_keys=True))
