package cz.senslog.analytics.utils.newvalidator;

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.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

public class ThresholdChecker<DS extends TimeSeriesDatasource> {

    public static <T extends TimeSeriesDatasource> ThresholdChecker<T> disabled() {
        return ThresholdChecker.create("disabled", 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 String moduleName,
            final List<Threshold> thresholds,
            final Validator<DS> validator,
            final Consumer<ViolationReport> notifier,
            final boolean defaultEnablingProcess
    ) {
//        Objects.requireNonNull(validator);
        Objects.requireNonNull(thresholds);
        Objects.requireNonNull(notifier);

        final Map<Long, List<Threshold>> dsToTh = thresholds.stream()
                .collect(Collectors.groupingBy(Threshold::dataSourceId, mapping(Function.identity(), toList())));

        // TODO get datasource
        final Map<Long, ThresholdManager<DS>> thresholdManagers = new HashMap<>(dsToTh.size());
        for (Map.Entry<Long, List<Threshold>> datasourceEntry : dsToTh.entrySet()) {
            thresholdManagers.put(datasourceEntry.getKey(), new ThresholdManager<>(
                    new DataSource(datasourceEntry.getKey(), "datasource("+datasourceEntry.getKey()+")", moduleName), validator, datasourceEntry.getValue()
            ));
        }

        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();
    }
}
