#!/usr/bin/env python

import pika
import uuid
import json
import sys

from tabulate import tabulate


class AMQPClient(object):

    def __init__(self):
        self.rpcQueue = "store_job_queue"
        self.connection = pika.BlockingConnection(pika.ConnectionParameters(host = "rabbitmq"))
        self.channel = self.connection.channel()
        result = self.channel.queue_declare(queue = '', exclusive = True)
        self.callback_queue = result.method.queue
        self.channel.basic_consume(queue = self.callback_queue, on_message_callback = self.on_response, auto_ack = True)

    def on_response(self, ch, method, props, body):
        if self.corr_id == props.correlation_id:
            self.response = json.loads(body)

    def call(self, msg):
        self.response = None
        self.corr_id = str(uuid.uuid4())
        self.channel.basic_publish(exchange = '',
                                   routing_key = self.rpcQueue,
                                   properties = pika.BasicProperties(reply_to = self.callback_queue,
                                                                     correlation_id = self.corr_id,
                                                                    ),
                                   body = json.dumps(msg))

        while self.response is None:
            self.connection.process_data_events()
        return self.response


    def help(self):
        sys.exit("""
NAME
       vos_data

SYNOPSYS
       vos_data COMMAND USERNAME

DESCRIPTION
       The purpose of this client application is to notify to the VOSpace backend that
       data is ready to be saved somewhere.
       
       The client accepts only one (mandatory) command at a time.
       A list of supported commands is shown here below:

       cstore
              performs a 'cold storage' request, data will be saved on tape

       hstore
              performs a 'hot storage' request, data will be saved to disk

       The client also needs to know the username associated to a storage request process.
       The username must be the same used for accessing the transfer node.
    """)

    def store(self, cmd, username):
        request_type = cmd.upper()
        if request_type == "CSTORE":
            sys.exit("\nNo se pol (per ora).\n")
        storeRequest = { "requestType": request_type, "userName": username }
        print(f"\nSending {request_type} request...\n")
        storeResponse = self.call(storeRequest)
        if "responseType" not in storeResponse:
            sys.exit("FATAL: Malformed response, storage acknowledge expected.\n")
        elif storeResponse["responseType"] == "STORE_ACK":
            storageList = storeResponse["storageList"]
            if not storageList:
                sys.exit("No storage point found. Please add a storage point using the 'vos_storage' command.")
            print("Choose one of the following storage locations:")
            print()
            print(tabulate(storageList, headers = "keys", tablefmt = "pretty"))
            print()
            #print("----------------------------------------------------------------------")
            storageIdList = []
            for st in storageList:
                storageIdList.append(st["storage_id"])
                #print("[*] storage_id: {:<2d}   =>   hostname: {}".format(st['storage_id'], st['hostname']))
            storageId = None
            #print("----------------------------------------------------------------------\n")
            while not storageId in storageIdList:
                try:
                    storageId = input("Please, insert a storage id: ")
                    storageId = int(storageId)
                except ValueError:
                    print("Input type is not valid!")
                except EOFError:
                    print("\nPlease, use CTRL+C to quit.")
                except KeyboardInterrupt:
                    sys.exit("\nCTRL+C detected. Exiting...")
            print()
            print("!!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!")
            print("If you confirm, all your data on the transfer node will be")
            print("available in read-only mode for all the time the storage")
            print("process is running.")
            print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
            print()
            confirm = None
            while not confirm in [ "yes", "no"]:
                try:
                    confirm = input("Are you sure to proceed? [yes/no]: ")
                except KeyboardInterrupt:
                    sys.exit("\nCTRL+C detected. Exiting...")
                except EOFError:
                    print("\nPlease, use CTRL+C to quit.")
            if confirm == "yes":
                confirmRequest = { "requestType": "STORE_CON", "userName": username, "storageId": storageId }
                confirmResponse = self.call(confirmRequest)
                if "responseType" not in confirmResponse:
                    sys.exit("\nFATAL: Malformed response, storage confirmation expected.\n")
                elif confirmResponse["responseType"] == "STORE_RUN":
                    jobId = confirmResponse["jobId"]
                    print()
                    print(f"JobID: {jobId}")
                    print("Storage process started successfully!")
                    print()
                else:
                    sys.exit("FATAL: Unknown response type.\n")
            else:
                sys.exit("\nStorage process aborted gracefully.\n")
        elif storeResponse["responseType"] == "ERROR":
            errorCode = storeResponse["errorCode"]
            errorMsg = storeResponse["errorMsg"]
            sys.exit(f"Error code: {errorCode}\nError message: {errorMsg}\n")
        else:
            sys.exit("\nFATAL: Unknown response type.\n")


# Create new AMQPClient object
vosDataCli = AMQPClient()

# Check the number of input args
if len(sys.argv) == 3:
    script, cmd, username = sys.argv
else:
    vosDataCli.help()

# Check the command passed by the user
if cmd == "cstore" or cmd == "hstore":
    vosDataCli.store(cmd, username)
else:
    vosDataCli.help()
