import sys
import json
import logging
import getpass
from argparse import ArgumentParser, ArgumentTypeError, Action
from ucampurestorage.lib.tokencreater import TokenCreater as token
from ucampurestorage.lib.pureconnect import PureAdvanceConnection as PureAdvance
from ucampurestorage.lib.process import Process
from ucampurestorage import VERSION

LOG = logging.getLogger(__name__)


class LoadFromFile(Action):
    def __call__(self, parser, namespace, values, option_string=None):
        with values as f:
            args = json.load(f)

        for k, v in args.items():
            setattr(namespace, k, v)


def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ("yes", "true", "t", "y", "1"):
        return True
    elif v.lower() in ("no", "false", "f", "n", "0"):
        return False
    else:
        raise ArgumentTypeError("Boolean value expected.")


class Options:
    def __init__(self):
        self.process = Process()
        self._init_parser()

    def _init_parser(self):
        """Define arguments for the CLI."""
        self.parser = ArgumentParser(
            description="Manage Pure Storage Manager objects via REST API."
        )
        self.parser.add_argument("-v", "--version", action="version", version=VERSION)
        self.parser.add_argument(
            "--storage",
            default="uis-pure-wcdc1.srv.uis.private.cam.ac.uk",
            type=str,
            help="Pure hostname. Default: uis-pure-wcdc1.srv.uis.private.cam.ac.uk",
        )
        self.parser.add_argument("--port", default=443, help="Pure port. Default: 443")
        self.parser.add_argument(
            "--client_id", type=str, help="Pure Storage Client ID for API"
        )
        self.parser.add_argument(
            "--key_id", type=str, help="Pure Storage Key ID for API"
        )
        self.parser.add_argument(
            "--client_name", type=str, help="Pure Storage Client Name for API"
        )
        self.parser.add_argument("--user", type=str, help="Pure Storage username")
        self.parser.add_argument("--password", type=str, help="Pure Storage password")
        self.parser.add_argument(
            "--is_secure",
            type=str2bool,
            default=False,
            help="Secure connection. Default: False",
        )
        self.parser.add_argument(
            "--record_config",
            type=str2bool,
            default=False,
            help="Record multipath and file system config details.\
                                 Default: False (do not record system config details)",
        )
        self.parser.add_argument(
            "--file",
            type=open,
            action=LoadFromFile,
            help="Read arguments from json file",
        )
        self.parser.add_argument(
            "--keyfile",
            type=str,
            help="path to private keys inorder to generate token for the API call",
        )
        self.subparsers = self.parser.add_subparsers(dest="subparser")
        self.parser_tokengen = self.subparsers.add_parser(
            "tokengen",
            help='Generate Pure Storage API token to access objects. \
                                                      Example: ucampurestorage tokengen --keyfile "./key.pem"',
        )
        self.parser_tokengen.add_argument(
            "-inkeyfile",
            "--newkeyfile",
            type=str,
            help="path to the new private key location to make pure storage call",
        )
        self.parser_tokengen.add_argument(
            "-out",
            "--outputfile",
            type=str,
            help="path and file name to generate token file in json as output",
        )
        self.parser_list = self.subparsers.add_parser("list", help="List Pure objects.")
        self.parser_list.add_argument(
            "--object",
            required=True,
            choices=[
                "arrays",
                "controllers",
                "hosts",
                "volumes",
                "destroyed_volumes",
                "volumegroups",
                "hostgroups",
                "volumesnapshots",
                "targetports",
                "networkports",
                "volumeconnections",
                "protectiongroups",
                "protectiongroupsnapshots",
                "protectiongroupvolmembers",
                "pods",
                "remotepods",
                "localmps",
            ],
        )
        self.parser_list.add_argument(
            "--format",
            choices=["json"],
            help="Format of the output. Supported formats: json",
        )
        self.parser_volume_operations = self.subparsers.add_parser(
            "volume", help="Operations with respect to volumes in the Pure objects."
        )
        self.volumeoperation = self.parser_volume_operations.add_subparsers(
            dest="volumeoperation"
        )
        self.listvol = self.volumeoperation.add_parser(
            "list", help="list a Volume details"
        )
        self.listvol.add_argument(
            "--name",
            required=False,
            help="Detail information of the volume using name from Pure Array.",
        )
        self.listvol.add_argument(
            "--wwn",
            required=False,
            help="Detail information of the volume using wwn from Pure Array.",
        )
        self.listvol.add_argument(
            "--format",
            required=False,
            help="Detail information of the volume on Pure Array in json format.",
        )
        self.listclones = self.volumeoperation.add_parser(
            "listclones", help="list a Clones of a Volume details"
        )
        self.listclones.add_argument(
            "--name",
            required=False,
            help="Retreive information of the clone volumes of provided souce volume name on Pure Array.",
        )
        self.listclones.add_argument(
            "--wwn",
            required=False,
            help="Retreive information of the clone volumes of provided souce volume name on Pure Array.",
        )
        self.listclones.add_argument(
            "--format",
            required=False,
            help="Detail information of the volume on Pure Array.",
        )
        self.createvol = self.volumeoperation.add_parser("create", help="create Volume")
        self.createvol.add_argument(
            "--size",
            required=True,
            help="Size of the volume to be created on Pure Array. \
            [Units: \"m\" for Megabytes, \"g\" for Gigabytes, \"t\" for Terabytes]",
        )
        self.createvol.add_argument(
            "--name",
            required=True,
            help="Name of the volume to be created on Pure Array.",
        )
        self.deletevol = self.volumeoperation.add_parser("delete", help="delete Volume")
        self.deletevol.add_argument(
            "--name",
            required=False,
            help="Name of the volume to be deleted on Pure Array.",
        )
        self.deletevol.add_argument(
            "--wwn",
            required=False,
            help="WWN of the volume to be deleted on Pure Array.",
        )
        self.deletevol.add_argument(
            "-nop",
            "--no_prompt",
            required=False,
            action="store_true",
            help="No prompt of validation for deletion of volume.",
        )
        self.eradicatevol = self.volumeoperation.add_parser(
            "eradicate", help="eradicate Volume"
        )
        self.eradicatevol.add_argument(
            "--name",
            required=True,
            help="Name of the volume to be eradicate on Pure Array.",
        )
        self.eradicatevol.add_argument(
            "-nop",
            "--no_prompt",
            required=False,
            action="store_true",
            help="No prompt of validation for eradicate of volume.",
        )
        self.connectvol = self.volumeoperation.add_parser(
            "connect", help="Connect Volume to Host"
        )
        self.connectvol.add_argument(
            "--hostname",
            required=True,
            help="Name of the host specified on Pure Array for initiators to which the volume has to be mapped.",
        )
        self.connectvol.add_argument(
            "--volname",
            required=True,
            help="Name of the volume to be connected to the host initiators on Pure Array.",
        )
        self.connectvol.add_argument(
            "--lunid",
            help="HOSTID need to be assigned for the volume while the  host initiators on Pure Array.",
        )
        self.disconnectvol = self.volumeoperation.add_parser(
            "disconnect", help="disconnect Volume from Host"
        )
        self.disconnectvol.add_argument(
            "--hostname",
            required=True,
            help="Name of the host specified on Pure Array for initiators to which the volume has to be unmapped.",
        )
        self.disconnectvol.add_argument(
            "--volname",
            required=True,
            help="Name of the volume to be disconnect from the host initiators of Pure Array.",
        )
        self.mapvol = self.volumeoperation.add_parser(
            "map",
            help="Maps the volume to local server and \
                                                      mounts it on the specified mount point.",
        )
        self.mapvol.add_argument(
            "--name", required=True, help="Name of the volume to map to local server."
        )
        self.mapvol.add_argument(
            "--mp",
            required=False,
            help="OPTIONAL. If specified, it is the path of the mountpoint \
                                            to mount the volume on. Otherwise, no mount attempt will take place.",
        )
        self.mapvol.add_argument(
            "-new",
            "--new_volume",
            required=False,
            action="store_true",
            help="NOTE: If volume is create but never formated then use this flag",
        )
        self.unmapvol = self.volumeoperation.add_parser(
            "unmap",
            help="Unmaps the volume from local server and \
                                                      Volume must be unmounted.",
        )
        self.unmapvol.add_argument(
            "--name",
            required=False,
            help="Name of the volume to unmap from local server.",
        )
        self.unmapvol.add_argument(
            "--wwn",
            required=False,
            help="OPTIONAL: WWN of the volume to unmap from local server.",
        )
        self.unmapvol.add_argument(
            "--mp",
            required=False,
            help="OPTIONAL: mount point of the local sever to be unmap from local server.",
        )
        self.replacevol = self.volumeoperation.add_parser(
            "replace",
            help="Clone the volume mounted on source mountpoint and mount it on destination mountpoint. \
                Then, delete the volume was initially mounted on destination mountpoint. ",
        )
        self.replacevol.add_argument(
            "--src_mp",
            required=True,
            help="Source mountpoint. Must be on local server.",
        )
        self.replacevol.add_argument(
            "--dst_mp",
            required=True,
            help="Destination mountpoint. Must be on local server.",
        )
        self.replacevol.add_argument(
            "--unmount_src_mp",
            type=str2bool,
            default=True,
            help="Optional. If False, do not unmount the source mountpoint. \
                                        Defaults to True",
        )
        self.clonevol = self.volumeoperation.add_parser(
            "clone",
            help="Create a snapshot with label of a volume with WWN, create a view volume from the snapshot, \
                then, map the view volume to local server and  mount it on target mountpoint.",
        )
        self.clonevol.add_argument(
            "--name",
            required=True,
            help="Name of the new volume to create",
        )
        self.clonevol.add_argument(
            "--srcwwn",
            metavar="Source Volume WWN",
            required=False,
            help="Source Volume WWN for which need to be cloned.",
        )
        self.clonevol.add_argument(
            "--srcvol",
            metavar="Source Volume",
            required=False,
            help="Source Volume for which need to be cloned.",
        )
        self.clonevol.add_argument(
            "--target_mp",
            metavar="Target Mount Point",
            required=False,
            help="The mountpoint that the view volume will be mounted on. \
                Must be on local server Eg: /d01",
        )
        self.parser_host_operations = self.subparsers.add_parser(
            "host", help="Operations with respect to hosts in the Pure objects."
        )
        self.hostoperation = self.parser_host_operations.add_subparsers(
            dest="hostoperation"
        )
        self.listhost = self.hostoperation.add_parser(
            "list", help="list a host details"
        )
        self.listhost.add_argument(
            "--name",
            required=True,
            help="Detail information of the host on Pure Array.",
        )
        self.listhost.add_argument(
            "--format",
            required=False,
            help="Detail information of the host on Pure Array.",
        )
        self.createhost = self.hostoperation.add_parser("create", help="create Volume")
        self.createhost.add_argument(
            "--iqn", required=False, help="IQN of the host to be created on Pure Array."
        )
        self.createhost.add_argument(
            "--name",
            required=True,
            help="Name of the host to be created on Pure Array.",
        )
        self.createhost.add_argument(
            "--personality",
            required=False,
            default="none",
            choices=[
                "aix",
                "esxi",
                "hpux",
                "solaris",
                "vms",
                "oracle-vm-server",
                "none",
            ],
            help="Personality of the host to be created on Pure Array.",
        )
        self.deletehost = self.hostoperation.add_parser("delete", help="delete host")
        self.deletehost.add_argument(
            "--name",
            required=True,
            help="Name of the host to be deleted on Pure Array.",
        )
        self.deletehost.add_argument(
            "-nop",
            "--no_prompt",
            required=False,
            action="store_true",
            help="No prompt of validation for deletion of host.",
        )
        self.parser_snapshot_operations = self.subparsers.add_parser(
            "snapshot", help="Operations with respect to snapshots in the Pure objects."
        )
        self.snapoperation = self.parser_snapshot_operations.add_subparsers(
            dest="snapoperation"
        )
        self.listsnap = self.snapoperation.add_parser(
            "list", help="list a snapshots of the volume"
        )
        self.listsnap.add_argument(
            "--volname",
            required=True,
            help="information of the snapshot of the volume on Pure Array.",
        )
        self.listsnap.add_argument(
            "--format",
            required=False,
            help="information of the snapshot on Pure Array in json. Ex: --format=json",
        )
        self.createsnap = self.snapoperation.add_parser(
            "create", help="Create a snapshots of the volume"
        )
        self.createsnap.add_argument(
            "--srcvol",
            metavar="Source Volume Name",
            required=False,
            help="name of the volume for which snapshot need to be captured on Pure Array.",
        )
        self.createsnap.add_argument(
            "--srcwwn",
            metavar="Source Volume WWN",
            required=False,
            help="Source Volume WWN for which snapshot need to be created on Pure Array.",
        )
        self.createsnap.add_argument(
            "--suffix",
            required=True,
            help="suffix of the snapshot of the volume on Pure Array.",
        )
        self.deletesnap = self.snapoperation.add_parser(
            "delete", help="delete a snapshots of the volume"
        )
        self.deletesnap.add_argument(
            "--snapname",
            required=True,
            help="name of the snapshot need to be deleted on Pure Array.",
        )

    def parse(self, args=None):
        """Parse given arguments and execute an action accordingly."""
        result = None
        self.known, self.unknown = self.parser.parse_known_args(args)[:]
        if len(self.unknown) != 0:
            LOG.error(
                f"Specified parameter is not suitable: \n \t \t \t \t {self.unknown}"
            )
            return False

        def _checkuserparms(data: dict):
            for key, value in data.items():
                if getattr(self.known, key) is None:
                    inputvalue = input(f"{value}: ")
                    setattr(self.known, key, inputvalue)

        check_dict = {
            "user": "Username",
            "client_id": "Client ID",
            "key_id": "Key ID",
            "client_name": "Client Name",
            "keyfile": "Private file location",
        }

        _checkuserparms(check_dict)

        if self.known.password is None:
            self.known.password = getpass.getpass()

        # If enabled, record system config before running ucamdsm command
        if self.known.record_config:
            self.process.record_config_details()

        result = run_option(self.known.subparser, self.known)

        # If enabled, record system config after running ucamdsm command
        if self.known.record_config:
            self.process.record_config_details()

        return result


