# (C) Datadog, Inc. 2010-2016
# All rights reserved
# Licensed under Simplified BSD License (see LICENSE)

# project
from checks import AgentCheck

from checks.libs.wmi.sampler import WMISampler

from collections import namedtuple

WMIMetric = namedtuple('WMIMetric', ['name', 'value', 'tags'])


class InvalidWMIQuery(Exception):
    """
    Invalid WMI Query.
    """
    pass


class MissingTagBy(Exception):
    """
    WMI query returned multiple rows but no `tag_by` value was given.
    """
    pass


class TagQueryUniquenessFailure(Exception):
    """
    'Tagging query' did not return or returned multiple results.
    """
    pass


class WinWMICheck(AgentCheck):
    """
    WMI check.

    Windows only.
    """
    def __init__(self, name, init_config, agentConfig, instances):
        AgentCheck.__init__(self, name, init_config, agentConfig, instances)
        self.wmi_samplers = {}
        self.wmi_props = {}

    def _format_tag_query(self, sampler, wmi_obj, tag_query):
        """
        Format `tag_query` or raise on incorrect parameters.
        """
        try:
            link_source_property = int(wmi_obj[tag_query[0]])
            target_class = tag_query[1]
            link_target_class_property = tag_query[2]
            target_property = tag_query[3]
        except IndexError:
            self.log.error(
                u"Wrong `tag_queries` parameter format. "
                "Please refer to the configuration file for more information.")
            raise
        except TypeError:
            self.log.error(
                u"Incorrect 'link source property' in `tag_queries` parameter:"
                " `{wmi_property}` is not a property of `{wmi_class}`".format(
                    wmi_property=tag_query[0],
                    wmi_class=sampler.class_name,
                )
            )
            raise

        return target_class, target_property, [{link_target_class_property: link_source_property}]

    def _raise_on_invalid_tag_query_result(self, sampler, wmi_obj, tag_query):
        """
        """
        target_property = sampler.property_names[0]
        target_class = sampler.class_name

        if len(sampler) != 1:
            message = "no result was returned"
            if len(sampler):
                message = "multiple results returned (one expected)"

            self.log.warning(
                u"Failed to extract a tag from `tag_queries` parameter: {reason}."
                " wmi_object={wmi_obj} - query={tag_query}".format(
                    reason=message,
                    wmi_obj=wmi_obj, tag_query=tag_query,
                )
            )
            raise TagQueryUniquenessFailure

        if sampler[0][target_property] is None:
            self.log.error(
                u"Incorrect 'target property' in `tag_queries` parameter:"
                " `{wmi_property}` is empty or is not a property"
                "of `{wmi_class}`".format(
                    wmi_property=target_property,
                    wmi_class=target_class,
                )
            )
            raise TypeError

    def _get_tag_query_tag(self, sampler, wmi_obj, tag_query):
        """
        Design a query based on the given WMIObject to extract a tag.

        Returns: tag or TagQueryUniquenessFailure exception.
        """
        self.log.debug(
            u"`tag_queries` parameter found."
            " wmi_object={wmi_obj} - query={tag_query}".format(
                wmi_obj=wmi_obj, tag_query=tag_query,
            )
        )

        # Extract query information
        target_class, target_property, filters = \
            self._format_tag_query(sampler, wmi_obj, tag_query)

        # Create a specific sampler
        tag_query_sampler = WMISampler(
            self.log,
            target_class, [target_property],
            filters=filters,
            **sampler.connection
        )

        tag_query_sampler.sample()

        # Extract tag
        self._raise_on_invalid_tag_query_result(tag_query_sampler, wmi_obj, tag_query)

        link_value = str(tag_query_sampler[0][target_property]).lower()

        tag = "{tag_name}:{tag_value}".format(
            tag_name=target_property.lower(),
            tag_value="_".join(link_value.split())
        )

        self.log.debug(u"Extracted `tag_queries` tag: '{tag}'".format(tag=tag))
        return tag

    def _extract_metrics(self, wmi_sampler, tag_by, tag_queries, constant_tags):
        """
        Extract and tag metrics from the WMISampler.

        Raise when multiple WMIObject were returned by the sampler with no `tag_by` specified.

        Returns: List of WMIMetric
        ```
        [
            WMIMetric("freemegabytes", 19742, ["name:_total"]),
            WMIMetric("avgdiskbytesperwrite", 1536, ["name:c:"]),
        ]
        ```
        """
        if len(wmi_sampler) > 1 and not tag_by:
            raise MissingTagBy(
                u"WMI query returned multiple rows but no `tag_by` value was given."
                " class={wmi_class} - properties={wmi_properties} - filters={filters}".format(
                    wmi_class=wmi_sampler.class_name, wmi_properties=wmi_sampler.property_names,
                    filters=wmi_sampler.filters,
                )
            )

        metrics = []
        tag_by = tag_by.lower()

        for wmi_obj in wmi_sampler:
            tags = list(constant_tags) if constant_tags else []

            # Tag with `tag_queries` parameter
            for query in tag_queries:
                try:
                    tags.append(self._get_tag_query_tag(wmi_sampler, wmi_obj, query))
                except TagQueryUniquenessFailure:
                    continue

            for wmi_property, wmi_value in wmi_obj.iteritems():
                # Tag with `tag_by` parameter
                if wmi_property == tag_by:
                    tag_value = str(wmi_value).lower()
                    if tag_queries and tag_value.find("#") > 0:
                        tag_value = tag_value[:tag_value.find("#")]

                    tags.append(
                        "{name}:{value}".format(
                            name=tag_by, value=tag_value
                        )
                    )
                    continue

                # No metric extraction on 'Name' property
                if wmi_property == 'name':
                    continue