# -*- coding: utf-8 -*-
# (c) 2021, Alexei Znamensky <russoz@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import argparse
import re
from functools import partial
from pathlib import Path

import yaml

from ..context import CollectionContext
from ..exceptions import AndeboxException
from .base import AndeboxAction


PLUGIN_TYPES = (
    "connection",
    "lookup",
    "modules",
    "doc_fragments",
    "module_utils",
    "callback",
    "inventory",
)
RUNTIME_TYPES = ("redirect", "tombstone", "deprecation")


def info_type_param(types, v):
    try:
        r = [t for t in types if t.startswith(v.lower())]
        return r[0][0].upper()
    except IndexError as e:
        raise argparse.ArgumentTypeError("invalid value: {v}") from e


class RuntimeAction(AndeboxAction):
    name = "runtime"
    help = "returns information from runtime.yml"
    args = [
        dict(
            names=["--plugin-type", "-pt"],
            specs=dict(
                choices=PLUGIN_TYPES, help="specify the plugin type to be searched"
            ),
        ),
        dict(
            names=["--regex", "--regexp", "-r"],
            specs=dict(
                action="store_true", help="treat plugin names as regular expressions"
            ),
        ),
        dict(
            names=["--info-type", "-it"],
            specs=dict(
                type=partial(info_type_param, RUNTIME_TYPES),
                help=f"restrict type of response elements. Must be one of {RUNTIME_TYPES}, "
                "and it may be shortened down to one letter.",
            ),
        ),
        dict(names=["plugin_names"], specs=dict(nargs="+")),
    ]
    name_tests = []
    current_version = None
    info_type = None

    def print_runtime(self, name, node):
        def is_info_type(_type):
            return self.info_type is None or self.info_type.lower() == _type.lower()

        redir, tomb, depre = [node.get(x) for x in RUNTIME_TYPES]
        if redir and is_info_type("R"):
            print(f"R {name}: redirected to {redir}")
        elif tomb and is_info_type("T"):
            print(
                f"T {name}: terminated in {tomb['removal_version']}: {tomb['warning_text']}"
            )
        elif depre and is_info_type("D"):
            print(
                f"D {name}: deprecation in {depre['removal_version']} (current={self.current_version}): {depre['warning_text']}"
            )

    def runtime_process_plugin(self, plugin_routing, plugin_types):
        for plugin_type in plugin_types:
            matching = [
                name
                for name in plugin_routing[plugin_type]
                if any(test(name) for test in self.name_tests)
            ]
            for name in matching:
                self.print_runtime(
                    f"{plugin_type} {name}", plugin_routing[plugin_type][name]
                )

    def run(self, context: CollectionContext):
        if context.type != context.COLLECTION:
            raise AndeboxException(
                "Action 'runtime' must be executed in a collection context!"
            )
        with open(Path("meta") / "runtime.yml") as runtime_yml:
            runtime = yaml.safe_load(runtime_yml)

        plugin_types = (
            [context.args.plugin_type] if context.args.plugin_type else PLUGIN_TYPES
        )
        _, _, self.current_version = context.read_coll_meta()
        self.info_type = context.args.info_type

        def name_test(name, other):
            if name.endswith(".py"):
                name = name.split("/")[-1]
                name = name.split(".")[0]
            return name == other

        test_func = re.search if context.args.regex else name_test
        self.name_tests = [partial(test_func, n) for n in context.args.plugin_names]

        self.runtime_process_plugin(runtime["plugin_routing"], plugin_types)