def _token_object(args):
    token_obj = token()
    if token_obj.current_token is None:
        if hasattr(args, "outputfile"):
            token_obj.token_generation(
                args.client_id,
                args.key_id,
                args.client_name,
                args.keyfile,
                args.user,
                args.storage,
                args.password,
                args.outputfile,
            )
        else:
            token_obj.token_generation(
                args.client_id,
                args.key_id,
                args.client_name,
                args.keyfile,
                args.user,
                args.storage,
                args.password,
                None,
            )
    return token_obj


def create_token(args):
    """
    Generate Pure Storage token.

    Example: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>
        --client_name=<ClientName> --keyfile="privatekey.pem"  tokengen

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  tokengen
    """
    token_obj = _token_object(args)
    if token_obj.current_token:
        LOG.info(f"\nAccess token for {args.client_name}:\n\n{token_obj.current_token}")
        return True
    else:
        return False


def run_option(subparser, args):
    """
    Runs a ucampurestorage subcommand.
    :param subparser: the subcommand to run.
    :param args: args to pass to the ucampurestorage subcommand.
    :return: result of the executed subcommand.
    """
    function_switcher = {
        "tokengen": create_token,
        "list": list_object,
        "volume": volume_operations,
        "host": host_operations,
        "snapshot": snap_operations,
    }
    subcommand_func = function_switcher.get(subparser)
    return subcommand_func(args) if subcommand_func is not None else None


