# -*- coding: utf-8 -*-

"""
This program calls the Agyo webservices to perform all the needs for the b2b invoice system

Needs:

 Virtualenv with python 2.7 and zeep package installed

Params:

 -f --function      Function of the webAPI requested to call (ex. aziendeList)
 -w --wsdl          url of the webservice wsdl
 -j --json          input json file
 -o --output        output json file
 -u --unsafe        disable ssl verification over https
 -v --verbose       write some messages to log file

The result of the script is written in json format, to the output file.

Other 4 files are written by the program:
- raw xml request, with the same file name of the input json file with the extension .xml
- raw xml result, with the same file name of the output json file with the extension .xml
- error log, with the same file name of the output json file with the extension .err
- output log, with the same file name of the output json file with the extension .log

"""

from requests import Session
from requests.exceptions import SSLError
from requests.exceptions import HTTPError
from zeep import Client
from zeep.transports import Transport
from zeep.exceptions import Fault
from zeep import Plugin
import lxml.etree as etree
import datetime
import sys
import json


class Log:
    def __init__(self, flog, ferr):
        self.flog = flog
        self.ferr = ferr

    def message(self, message):
        with open(self.flog, 'a') as f:
            f.write("%s: %s\n" % (datetime.datetime.now(), message))

    def error(self, error):
        with open(self.ferr, 'a') as f:
            f.write("%s: %s\n" % (datetime.datetime.now(), error))
        self.message(error)


class XMLSniffer(Plugin):
    def __init__(self, frequest, fresult):
        self.frequest = frequest
        self.fresult = fresult

    def ingress(self, envelope, http_headers, operation):
        with open(self.fresult, 'w') as f:
            f.write(etree.tostring(envelope, pretty_print=True))
        return envelope, http_headers

    def egress(self, envelope, http_headers, operation, binding_options):
        with open(self.frequest, 'w') as f:
            f.write(etree.tostring(envelope, pretty_print=True))
        return envelope, http_headers


def errlog(msg):
    print >> sys.stderr, "%s" % (msg,)


def option_parsing():
    from optparse import OptionParser

    usage = "usage: %prog [options] argument"
    parser = OptionParser(usage=usage)
    parser.add_option("-f", "--function", dest="func", action="store", type="string",
                      help="requested service (ex: aziendeList) [REQUIRED]")
    parser.add_option("-w", "--wsdl", dest="wsdl", action="store", type="string",
                      help="url to invoke [REQUIRED]")
    parser.add_option("-j", "--json", dest="json", action="store", type="string",
                      help="input data in json format [REQUIRED]")
    parser.add_option("-o", "--output", dest="output", action="store", type="string",
                      help="output file name [REQUIRED]")
    parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
                      help="display verbose output")
    parser.add_option("-u", "--unsafe", action="store_false", dest="safe", default=True,
                      help="disable ssl cert verification")
    opz, args = parser.parse_args()
    if not opz.func:
        parser.error("-f param is required; try -h")
    if not opz.wsdl:
        parser.error("-w param is required; try -h")
    if not opz.json:
        parser.error("-j param is required; try -h")
    if not opz.json:
        parser.error("-o param is required; try -h")
    return opz


def call_the_service(wsdl, func, jfile, ofile, safe, verbose):
    flog = ofile.rsplit('.', 1)[0] + ".log"
    ferr = ofile.rsplit('.', 1)[0] + ".err"
    logger = Log(flog, ferr)
    try:
        with open(jfile) as f:
            if verbose:
                logger.message("Reading json input data from %s" % jfile)
            data = json.load(f)
    except IOError as e:
        logger.error("%s %s" % (e.strerror, e.filename))
        sys.exit(1)
    try:
        session = Session()
        session.verify = safe
        transport = Transport(session=session, timeout=45)
        xml_request = jfile.rsplit('.', 1)[0] + ".xml"
        xml_result = ofile.rsplit('.', 1)[0] + ".xml"
        client = Client(wsdl, transport=transport, plugins=[XMLSniffer(xml_request, xml_result)])
        # call the function by its name
        if verbose:
            logger.message("Sent %s request. See %s for content details." % (func, xml_request))
        open(ofile, 'w').write(str(getattr(client.service, func)(**data)))
        if verbose:
            logger.message("Received %s response. See %s for content details." % (func, xml_result))
        sys.exit(0)
    except Fault as e:
        logger.error(e.message)
        sys.exit(2)
    except SSLError:
        logger.error("SSL cert error. Try --unsafe option (at your own risk).")
        sys.exit(3)
    except HTTPError as e:
        logger.error("HTTPError: %s." % e.message)
        sys.exit(4)
    except Exception as e:
        logger.error(e.__class__)
        sys.exit(5)


if __name__ == "__main__":
    options = option_parsing()
    call_the_service(wsdl=options.wsdl, func=options.func, jfile=options.json, ofile=options.output,
                     safe=options.safe, verbose=options.verbose)
