import inspect
from argparse import ArgumentParser


def get_main(function):
    """Create a main function for a command.

    The main function will be callable using the command-line arguments:
    `main('--flag', '--option=option')`

    The command will be called according to its argspec using available args.
    """
    parser = get_parser(function)

    def main(*args: str):
        namespace = parser.parse_args(args if args else None)
        return call(function, namespace)

    return main


def get_parser(function):
    """Create a parser for a function.

    The parser description will use the function docstring.

    Example:

    .. code: python

        from argparse_decorators import add_argument, get_parser

        @add_argument("argument")
        def command(argument)
            ...

        parser = get_parser(command)

    It's same as:

    .. code: python

        import argparse

        from argparse_decorators import add_argument, get_parser

        def command(argument)
            ...

        parser = argparse.ArgumentParser(description=command.__doc__)
        parser.add_argument("argument")


    The parser will be configured according to __argparse_annotations__.
    """
    parser = ArgumentParser(description=function.__doc__)
    configure_parser(parser, function)
    return parser


def configure_parser(parser, command):
    """Configure a parser from a decorated function."""
    annotations = getattr(command, "__argparse_annotations__", [])
    for args, kwargs in annotations:
        parser.add_argument(*args, **kwargs)


def call(function, args):
    """Call a function according to its argspec using available cli args.

    Example:

    .. code: python

        import argparse

        from argparse_decorators import add_argument, call


        @add_argument("argument")
        def command(argument)
            assert argument == "value"


        call(command, argparse.Namespace(argument="value"))

    The parser will be configured according to __argparse_annotations__.
    """
    argspec = inspect.getargspec(function)
    call_args = {arg: getattr(args, arg) for arg in argspec.args}
    return function(**call_args)


def add_argument(*args, **kwargs):
    """Build a decorator that will add argparse annotations to a function."""
    return lambda decorated: _add_annotation(decorated, (args, kwargs))


def _add_annotation(function, argument):
    """Add argparse annotations to a function."""
    annotations = getattr(function, "__argparse_annotations__", [])
    annotations.append(argument)
    setattr(function, "__argparse_annotations__", annotations)
    return function