def list_object(args):
    """
    List Pure Storage objects.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>
        --client_name=<ClientName> --keyfile="privatekey.pem" list --object controllers

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json list --object controllers

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json list --object controllers --format=json
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    (result, data) = PureObj.run_list_object(args.object)
    if not result or data is None:
        return result
    if args.format is None:
        LOG.info(
            f"List of {args.object}: \n{chr(10).join(str(item) for item in data.items())}"
        )
    if args.format == "json":
        LOG.info(
            f"List of {args.object}:\n {json.dumps(data, indent=4, sort_keys=False)}"
        )
    return result


def volume_operations(args):
    """
    Perform Pure Storage volume level Operations.

    Example: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume {list,create,delete,eradicate,connect,\
                                        disconnect,map,unmap,replace,clone}

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume {list,create,delete,eradicate,\
                                        connect,disconnect,map,unmap,replace,clone}
    """
    volume_operations = {
        "list": detail_volume,
        "create": create_volume,
        "connect": connect_volume,
        "disconnect": disconnect_volume,
        "map": map_volume,
        "unmap": unmap_volume,
        "delete": delete_volume,
        "replace": replace_volume,
        "clone": clone_volume,
        "eradicate": eradicate_volume,
        "listclones": clone_volumes,
    }
    subcommand_func = volume_operations.get(args.volumeoperation)
    return subcommand_func(args) if subcommand_func is not None else None


def host_operations(args):
    """
    Perform Pure Storage host level Operations.

    Example: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>
        --client_name=<ClientName> --keyfile="privatekey.pem"  host {list,create,delete}

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  host {list,create,delete}
    """
    host_operations = {
        "list": detail_host,
        "create": create_host,
        "delete": delete_host,
    }
    subcommand_func = host_operations.get(args.hostoperation)
    return subcommand_func(args) if subcommand_func is not None else None


def snap_operations(args):
    """
    Perform Pure Storage Snapshot level Operations.

    Example: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  snapshot {list,create,delete}

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot {list,create,delete}
    """
    snap_operations = {
        "list": list_snapshots,
        "create": create_snapshot,
        "delete": delete_snapshot,
    }
    subcommand_func = snap_operations.get(args.snapoperation)
    return subcommand_func(args) if subcommand_func is not None else None


def detail_host(args):
    """
    List the detail information of the host connected to the  Pure Storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  host  list  --name cs-dev-db2

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  host list --name cs-dev-db2

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json  host list --name cs-dev-db2 --format=json
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.get_hosts(args.name)
    data = PureObj.hosts
    if not result or data is None:
        return result
    if args.format is None:
        LOG.info(
            f"Details of {args.name}: \n{chr(10).join(str(item) for item in data.items())}"
        )
        return result
    if args.format == "json":
        LOG.info(
            f"Details of {args.name}:\n {json.dumps(data, indent=4, sort_keys=False)}"
        )
        return result


