from __future__ import print_function
import os
import pkgutil
from .core import Scannable, LogFileOutput, Parser, IniConfigFile  # noqa: F401
from .core import FileListing, LegacyItemAccess, SysconfigOptions  # noqa: F401
from .core import YAMLParser, JSONParser, XMLParser, CommandParser  # noqa: F401
from .core import AttributeDict  # noqa: F401
from .core import Syslog  # noqa: F401
from .core.archives import COMPRESSION_TYPES, extract  # noqa: F401
from .core import dr  # noqa: F401
from .core.context import ClusterArchiveContext, HostContext, HostArchiveContext  # noqa: F401
from .core.dr import SkipComponent  # noqa: F401
from .core.hydration import create_context
from .core.plugins import combiner, fact, metadata, parser, rule  # noqa: F401
from .core.plugins import datasource, condition, incident  # noqa: F401
from .core.plugins import make_response, make_metadata, make_fingerprint  # noqa: F401
from .core.filters import add_filter, apply_filters, get_filters  # noqa: F401
from .formats import get_formatter
from .parsers import get_active_lines  # noqa: F401
from .util import defaults  # noqa: F401


package_info = dict((k, None) for k in ["RELEASE", "COMMIT", "VERSION", "NAME"])


for name in package_info:
    package_info[name] = pkgutil.get_data(__name__, name).strip().decode("utf-8")


def get_nvr():
    return "{0}-{1}-{2}".format(package_info["NAME"],
                                package_info["VERSION"],
                                package_info["RELEASE"])


RULES_STATUS = {}
"""
Mapping of dictionaries containing nvr and commitid for each rule repo included
in this instance

{"rule_repo_1": {"version": nvr(), "commit": sha1}}
"""


def add_status(name, nvr, commit):
    """
    Rule repositories should call this method in their package __init__ to
    register their version information.
    """
    RULES_STATUS[name] = {"version": nvr, "commit": commit}


def process_dir(broker, root, graph, context, use_pandas=False):
    ctx = create_context(root, context)

    if isinstance(ctx, ClusterArchiveContext):
        from .core.cluster import process_cluster
        archives = [f for f in ctx.all_files if f.endswith(COMPRESSION_TYPES)]
        return process_cluster(archives, use_pandas=use_pandas, broker=broker)

    broker[ctx.__class__] = ctx
    broker = dr.run(graph, broker=broker)
    return broker


def _run(broker, graph=None, root=None, context=None, use_pandas=False):
    """
    run is a general interface that is meant for stand alone scripts to use
    when executing insights components.

    Args:
        root (str): None will causes a host collection in which command and
            file specs are run. A directory or archive path will cause
            collection from the directory or archive, and only file type specs
            or those that depend on `insights.core.context.HostArchiveContext`
            will execute.
        component (function or class): The component to execute. Will only execute
            the component and its dependency graph. If None, all components with
            met dependencies will execute.

    Returns:
        broker: object containing the result of the evaluation.
    """

    if not root:
        context = context or HostContext
        broker[context] = context()
        return dr.run(graph, broker=broker)

    if os.path.isdir(root):
        return process_dir(broker, root, graph, context, use_pandas)
    else:
        with extract(root) as ex:
            return process_dir(broker, ex.tmp_dir, graph, context, use_pandas)


def _load_context(path):
    if path is None:
        return

    if "." not in path:
        path = ".".join(["insights.core.context", path])
    return dr.get_component(path)


def run(component=None, root=None, print_summary=False,
        context=None, use_pandas=False,
        print_component=None):

    from .core import dr
    dr.load_components("insights.specs.default")
    dr.load_components("insights.specs.insights_archive")
    dr.load_components("insights.specs.sos_archive")
    dr.load_components("insights.specs.jdr_archive")

    args = None
    formatter = None
    if print_summary:
        import argparse
        import logging
        p = argparse.ArgumentParser(add_help=False)
        p.add_argument("archive", nargs="?", help="Archive or directory to analyze.")
        p.add_argument("-p", "--plugins", default="", help="Comma-separated list without spaces of package(s) or module(s) containing plugins.")
        p.add_argument("-v", "--verbose", help="Verbose output.", action="store_true")
        p.add_argument("-f", "--format", help="Output format.", default="insights.formats.text")
        p.add_argument("-D", "--debug", help="Verbose debug output.", action="store_true")
        p.add_argument("--context", help="Execution Context. Defaults to HostContext if an archive isn't passed.")
        p.add_argument("--pandas", action="store_true", help="Use pandas dataframes with cluster rules.")

        class Args(object):
            pass

        args = Args()
        p.parse_known_args(namespace=args)
        p = argparse.ArgumentParser(parents=[p])
        args.format = "insights.formats._json" if args.format == "json" else args.format
        args.format = "insights.formats._yaml" if args.format == "yaml" else args.format
        fmt = args.format if "." in args.format else "insights.formats." + args.format
        Formatter = dr.get_component(fmt)
        if not Formatter:
            dr.load_components(fmt, continue_on_error=False)
            Formatter = get_formatter(fmt)
        Formatter.configure(p)
        p.parse_args(namespace=args)
        formatter = Formatter(args)

        logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO if args.verbose else logging.ERROR)
        context = _load_context(args.context) or context
        use_pandas = args.pandas or use_pandas

        root = args.archive or root
        if root:
            root = os.path.realpath(root)

        plugins = []
        if args.plugins:
            for path in args.plugins.split(","):
                path = path.strip()
                if path.endswith(".py"):
                    path, _ = os.path.splitext(path)
                path = path.rstrip("/").replace("/", ".")
                plugins.append(path)

        for p in plugins:
            dr.load_components(p)

        if component is None:
            component = []
            plugins = tuple(plugins)
            for c in dr.DELEGATES:
                if c.__module__.startswith(plugins):
                    component.append(c)

    if component:
        if not isinstance(component, (list, set)):
            component = [component]
        graph = {}
        for c in component:
            graph.update(dr.get_dependency_graph(c))
    else:
        graph = dr.COMPONENTS[dr.GROUPS.single]

    broker = dr.Broker()

    if formatter:
        formatter.preprocess(broker)
        broker = _run(broker, graph, root, context=context, use_pandas=use_pandas)
        formatter.postprocess(broker)
    elif print_component:
        broker = _run(broker, graph, root, context=context, use_pandas=use_pandas)
        broker.print_component(print_component)
    else:
        broker = _run(broker, graph, root, context=context, use_pandas=use_pandas)

    return broker


def main():
    run(print_summary=True)


if __name__ == "__main__":
    main()
