#                                   MIT License
#
#              Copyright (c) 2021 Javier Alonso <jalonso@teldat.com>
#
#  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.
"""
Main application entry point. Here, a single method is exposed which prepares
the environment for running the application with the installed plugins. Notice
that `orcha.main` won't work if no plugin is installed. For more information,
see: :class:`BasePlugin`.
"""
import argparse
import importlib
import pkgutil
import sys
from typing import Type

import orcha.properties

from ..plugins import BasePlugin
from ..utils.logging_utils import get_logger

# application universal logger
log = get_logger()


def main():
    """Main application entry point. Multiple arguments are defined which allows
    further customization of the server/client process:

    --listen-address ADDRESS  defines the IP address used when serving/connecting the
                              application. By default, it is ``127.0.0.1``.
    --port N                  indicates which port is used during communication.
                              By default, it is **50000**.
    --key KEY                 defines the authentication key used during communication.
                              This field is not mandatory but **it is recommended to define it**
                              as it will be necessary for other processes to communicate with the
                              service itself. By default, it is :attr:`None` which
                              means that it will be auto-generated by the main process when
                              running.

    The application automatically detects the plugins that are installed in the system. It
    is important that the installed plugins follows the name convention in order to be
    correctly identified. In other case, the subcommands won't appear here. For more
    details, see :class:`BasePlugin`.

    Returns:
        int: execution return code. Multiple return codes are possible:

              + ``0`` means that the execution was successful.
              + ``1`` refers to a standard error happened during execution.
              + ``127`` indicates that no plugins were found or no plugins
                can handle the parsed command line options.
    """
    parser = argparse.ArgumentParser(
        description="Orcha command line utility for handling services"
    )
    parser.add_argument(
        "--listen-address",
        metavar="ADDRESS",
        type=str,
        default="127.0.0.1",
        help="Listen address of the service",
    )
    parser.add_argument(
        "--port",
        metavar="N",
        type=int,
        default=50000,
        help="Listen port of the service",
    )
    parser.add_argument(
        "--key",
        metavar="KEY",
        type=str,
        default=None,
        help="Authentication key used for verifying clients",
    )
    subparsers = parser.add_subparsers(
        description="Available commands installed from external plugins",
        required=True,
        metavar="Commands",
    )

    discovered_plugins = {
        name: importlib.import_module(name)
        for _, name, _ in pkgutil.iter_modules()
        if name.startswith("orcha_")
    }

    plugins = []
    for plugin, mod in discovered_plugins.items():
        pl: Type[BasePlugin] = getattr(mod, "plugin", None)
        if pl is None:
            log.warning(
                'invalid plugin specified for "%s". '
                "Is there a plugin export class defined in __init__?",
                plugin,
            )
            continue

        plugins.append(pl(subparsers))

    args: argparse.Namespace = parser.parse_args()
    orcha.properties.listen_address = args.listen_address
    orcha.properties.port = args.port
    if args.key is not None:
        orcha.properties.authkey = args.key.encode()

    for arg, value in vars(args).items():
        orcha.properties.extras[arg] = value

    for plugin in plugins:
        if plugin.can_handle(args.owner):
            return plugin.handle(args)

    return 127


if __name__ == "__main__":
    sys.exit(main())