def create_host(args):
    """Create host defination on the Purestorage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  host  create  --name <HOSTNAME> --iqn <IQN number>\
            --personality <aix,esxi,hpux,solaris,vms,oracle-vm-server,none>

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json host create --name <HOSTNAME> \
            --iqn <IQN number> --personality <aix,esxi,hpux,solaris,vms,oracle-vm-server,none>
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.create_host(args.iqn, args.name, args.personality)
    if result:
        LOG.info(
            f'Successfully "{args.name}" created : "{args.iqn}" with personality "{args.personality}"'
        )
    else:
        LOG.error(f'Failure in creation: "{args.name}" !!')
    return result


def delete_host(args):
    """Delete host defination on the Pure Storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  host  delete  --name <HOSTNAME>

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json host delete --name <HOSTNAME>
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if not args.no_prompt:
        valid_inputs = ["y", "yes", "n", "no"]
        invalid = True
        while invalid:
            validate_deletion = input(
                f"Are you sure you want to delete {args.name} [Y/N](yes/no) : "
            )
            user_entry = validate_deletion.lower()
            if user_entry in valid_inputs and user_entry in valid_inputs[:2]:
                result = PureObj.delete_host(args.name)
                if result:
                    LOG.info(f"Successfully {args.name} deleted !!")
                    invalid = False
                    return True
                else:
                    LOG.error(f"Failed in {args.name} deleted !!")
                    return False
            elif user_entry in valid_inputs and user_entry in valid_inputs[2:]:
                LOG.warning(f"Deletion of {args.name} has been cancelled !!")
                invalid = False
            else:
                LOG.error(f"{user_entry} is invalide input")
    else:
        result = PureObj.delete_host(args.name)
        if result:
            LOG.info(f"Successfully {args.name} deleted !!")
        else:
            LOG.error(f"Failed in deletion of Volume : {args.name}")
        return result


