# coding: utf-8
# Modified Work: Copyright (c) 2018, 2021, Oracle and/or its affiliates.  All rights reserved.
# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
# Original Work: Copyright (c) 2015 José Padilla

#!/usr/bin/env python

from __future__ import absolute_import, print_function

import argparse
import json
import sys
import time

from . import DecodeError, __version__, decode, encode


def encode_payload(args):
    # Try to encode
    if args.key is None:
        raise ValueError('Key is required when encoding. See --help for usage.')

    # Build payload object to encode
    payload = {}

    for arg in args.payload:
        k, v = arg.split('=', 1)

        # exp +offset special case?
        if k == 'exp' and v[0] == '+' and len(v) > 1:
            v = str(int(time.time()+int(v[1:])))

        # Cast to integer?
        if v.isdigit():
            v = int(v)
        else:
            # Cast to float?
            try:
                v = float(v)
            except ValueError:
                pass

        # Cast to true, false, or null?
        constants = {'true': True, 'false': False, 'null': None}

        if v in constants:
            v = constants[v]

        payload[k] = v

    token = encode(
        payload,
        key=args.key,
        algorithm=args.algorithm
    )

    return token.decode('utf-8')


def decode_payload(args):
    try:
        if args.token:
            token = args.token
        else:
            if sys.stdin.isatty():
                token = sys.stdin.readline().strip()
            else:
                raise IOError('Cannot read from stdin: terminal not a TTY')

        token = token.encode('utf-8')
        data = decode(token, key=args.key, verify=args.verify)

        return json.dumps(data)

    except DecodeError as e:
        raise DecodeError('There was an error decoding the token: %s' % e)


def build_argparser():

    usage = '''
    Encodes or decodes JSON Web Tokens based on input.

    %(prog)s [options] <command> [options] input

    Decoding examples:

    %(prog)s --key=secret decode json.web.token
    %(prog)s decode --no-verify json.web.token

    Encoding requires the key option and takes space separated key/value pairs
    separated by equals (=) as input. Examples:

    %(prog)s --key=secret encode iss=me exp=1302049071
    %(prog)s --key=secret encode foo=bar exp=+10

    The exp key is special and can take an offset to current Unix time.
    '''

    arg_parser = argparse.ArgumentParser(
        prog='pyjwt',
        usage=usage
    )

    arg_parser.add_argument(
        '-v', '--version',
        action='version',
        version='%(prog)s ' + __version__
    )

    arg_parser.add_argument(
        '--key',
        dest='key',
        metavar='KEY',
        default=None,
        help='set the secret key to sign with'
    )

    arg_parser.add_argument(
        '--alg',
        dest='algorithm',
        metavar='ALG',
        default='HS256',
        help='set crypto algorithm to sign with. default=HS256'
    )

    subparsers = arg_parser.add_subparsers(
        title='PyJWT subcommands',
        description='valid subcommands',
        help='additional help'
    )

    # Encode subcommand
    encode_parser = subparsers.add_parser('encode', help='use to encode a supplied payload')

    payload_help = """Payload to encode. Must be a space separated list of key/value
    pairs separated by equals (=) sign."""

    encode_parser.add_argument('payload', nargs='+', help=payload_help)
    encode_parser.set_defaults(func=encode_payload)

    # Decode subcommand
    decode_parser = subparsers.add_parser('decode', help='use to decode a supplied JSON web token')
    decode_parser.add_argument(
        'token',
        help='JSON web token to decode.',
        nargs='?')

    decode_parser.add_argument(
        '-n', '--no-verify',
        action='store_false',
        dest='verify',
        default=True,
        help='ignore signature and claims verification on decode'
    )

    decode_parser.set_defaults(func=decode_payload)

    return arg_parser


def main():
    arg_parser = build_argparser()

    try:
        arguments = arg_parser.parse_args(sys.argv[1:])

        output = arguments.func(arguments)

        print(output)
    except Exception as e:
        print('There was an unforseen error: ', e)
        arg_parser.print_help()
