# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
# Authors: Amador Pahim <amador@pahim.org>
#          Bestoun S. Ahmed <bestoon82@gmail.com>
#          Cleber Rosa <crosa@redhat.com>

import logging
import os
import sys

from avocado_varianter_cit.Cit import LOG, Cit
from avocado_varianter_cit.Parser import Parser

from avocado.core import exit_codes, varianter
from avocado.core.output import LOG_UI
from avocado.core.plugin_interfaces import CLI, Varianter
from avocado.core.settings import settings
from avocado.core.tree import TreeNode

#: The default order of combinations
DEFAULT_ORDER_OF_COMBINATIONS = 2


class VarianterCitCLI(CLI):

    """
    CIT Varianter options
    """

    name = "cit"
    description = "CIT Varianter options for the 'run' subcommand"

    def configure(self, parser):

        for name in ("run", "variants"):
            subparser = parser.subcommands.choices.get(name, None)
            if subparser is None:
                continue
            subparser.add_argument_group("CIT varianter options")
            settings.register_option(
                section=f"{name}.cit",
                key="parameter_file",
                metavar="PATH",
                help_msg="Paths to a parameter file",
                parser=subparser,
                default=None,
                long_arg="--cit-parameter-file",
            )

            help_msg = "Order of combinations. Maximum number is 6"
            settings.register_option(
                section=f"{name}.cit",
                key="combination_order",
                key_type=int,
                parser=subparser,
                help_msg=help_msg,
                metavar="ORDER",
                default=DEFAULT_ORDER_OF_COMBINATIONS,
                long_arg="--cit-order-of-combinations",
            )

    def run(self, config):
        if config.get("variants.debug"):
            LOG.setLevel(logging.DEBUG)


class VarianterCit(Varianter):

    """
    Processes the parameters file into variants
    """

    name = "cit"
    description = "CIT Varianter"

    def initialize(self, config):
        subcommand = config.get("subcommand")
        self.variants = None  # pylint: disable=W0201
        order = config.get(f"{subcommand}.cit.combination_order")
        if order and order > 6:
            LOG_UI.error("The order of combinations is bigger then 6")
            self.error_exit(config)

        section_key = f"{subcommand}.cit.parameter_file"
        cit_parameter_file = config.get(section_key)
        if cit_parameter_file is None:
            return
        else:
            cit_parameter_file = os.path.expanduser(cit_parameter_file)
            if not os.access(cit_parameter_file, os.R_OK):
                LOG_UI.error(
                    "parameter file '%s' could not be found or " "is not readable",
                    cit_parameter_file,
                )
                self.error_exit(config)

        try:
            parameters, constraints = Parser.parse(
                open(cit_parameter_file, encoding="utf-8")
            )
        except ValueError as details:
            LOG_UI.error("Cannot parse parameter file: %s", details)
            self.error_exit(config)

        input_data = [len(parameter[1]) for parameter in parameters]

        cit = Cit(input_data, order, constraints)
        final_list = cit.compute()
        self.headers = [  # pylint: disable=W0201
            parameter[0] for parameter in parameters
        ]
        results = [
            [parameters[j][1][final_list[i][j]] for j in range(len(final_list[i]))]
            for i in range(len(final_list))
        ]
        self.variants = []  # pylint: disable=W0201
        for combination in results:
            self.variants.append(dict(zip(self.headers, combination)))

    @staticmethod
    def error_exit(config):
        if config.get("subcommand") == "run":
            sys.exit(exit_codes.AVOCADO_JOB_FAIL)
        else:
            sys.exit(exit_codes.AVOCADO_FAIL)

    def __iter__(self):
        if self.variants is None:
            return

        variant_ids = []
        for variant in self.variants:
            variant_ids.append("-".join([variant.get(key) for key in self.headers]))

        for vid, variant in zip(variant_ids, self.variants):
            yield {
                "variant_id": vid,
                "variant": [TreeNode("", variant)],
                "paths": ["/"],
            }

    def __len__(self):
        return sum(1 for _ in self.variants) if self.variants else 0

    def to_str(self, summary, variants, **kwargs):
        """
        Return human readable representation

        The summary/variants accepts verbosity where 0 means silent and
        maximum is up to the plugin.

        :param summary: How verbose summary to output (int)
        :param variants: How verbose list of variants to output (int)
        :param kwargs: Other free-form arguments
        :rtype: str
        """
        if not self.variants:
            return ""
        out = []

        if variants:
            # variants == 0 means disable, but in plugin it's brief
            out.append(f"CIT Variants ({len(self)}):")
            for variant in self:
                out.extend(
                    varianter.variant_to_str(variant, variants - 1, kwargs, False)
                )
        return "\n".join(out)