def detail_volume(args):
    """
    List the detail information of the volume from the  Pure Storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  list  --name TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume list  --name TEST113

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume list  --name TEST113 --format=json

    Example 4: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume list  \
        --wwn 624a9370a9da32b3267c47150005fa7a

    Example 5: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume list \
          --wwn 624a9370a9da32b3267c47150005fa7a --format=json
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if (args.name and args.wwn):
        LOG.error("Specify only one parameter : name or wwn")
        return False
    if (args.name is None and args.wwn is None):
        LOG.error("Specify atleast one parameter : name or wwn")
        return False
    result = PureObj.get_volumes(args.name, args.wwn)
    data = PureObj.volumes
    if not result or data is None:
        return result
    if args.format is None:
        LOG.info(
            f"Details of {args.name}: \n{chr(10).join(str(item) for item in data.items())}"
        )
        return result
    if args.format == "json":
        LOG.info(
            f"Details of {args.name}:\n {json.dumps(data, indent=4, sort_keys=False)}"
        )
        return result


def clone_volumes(args):
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )

    if (args.name and args.wwn):
        LOG.error("Specify only one parameter : name or wwn")
        return False

    if (args.name is None and args.wwn is None):
        LOG.error("Specify atleast one parameter : name or wwn")
        return False

    result = PureObj.get_vol_clones(args.name, args.wwn)
    data = PureObj.clone_volumes
    if not result or data is None:
        return result
    if args.format is None:
        LOG.info(
            f"Clones of {args.name}: \n{chr(10).join(str(item) for item in data.items())}"
        )
    if args.format == "json":
        LOG.info(
            f"Clones of {args.name}:\n {json.dumps(data, indent=4, sort_keys=False)}"
        )
    return result


def create_volume(args):
    """Create Volume on the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  create  --name TEST113 --size 1T

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume create  --name TEST113 --size 1T
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.create_volume(args.size, args.name)
    if result:
        LOG.info(f"Successfully {args.name} created : {args.size}")
    else:
        LOG.error(f"Failure in {args.name} created")
    return result


