package cz.senslog.analytics.utils.validator;

import cz.senslog.analytics.domain.TimeSeriesDatasource;

import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import static java.util.Collections.emptyList;

public class ThresholdChecker<DS extends TimeSeriesDatasource> {

    public static <T extends TimeSeriesDatasource> ThresholdChecker<T> disabled() {
        return ThresholdChecker.create(emptyList(), null, r -> {}, true);
    }

    private final Map<Long, ThresholdManager<DS>> thresholdManagers;
    private final Consumer<ViolationReport> notifier;
    private final boolean defaultEnablingProcess;

    public static <DS extends TimeSeriesDatasource> ThresholdChecker<DS> create(
            final List<DataSource> dataSources,
            final Validator<DS> validator,
            final Consumer<ViolationReport> notifier,
            final boolean defaultEnablingProcess
    ) {
        final Map<Long, ThresholdManager<DS>> thresholdManagers = new HashMap<>(dataSources.size());
        for (DataSource ds : dataSources) {
            thresholdManagers.put(ds.id(), new ThresholdManager<>(ds, validator));
        }
        return new ThresholdChecker<>(notifier, thresholdManagers, defaultEnablingProcess);
    }

    private ThresholdChecker(Consumer<ViolationReport> notifier, Map<Long, ThresholdManager<DS>> thresholdManagers, boolean defaultEnablingProcess) {
        this.defaultEnablingProcess = defaultEnablingProcess;
        this.notifier = notifier;
        this.thresholdManagers = thresholdManagers;
    }

    public boolean check(DS data) {

        long sourceId = data.datasourceId();
        OffsetDateTime timestamp = data.timestamp();

        ThresholdManager<DS> thresholdManager = thresholdManagers.get(sourceId);
        if (sourceId <= 0 || thresholdManager == null) {
            return defaultEnablingProcess;
        }

        ValidationReport validationReport = thresholdManager.accept(data);

        if (validationReport.message().isPresent()) {
            notifier.accept(new ViolationReport(sourceId, timestamp, validationReport.message().get()));
        }

        return validationReport.process();
    }
}
