#!/usr/bin/python3
"""Make a token with the admin claim for the router's web service, with vpnrouter_auth.

The token is signed with the router's key and is valid for the lifetime given, without a
maximum. It may call every protected path, and it is the only token that may change the
admins group. The answer is the token on standard output, and a failure is a message on
standard error. With --json the answer is {"token", "expires"} on standard output, and a
failure is {"error": ...} there too. The exit status is 2 for an invalid request and 1 for
any other failure. -h or --help shows the options.

  vpn-router-token [--json] (--minutes N | --hours N | --days N)
"""

import argparse
import json
import sys

sys.path.insert(0, '/usr/lib/vpn-router')

import vpnrouter_auth

#: The seconds of each unit of the lifetime.
UNITS = {'minutes': 60, 'hours': 3600, 'days': 86400}


class Failure(Exception):
    """A request that cannot be done; status is 2 invalid, 1 other."""

    def __init__(self, message, status=1):
        super().__init__(message)
        self.status = status


class Parser(argparse.ArgumentParser):
    """An argument parser whose errors are Failures with status 2, so that --json answers them as JSON."""

    def error(self, message):
        raise Failure(f'{message}; see {self.prog} --help', 2)


def positive(value):
    try:
        number = int(value)
    except ValueError:
        number = 0
    if number < 1:
        raise argparse.ArgumentTypeError(f'{value!r} is not a positive whole number')
    return number


def main(argv):
    # A request that cannot be parsed is answered in JSON when --json is among the arguments.
    as_json = '--json' in argv
    try:
        parser = Parser(prog='vpn-router-token', description='Make a token with the admin claim for the web service.')
        parser.add_argument('--json', action='store_true', help='answer in JSON')
        lifetime = parser.add_mutually_exclusive_group(required=True)
        for unit in UNITS:
            lifetime.add_argument(f'--{unit}', type=positive, metavar='N', help=f'valid for N {unit}')
        options = parser.parse_args(argv)
        as_json = options.json
        seconds = next(getattr(options, unit) * factor for unit, factor in UNITS.items() if getattr(options, unit))
        answer = vpnrouter_auth.admin_token(seconds)
    except Failure as failure:
        message, status = str(failure), failure.status
    except OSError as exc:
        message, status = str(exc), 1
    else:
        print(json.dumps(answer) if as_json else answer['token'])
        return 0
    if as_json:
        print(json.dumps({'error': message}))
    else:
        print(f'vpn-router-token: {message}', file=sys.stderr)
    return status


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