def delete_volume(args):
    """Delete Volume from the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem" volume delete --name TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume delete --name TEST113

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume delete \
        --wwn 624a9370a9da32b3267c47150005fa7a
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if (args.name and args.wwn):
        LOG.error("Specify only one parameter : name or wwn")
        return False
    if (args.name is None and args.wwn is None):
        LOG.error("Specify atleast one parameter : name or wwn")
        return False
    if not args.no_prompt:
        valid_inputs = ["y", "yes", "n", "no"]
        invalid = True
        while invalid:
            validate_deletion = input(
                f"Are you sure you want to delete {args.name} [Y/N](yes/no) : "
            )
            user_entry = validate_deletion.lower()
            if user_entry in valid_inputs and user_entry in valid_inputs[:2]:
                result = PureObj.delete_volume(args.name, args.wwn)
                if result:
                    LOG.info(f"Successfully {args.name} deleted !!")
                    invalid = False
                else:
                    LOG.error(f"Failed in deletion of Volume : {args.name}")
                return result
            elif user_entry in valid_inputs and user_entry in valid_inputs[2:]:
                LOG.warning(f"Deletion of {args.name} has been cancelled !!")
                invalid = False
            else:
                LOG.error(f"{user_entry} is invalid input")
    else:
        result = PureObj.delete_volume(args.name, args.wwn)
        if result:
            LOG.info(f"Successfully {args.name} deleted !!")
        else:
            LOG.error(f"Failed in deletion of Volume : {args.name}")
        return result


def eradicate_volume(args):
    """eradicate Volume from the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  eradicate  --name TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume eradicate  --name TEST113
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if not args.no_prompt:
        valid_inputs = ["y", "yes", "n", "no"]
        invalid = True
        while invalid:
            validate_eradicate = input(
                f"Are you sure you want to delete {args.name} [Y/N](yes/no) : "
            )
            user_entry = validate_eradicate.lower()
            if user_entry in valid_inputs and user_entry in valid_inputs[:2]:
                result = PureObj.eradicate_volume(args.name)
                if result:
                    LOG.info(f"Successfully {args.name} eradicated !!")
                    invalid = False
                else:
                    LOG.error(f"Failed in Eradication of Volume : {args.name}")
                return result
            elif user_entry in valid_inputs and user_entry in valid_inputs[2:]:
                LOG.warning(f"Eradication of {args.name} has been cancelled !!")
                invalid = False
            else:
                LOG.error(f"{user_entry} is invalid input")
    else:
        result = PureObj.eradicate_volume(args.name)
        if result:
            LOG.info(f"Successfully {args.name} eradicated !!")
        else:
            LOG.error(f"Failed in Eradication of Volume : {args.name}")
        return result


