from collections import defaultdict, namedtuple
import itertools


Issue = namedtuple("Issue", ["dimensions", "measure", "period", "scale"])


def get_never_met_measures(data, checked_measures):
    """Get all measures for which data has no rows"""
    result = set(checked_measures)
    for measure in checked_measures:
        for row in data:
            # Process case when row has None value for measure key.
            # Such situation occurs when report has no value for measure
            # in given slice with given fielddate.
            if row.get(measure) is not None:
                result.remove(measure)
                break
    return result


def find_issues_by_check(check, report, scale):
    """Find all issues in single check: set of dimensions and monitoring settings"""
    issues = defaultdict(list)

    # Find issues for each slice of given dimensions values.
    for dimvalues in itertools.product(*check.dimensions.values()):
        report_params = {dim_name: value for dim_name, value in zip(check.dimensions, dimvalues)}

        for level in check.levels:
            data = report.download_data(
                scale=scale,
                _period_distance=level.period,
                _measures=check.measures,
                **report_params)
            if len(data) == 0:
                issues[level.kind].append(Issue(
                    dimensions=report_params, measure=None, period=level.period, scale=scale))
                continue
            measures = get_never_met_measures(data, check.measures)
            issues[level.kind].extend(
                [Issue(report_params, measure, level.period, scale) for measure in measures])
    return issues


def issue2text(issue):
    template = (
        "No data for dimensions {dimensions} "
        + ("" if issue.measure is None else "of measure {measure} ")
        + "in period {period}{scale}"
    )
    return template.format(**dict(issue._asdict()))


def issues2text(issues):
    return ";\n".join(map(issue2text, issues))


def make_text(issues, report_table):
    if issues.get("crit"):
        result_str = "2;Critical: {}".format(issues2text(issues["crit"]))
    elif issues.get("warn"):
        result_str = "1;Warning: {}".format(issues2text(issues["warn"]))
    else:
        result_str = "0;OK"
    return result_str + " in stat db: " + report_table


def find_issues(checks, report, scale):
    """ Find missing values in report for given checks"""
    issues = defaultdict(list)
    for check in checks:
        for level in check.levels:
            assert level.kind in ["warn", "crit"]
        check_issues = find_issues_by_check(check, report, scale)
        for kind, kind_issues in check_issues.items():
            issues[kind].extend(kind_issues)
    return issues
