#  Copyright (c) 2020 Markus Ressel
#  .
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#  .
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#  SOFTWARE.
from typing import List

from telegram_click.argument import Argument
from telegram_click.const import ARG_NAMING_PREFIXES
from telegram_click.util import escape_for_markdown


def generate_help_message(names: [str], description: str, args: List[Argument]) -> str:
    """
    Generates a command usage description
    :param names: names of the command
    :param description: command description
    :param args: command argument list
    :return: help message
    """
    synopsis = generate_synopsis(names, args)

    flags = list(filter(lambda x: x.flag, args))
    flags = sorted(flags, key=lambda x: x.name)
    arguments = list(filter(lambda x: not x.flag, args))

    flags_description = generate_arguments_description(flags)
    arguments_description = generate_arguments_description(arguments)

    lines = [
        synopsis,
        "  " + description
    ]
    if len(flags) > 0:
        lines.extend([
            "Flags:",
            flags_description
        ])

    if len(arguments) > 0:
        lines.extend([
            "Arguments:",
            arguments_description
        ])

    if len(arguments) > 0 or len(flags) > 0:
        example = generate_command_example(names, arguments, flags)
        lines.extend([
            "Example:",
            "  " + example
        ])

    return "\n".join(lines)


def generate_synopsis(names: [str], args: List[Argument]) -> str:
    """
    Generates the synopsis for a command
    :param names: command names
    :param args: arguments
    :return:
    """
    command_names = list(map(lambda x: "/{}".format(escape_for_markdown(x)), names))
    synopsis = command_names[0]
    # append command name aliases in round brackets
    if len(command_names) > 1:
        synopsis += " ({})".format(", ".join(command_names[1:]))
    # add hints about flags and arguments
    if len(args) > 0:
        if any(map(lambda x: x.flag, args)):
            synopsis += " [[FLAGS]]"
        if any(map(lambda x: not x.flag, args)):
            synopsis += " [[ARGS]]"

    return synopsis


def generate_arguments_description(args: List[Argument]) -> str:
    """
    Generates the description of all given arguments
    :param args: arguments
    :return: description
    """
    argument_lines = list(map(generate_argument_description, args))
    return "\n".join(argument_lines)


def generate_argument_description(arg: Argument) -> str:
    """
    Generates the usage text for an argument
    :param arg: the argument
    :return: usage text line
    """
    arg_prefix = next(iter(ARG_NAMING_PREFIXES))
    arg_names = list(map(lambda x: "`{}{}`".format(arg_prefix, x), arg.names))

    message = "  " + ", ".join(arg_names)
    if not arg.flag:
        message += "\t\t`{}`".format(arg.type.__name__.upper())
    message += "\t\t" + escape_for_markdown(arg.description)

    if arg.optional and not arg.flag:
        message += "\t(`{}`)".format(escape_for_markdown(arg.default))
    return message


def generate_command_example(names: List[str], arguments: List[Argument], flags: List[Argument]) -> str:
    """
    Generates an example call of a command
    :param names: possible command names
    :param arguments: command arguments (without flags)
    :param flags: command flags
    :return: example call
    """
    arg_prefix = next(iter(ARG_NAMING_PREFIXES))
    argument_examples = list(map(lambda x: "{}".format(x.example), arguments))
    flag_examples = list(map(lambda x: "{}{}".format(arg_prefix, x.name), flags))
    return "`/{} {}`".format(names[0], " ".join(flag_examples + argument_examples)).strip()