def connect_volume(args):
    """Connect Volume to the host on the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  connect --hostname HOST01 --volname TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume connect  \
        --hostname HOST01 --volname TEST113

    [optionally define the LUNID]
    Example 3: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  connect --hostname HOST01 --volname \
            TEST113 --lunid 77

    Example 4: ucampurestorage --file=./ucampurestorage/lib/secrets.json  volume connect  --hostname HOST01 \
        --volname TEST113  --lunid 77
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.connect_volume(args.hostname, args.volname, lunid=args.lunid)
    if result:
        LOG.info(f"connections of {args.volname} to initiator {args.hostname}")
    else:
        LOG.error(
            f"failure in connections of {args.volname} to initiator {args.hostname}"
        )
    return result


def disconnect_volume(args):
    """Disconnect Volume from the host on the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  volume  disconnect --hostname HOST01 --volname TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume disconnect \
            --hostname HOST01 --volname TEST113
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.disconnect_volume(args.hostname, args.volname)
    if result:
        LOG.info(
            f"connections removed of {args.volname} from initiator {args.hostname}"
        )
    else:
        LOG.error(
            f"failure in connections removal of {args.volname} from initiator {args.hostname}"
        )
    return result


def replace_volume(args):
    """Replace the mounted clone volume with new clone from Pure storage and mount to the local server.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" volume replace --src_mp /t1  --dst_mp /t2

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume replace --src_mp /t1  --dst_mp /t2
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.replace_volume(args.src_mp, args.dst_mp, args.unmount_src_mp)
    if result:
        LOG.info(f"replace the volume {args.src_mp} with {args.dst_mp}")
    else:
        LOG.error(f"failed the replace the volume {args.src_mp} with {args.dst_mp}")
    return result


def clone_volume(args):
    """Clone volume on Pure storage and mount to the local server.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" volume clone --name TEST113_clone --srcvol \
            TEST113 --target_mp /t2

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume clone --name TEST113_clone \
        --srcvol TEST113 --target_mp /t2

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume clone --name TEST113_clone \
        --srcwwn 624a9370a9da32b3267c47150005fa7a --target_mp /t2
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )

    if (args.srcvol and args.srcwwn):
        LOG.error("Specify only one parameter : source volume name or source volume wwn")
        return False

    if (args.srcvol is None and args.srcwwn is None):
        LOG.error("Specify atleast one parameter : source volume name or source volume wwn")
        return False

    result = PureObj.clone_volume(args.name, args.srcwwn, args.srcvol, args.target_mp)
    if result:
        if args.target_mp:
            LOG.info(
                f"Cloned  volume {args.name} of {args.srcvol} created to be mounted on {args.target_mp}."
            )
        else:
            LOG.info(f"Cloned  volume {args.name} of {args.srcvol} created.")
    return result


