# SPDX-FileCopyrightText: PhiBo DinoTools (2021)
# SPDX-License-Identifier: GPL-3.0-or-later

import re
from typing import Any, Dict, List, Optional, Union

import click
import nagiosplugin

from ..cli import cli
from ..context import BooleanContext
from ..helper import logger
from ..resource import RouterOSCheckResource


class InterfaceGREResource(RouterOSCheckResource):
    name = "GRE"

    def __init__(
            self,
            cmd_options: Dict[str, Any],
            names: List[str],
            regex: bool,
            single_interface: bool,
            ignore_disabled: bool,
    ):
        super().__init__(cmd_options=cmd_options)

        self._interface_data: Optional[Dict[str, Any]] = None
        self.names: List[Union[Any]] = names
        self.regex = regex
        if self.regex:
            regex_names = []
            for name in names:
                regex_names.append(re.compile(name))
            self.names = regex_names
        self.single_interface = single_interface
        self.ignore_disabled = ignore_disabled

        self._routeros_metric_values = [
            {"name": "disabled", "type": bool},
            {"name": "running", "type": bool},
            {"name": "actual-mtu", "type": int, "min": 0},
        ]

    def fetch_data(self) -> Dict[str, Dict]:
        if self._interface_data:
            return self._interface_data

        api = self._connect_api()

        logger.info("Fetching data ...")
        call = api.path(
            "/interface/gre"
        )
        call_results = tuple(call)

        self._interface_data = {}
        for result in call_results:
            if self.ignore_disabled and result["disabled"]:
                continue
            if len(self.names) == 0:
                self._interface_data[result["name"]] = result
            elif self.regex:
                for name in self.names:
                    if name.match(result["name"]):
                        self._interface_data[result["name"]] = result
            elif result["name"] in self.names:
                self._interface_data[result["name"]] = result
        return self._interface_data

    @property
    def interface_names(self):
        return tuple(self.fetch_data().keys())

    def probe(self):
        routeros_metrics = []
        data = self.fetch_data()

        if self.single_interface:
            if len(self.interface_names) == 1:
                return self.get_routeros_metrics(data[self.interface_names[0]])
        else:
            for name in self.interface_names:
                routeros_metrics += self.get_routeros_metrics(data[name], name_prefix=f"{name} ")

        return routeros_metrics


class InterfaceGREDisabledContext(BooleanContext):
    def __init__(self, name, interface_name):
        super().__init__(name=name)
        self._interface_name = interface_name

    def evaluate(self, metric, resource: InterfaceGREResource):
        if metric.value is True:
            return self.result_cls(
                nagiosplugin.state.Warn,
                hint="GRE interface '{self._interface_name}' is disabled",
                metric=metric
            )
        return self.result_cls(nagiosplugin.state.Ok)


class InterfaceGRERunningContext(BooleanContext):
    def __init__(self, name, interface_name):
        super().__init__(name=name)

        self._interface_name = interface_name

    def evaluate(self, metric, resource: InterfaceGREResource):
        if metric.value is False:
            return self.result_cls(
                state=nagiosplugin.state.Warn,
                hint=f"GRE interface '{self._interface_name}' not running",
                metric=metric
            )
        return self.result_cls(nagiosplugin.state.Ok)


@cli.command("interface.gre")
@click.option(
    "--name",
    "names",
    default=[],
    multiple=True,
    help="The name of the GRE interface to monitor. This can be specified multiple times",
)
@click.option(
    "--regex",
    "regex",
    default=False,
    is_flag=True,
    help="Treat the specified names as regular expressions and try to find all matching interfaces. (Default: not set)",
)
@click.option(
    "--single",
    "single",
    default=False,
    is_flag=True,
    help="If set the check expects the interface to exist",
)
@click.option(
    "--ignore-disabled/--no-ignore-disabled",
    default=True,
    is_flag=True,
    help="Ignore disabled interfaces",
)
@click.pass_context
def interface_gre(ctx, names, regex, single, ignore_disabled):
    """Check the state of a GRE interface."""
    resource = InterfaceGREResource(
        cmd_options=ctx.obj,
        names=names,
        regex=regex,
        single_interface=single,
        ignore_disabled=ignore_disabled,
    )
    check = nagiosplugin.Check(
        resource,
    )

    if single:
        if len(resource.interface_names) == 1:
            name = resource.interface_names[0]
            check.add(
                InterfaceGREDisabledContext("disabled", interface_name=name),
                InterfaceGRERunningContext("running", interface_name=name),
                nagiosplugin.ScalarContext("actual-mtu"),
            )
        else:
            check.results.add(
                nagiosplugin.Result(
                    nagiosplugin.state.Unknown,
                    f"Only one matching interface is allowed. Found {len(resource.interface_names)}"
                )
            )
    else:
        for name in resource.interface_names:
            check.add(
                InterfaceGREDisabledContext(f"{name} disabled", interface_name=name),
                InterfaceGRERunningContext(f"{name} running", interface_name=name),
                nagiosplugin.ScalarContext(f"{name} actual-mtu"),
            )

    check.main(verbose=ctx.obj["verbose"])
