#!/usr/bin/env python

#
#   $ aletheia generate
#   $ aletheia sign /path/to/file public-key-url
#   $ aletheia verify /path/to/file
#

import argparse
import os
import sys

from cryptography.exceptions import InvalidSignature
from termcolor import cprint

from aletheia.aletheia import Aletheia
from aletheia.exceptions import (
    InvalidURLError,
    PublicKeyNotExistsError,
    UnknownFileTypeError
)
from aletheia.utils import generate, sign, verify


class Command:

    def __init__(self):

        parser = argparse.ArgumentParser(prog="aletheia")
        parser.set_defaults(func=parser.print_help)
        subparsers = parser.add_subparsers(dest="subcommand")

        subparsers.add_parser(
            "generate",
            help="Generate a public/private key pair for use in signing & 3rd "
                 "party verification. (Do this first)"
        )

        parser_sign = subparsers.add_parser("sign", help="Sign a file")
        parser_sign.add_argument("path")
        parser_sign.add_argument(
            "url", nargs="?", default=os.getenv("ALETHEIA_PUBLIC_KEY"))

        parser_verify = subparsers.add_parser(
            "verify", help="Verify the origin of a file")
        parser_verify.add_argument("path")

        args = parser.parse_args()

        try:
            if args.subcommand:
                getattr(self, args.subcommand)(args)
            else:
                parser.print_help()
        except (RuntimeError, FileNotFoundError) as e:
            print(e, file=sys.stdout)
            sys.exit(1)

        sys.exit(0)

    @classmethod
    def generate(cls, *args):

        private = Aletheia().private_key_path
        if os.path.exists(private):
            print(
                "It looks like you already have a key setup at {}.\n"
                "Exiting prematurely just to be safe.".format(private)
            )
            sys.exit(1)

        print("Generating private/public key pair...")
        generate()
        print("""
            All finished!

            You now have two files: aletheia.pem (your private key) and
            aletheia.pub (your public key).  Keep the former private, and share
            the latter far-and-wide.  Importantly, place your public key at a
            publicly accessible URL so that when you sign a file with your
            private key, it can be verified by reading the public key at that
            URL.
        """.replace("            ", ""))

    @classmethod
    def sign(cls, args):

        if not args.url:
            cprint(
                "\nYou must specify the public key URL either in the "
                "environment as ALETHEIA_PUBLIC_KEY_URL or on the command "
                "line as the second argument.\n",
                "red"
            )
            sys.exit(1)

        sign(args.path, args.url)
        template = "\n  ✔  {} was signed with your private key\n"
        cprint(template.format(args.path), "green")
        sys.exit(0)

    @classmethod
    def verify(cls, args):

        try:
            domain = verify(args.path)
        except FileNotFoundError:
            cprint(
                "\n  ✖️  Aletheia can't find that file\n",
                "red"
            )
            sys.exit(1)
        except UnknownFileTypeError:
            cprint(
                "\n  ✖️  Aletheia doesn't recognise that file type\n",
                "red"
            )
            sys.exit(2)
        except InvalidURLError:
            cprint(
                "\n  ✖️  The public key URL in the file provided is invalid\n",
                "red"
            )
            sys.exit(3)
        except PublicKeyNotExistsError:
            cprint(
                "\n  ✖️  The specified URL does not contain a public key\n",
                "red"
            )
            sys.exit(4)
        except InvalidSignature:
            cprint("\n  ✖️  There's something wrong with that file\n", "red")
            sys.exit(5)

        template = "\n  ✔️  The file is verified as having originated at {}\n"
        cprint(template.format(domain), "green")
        sys.exit(0)


if __name__ == "__main__":
    Command()