def list_snapshots(args):
    """
    List the detail information of the snapshot from the  Pure Storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem"  snapshot list --volname TEST12

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot list --volname TEST12

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot list --volname TEST12  --format=json
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.get_volumesnapshots(args.volname)
    data = PureObj.volumesnapshots
    if not result or data is None:
        return result
    if args.format is None:
        LOG.info(
            f"Snapshot of Volume {args.volname}: \n{chr(10).join(str(item) for item in data.items())}"
        )
    if args.format == "json":
        LOG.info(
            f"Snapshot of Volume {args.volname}:\n {json.dumps(data, indent=4, sort_keys=False)}"
        )
    return result


def create_snapshot(args):
    """Create snapshot of the volume on the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" snapshot create --srcvol TEST113 --suffix snap01

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot create \
            --srcvol TEST113 --suffix snap01

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot create \
            --srcwwn 624a9370a9da32b3267c47150005fa7a --suffix snap01
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if (args.srcvol and args.srcwwn):
        LOG.error("Specify only one parameter : source volume name or source volume wwn")
        return False

    if (args.srcvol is None and args.srcwwn is None):
        LOG.error("Specify atleast one parameter : source volume name or source volume wwn")
        return False
    result = PureObj.create_snapshot(args.srcvol, args.srcwwn, args.suffix)
    if result:
        if (args.srcvol) is not None:
            LOG.error(
                f"Snapshot of the volume {args.srcvol} created : {args.srcvol}.{args.suffix}"
            )
        elif (args.srcwwn) is not None:
            LOG.error(
                f"Snapshot of the volume wwn {args.srcwwn} created with suffix : {args.suffix} on PureStorage"
            )

    else:
        LOG.error(
            f"Failure in Snapshot of the volume {args.srcvol}: {args.srcvol}.{args.suffix}"
        )
    return result


def delete_snapshot(args):
    """Delete snapshot of the volume on the Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" snapshot delete --snapname TEST113.snap01

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json  snapshot delete --snapname TEST113.snap01
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.delete_snapshot(args.snapname)
    if result:
        LOG.info(f"Deletion of the snapshot {args.snapname}")
    else:
        LOG.error(f"Failure in deletion of the snapshot : {args.snapname}")
    return result


def map_volume(args):
    """Map volume to the local server which  is provided by Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" volume map --name TEST113 --mp /t2

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume map --name TEST113 --mp /t2

    [NOTE: New volume need to be formated therefore -new flag is required]

    Example 3: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume map --name TEST113_new --mp /t2 -new

    Example 4: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd> \
        --client_name=<ClientName> --keyfile="privatekey.pem" volume map --name TEST113 --mp /t2 -new
    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    result = PureObj.map_volume(args.name, args.mp, args.new_volume)
    if result:
        LOG.info(f"mapping of {args.name} to mountpoint {args.mp}")
    else:
        LOG.error(f"Fail in mapping of {args.name} to mountpoint {args.mp}")
    return result


def unmap_volume(args):
    """Map volume to the local server which  is provided by Pure storage.

    Example 1: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem" volume unmap --name TEST113

    Example 2: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume unmap --name TEST113

    [optionally define the WWN]

    Example 3: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem" volume unmap --wwn <WWN number>

    Example 4: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume unmap --wwn <WWN number>

    [optionally define the mountpoint]

    Example 5: ucampurestorage --client_id=<clientID> --key_id=<KeyID> --user=<User> --password=<passwd>\
        --client_name=<ClientName> --keyfile="privatekey.pem" volume unmap --mp /t1

    Example 6: ucampurestorage --file=./ucampurestorage/lib/secrets.json volume unmap --mp /t1

    """
    token_obj = _token_object(args)
    PureObj = PureAdvance(
        args.storage,
        args.port,
        args.user,
        args.password,
        token_obj.current_token,
        args.is_secure,
    )
    if (
        (args.name and args.wwn and args.mp)
        or (args.name and args.wwn)
        or (args.name and args.mp)
        or (args.mp and args.wwn)
    ):
        LOG.error("Specify only one parameter : name, wwn or mount point")
        return False
    if (args.name is None and args.wwn is None and args.mp is None):
        LOG.error("Specify atleast one parameter : name, wwn or mount point")
        return False
    result = PureObj.unmap_volume(args.name, args.wwn, args.mp)
    if result:
        LOG.info(f"Successfully unmapping of {args.wwn}")
    else:
        LOG.error(f"failure in unmapping of {args.wwn}")
    return result


if __name__ == "__main__":
    obj = Options()
    obj.parse(sys.argv[1:])
