#!/usr/bin/env python3

"""
Command-line interface for common tasks when working with plots stored in uhepp
format. This includes local file operations and communication with a remote API
server.
"""

import sys
import os
import argparse
import re
import matplotlib.pyplot as plt
import uhepp

USAGE="""uhepp <command> [<args>]

The most commonly used commands are:
"""

def sanitize(filename):
    """Remove illegal, potentially harmfull substrings from filenames"""
    filename = re.sub("^[^a-zA-Z0-9_.-]+", "", filename)
    filename = re.sub("[^a-zA-Z0-9_.-]+$", "", filename)

    return re.sub("[^a-zA-Z0-9_.-]+", "_", filename)

class UheppCLI:
    """Implementation of the command-line parsing the their actions"""

    commands = {
        "pull": "Retrieve a plot from a central hub",
        "show": "Show local or remote plot",
        "push": "Push local files to a central hub",
        "render": "Convert a local or remote uhepp file into a graphics",
        "cpull": "Retrieve all plots of a collection",
    }

    def build_usage(self, usage_template):
        """Append registered commands to usage string"""
        for command, desc in self.commands.items():
            usage_template += f"  {command:20} {desc}\n"
        return usage_template

    def __init__(self):
        """Create new CLI object"""
        parser = argparse.ArgumentParser(
            description="Uhepp converter and API client",
            usage=self.build_usage(USAGE))
        parser.add_argument('command', help='Subcommand to run')

        args = parser.parse_args(sys.argv[1:2])
        if not hasattr(self, args.command) \
                or args.command not in self.commands:
            print("Unrecognized command")
            parser.print_help()
            sys.exit(1)

        getattr(self, args.command)()

    def cpull(self):
        """Parse command line options for cpull sub command"""
        parser = argparse.ArgumentParser(
            description=self.commands["cpull"])
        parser.add_argument("collection_id", metavar="COLLECTION",
                            help="The ID of the collection")
        parser.add_argument("--render", "-r", action="store_true",
                            help="Render the plot to a graphic")
        parser.add_argument("path", metavar="PATH", nargs="?",
                            help="Destination path", default=".")
        parser.add_argument("--api-url", default=None,
                            help="Custom API root endpoint")
        parser.add_argument("--api-key", default=None,
                            help="Custom API authentication token")

        args = parser.parse_args(sys.argv[2:])
        self.cpull_action(args.collection_id, args.path, args.render,
                         api_key=args.api_key, api_url=args.api_url)

    @staticmethod
    def cpull_action(collectionp_id, path, render, api_key=None, api_url=None):
        """Pull all plot of a collection and save a local file"""
        os.makedirs(path, exist_ok=True)

        plots = uhepp.pull_collection(collectionp_id, api_key, api_url)

        for plot in plots:
            if render:
                filename = sanitize(plot.filename) + ".pdf"
                filename = os.path.join(path, filename)
                plot.render(filename)
            else:
                filename = sanitize(plot.filename) + ".json"
                filename = os.path.join(path, filename)
                plot.to_json(filename)
            print(f"Created {filename}")

    def pull(self):
        """Parse command line options for pull sub command"""
        parser = argparse.ArgumentParser(
            description=self.commands["pull"])
        parser.add_argument("uuid", metavar="UUID",
                            help="The uuid of the plot to download")
        parser.add_argument("--render", "-r", action="store_true",
                            help="Render the plot to a graphic")
        parser.add_argument("filename", metavar="FILENAME", nargs="?",
                            help="Destination filename")
        parser.add_argument("--api-url", default=None,
                            help="Custom API root endpoint")
        parser.add_argument("--api-key", default=None,
                            help="Custom API authentication token")

        args = parser.parse_args(sys.argv[2:])
        self.pull_action(args.uuid, args.filename, args.render,
                         api_key=args.api_key, api_url=args.api_url)

    @staticmethod
    def pull_action(uuid, filename, render, api_key=None, api_url=None):
        """Pull a plot from an API server and save a local file"""
        plot = uhepp.pull(uuid, api_key, api_url)

        if render:
            if not filename:
                filename = sanitize(plot.filename) + ".pdf"

            plot.render(filename)
        else:
            if not filename:
                filename = sanitize(plot.filename) + ".json"
                filename = sanitize(filename)

            plot.to_json(filename)
        print(f"Created {filename}")

    @staticmethod
    def is_uuid(uuid):
        """Return true if the given string is a uuid with dashs"""
        lengths = [8, 4, 4, 4, 12]
        pattern = "^" + "-".join(["[0-9a-f]{%d}" % l for l in lengths]) + "$"
        return re.match(pattern, uuid, re.I)

    def show(self):
        """Parse CLI arguments for show subcommand"""
        parser = argparse.ArgumentParser(
            description=self.commands["show"])
        parser.add_argument("input", metavar="UUID_OR_FILE",
                            help="The uuid or filename of the plot")
        parser.add_argument("--api-url", default=None,
                            help="Custom API root endpoint")
        parser.add_argument("--api-key", default=None,
                            help="Custom API authentication token")

        args = parser.parse_args(sys.argv[2:])
        self.show_action(args.input, args.api_key, args.api_url)

    def show_action(self, soruce, api_key=None, api_url=None):
        """Show remote file if input is a UUID, otherwise show a local file"""
        if self.is_uuid(soruce):
            plot = uhepp.pull(soruce,
                              api_key=api_key,
                              api_url=api_url)
        else:
            plot = uhepp.from_json(soruce)

        plot.render()
        plt.show()

    def push(self):
        """Parse CLI arguments for push subcommand"""
        parser = argparse.ArgumentParser(
            description=self.commands["push"])
        parser.add_argument("collection_id", metavar="COLLECTION",
                            help="ID of the collection")
        parser.add_argument("input", metavar="FILE", nargs="+",
                            help="The filename of the plot")
        parser.add_argument("--api-url", default=None,
                            help="Custom API root endpoint")
        parser.add_argument("--api-key", default=None,
                            help="Custom API authentication token")

        args = parser.parse_args(sys.argv[2:])
        self.push_action(args.input, args.collection_id,
                         api_key=args.api_key, api_url=args.api_url)

    @staticmethod
    def push_action(filenames, collection, api_key, api_url):
        """Push a local file to an API server"""
        for filename in filenames:
            plot = uhepp.from_json(filename)

            receipt = plot.push(collection, api_key, api_url)
            print(f"Pushed {filename} to {receipt.ui_url}")

    def render(self):
        """Parse CLI arguments for render subcommand"""
        parser = argparse.ArgumentParser(
            description=self.commands["render"])
        parser.add_argument("input", metavar="UUID_OR_FILE",
                            help="The uuid or filename of the plot")
        parser.add_argument("output", metavar="OUTPUT",
                            help="Filename of the destination graphics file")
        parser.add_argument("--api-url", default=None,
                            help="Custom API root endpoint")
        parser.add_argument("--api-key", default=None,
                            help="Custom API authentication token")

        args = parser.parse_args(sys.argv[2:])
        self.render_action(args.input, args.output, args.api_key, args.api_url)

    def render_action(self, source, output, api_key=None, api_url=None):
        """Render a local or remote file into a graphics format"""
        if self.is_uuid(source):
            plot = uhepp.pull(source,
                              api_key=api_key,
                              api_url=api_url)
        else:
            plot = uhepp.from_json(source)

        plot.render(output)

if __name__ == '__main__':
    UheppCLI()
