package cz.senslog.analytics.utils.validator;

import cz.senslog.analytics.domain.AttributeType;
import cz.senslog.analytics.domain.Threshold;
import cz.senslog.analytics.domain.ThresholdDimension;
import cz.senslog.analytics.utils.validator.domain.ValidationReport;
import cz.senslog.analytics.utils.validator.domain.ValidationResult;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static java.util.Collections.emptyMap;

class ThresholdManager {

    private static final NotifyTrigger DISABLED_TRIGGER = NotifyTrigger.Mode.DISABLED.createInstance();

    private final long datasourceId;

    private final Map<Long, Threshold> thresholds;
    private final Map<Long, Map<AttributeType, NotifyTrigger>> notifyTriggers;

    ThresholdManager(long datasourceId) {
        this.datasourceId = datasourceId;
        this.thresholds = new HashMap<>();
        this.notifyTriggers = new HashMap<>();
    }

    void register(Threshold threshold, NotifyTrigger... notifyTrigger) {
        thresholds.put(threshold.id(), threshold);

        for (ThresholdDimension dimension : threshold.thresholdDimensions()) {
            for (NotifyTrigger trigger : notifyTrigger) {
                if (dimension.notifyTriggerMode().equals(trigger.mode())) {
                    notifyTriggers.computeIfAbsent(threshold.id(), id -> new HashMap<>())
                            .put(dimension.attributeType(), trigger);
                }
            }
        }
    }

    Collection<Threshold> thresholds() {
        return thresholds.values();
    }

    void accept(Threshold threshold, ValidationResult... res) {
        if (threshold.datasourceId() == this.datasourceId) {
            Map<AttributeType, NotifyTrigger> triggers = notifyTriggers.getOrDefault(threshold.id(), emptyMap());
            for (ValidationResult vRes : res) {
                triggers.getOrDefault(vRes.attributeType(), DISABLED_TRIGGER).accept(threshold, vRes);
            }
        }
    }

    boolean shouldNotify() {
        boolean shouldNotify = true;
        for (Map<AttributeType, NotifyTrigger> triggers : notifyTriggers.values()) { // over all thresholds
            for (NotifyTrigger trigger : triggers.values()) {
                shouldNotify &= trigger.shouldNotify();
            }
        }
        return shouldNotify;
    }

    ValidationReport resultsToNotify() {
        ValidationReport finalReport = ValidationReport.empty();
        for (Map.Entry<Long, Map<AttributeType, NotifyTrigger>> entry : notifyTriggers.entrySet()) {
            Threshold threshold = thresholds.get(entry.getKey());
            ValidationReport thReport = ValidationReport.empty();
            for (NotifyTrigger trigger : entry.getValue().values()) {
                thReport = thReport.join(trigger.resultsToNotify());
            }


            finalReport.join(thReport);
        }
        return finalReport;
    }
}
