package cz.senslog.analytics.utils.validator;


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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class OnChangeNotifyTrigger implements NotifyTrigger {

    private static final class CacheWrapper {
        private final ThresholdValidationResult[] resultsArr;
        private int freeIndex;

        private CacheWrapper(ThresholdValidationResult[] resultsArr, int freeIndex) {
            this.resultsArr = resultsArr;
            this.freeIndex = freeIndex;
        }
    }

    private static final int CACHE_LEN = 2;

    private final Map<Long, CacheWrapper> cacheMap;

    private List<ThresholdValidationResult> tempToNotify;

    public OnChangeNotifyTrigger() {
        this.cacheMap = new HashMap<>();
    }

    @Override
    public void accept(Threshold threshold, ValidationResult... validationResults) {
        long thresholdId = threshold.id();
        if (!cacheMap.containsKey(thresholdId)) {
            cacheMap.put(thresholdId, new CacheWrapper(new ThresholdValidationResult[CACHE_LEN], 0));
        }
        CacheWrapper cache = cacheMap.get(thresholdId);
        cache.resultsArr[cache.freeIndex] = new ThresholdValidationResult(threshold, validationResults);
        cache.freeIndex = nextIndex(cache.freeIndex);
    }

    @Override
    public Mode mode() {
        return Mode.ON_CHANGE;
    }

    private static int nextIndex(int index) {
        return  (index + 1) % CACHE_LEN;
    }

    private static int previousIndex(int index) {
        return (index <= 0 ? CACHE_LEN : index) - 1;
    }

    @Override
    public boolean shouldNotify() {
        boolean notify = false;
        tempToNotify = new ArrayList<>(cacheMap.size());
        for (CacheWrapper cache : cacheMap.values()) { // over all thresholds
            int currentDataInd = previousIndex(cache.freeIndex);
            ThresholdValidationResult currentRes = cache.resultsArr[currentDataInd];
            ThresholdValidationResult previousRes = cache.resultsArr[previousIndex(currentDataInd)];
            if ((previousRes == null || !previousRes.isViolated()) && currentRes.isViolated()) {
                notify = true; // enter the threshold
                tempToNotify.add(currentRes);
            } else if(!currentRes.isViolated() && (previousRes != null && previousRes.isViolated())) {
                notify = true; // exit the threshold
                tempToNotify.add(currentRes);
            }
        }
        return notify;
    }

    @Override
    public ValidationReport resultsToNotify() {

        if (tempToNotify == null) {
            return ValidationReport.empty();
        }

        StringBuilder alertMessageBuilder = new StringBuilder();
        List<ValidationResult> attributeValidationResults = new ArrayList<>();

        for (ThresholdValidationResult thResult : tempToNotify) {
            for (ThresholdDimension thEdge : thResult.threshold().thresholdDimensions()) {
                    for (ValidationResult validationResult : thResult.validationResults()) {
                        if (validationResult.attributeType().equals(thEdge.attributeType())) {
                            attributeValidationResults.add(validationResult);
                        }
                }
            }

            if (thResult.threshold().enableAlert()) {
                String alertMessage = thResult.threshold().customAlertMessage();
                if (alertMessage != null) {
                    alertMessageBuilder.append(alertMessage).append("\n");
                }
            }
        }

        tempToNotify = null;

        return new ValidationReport(
                alertMessageBuilder.toString(),
                attributeValidationResults.toArray(new ValidationResult[0])
        );
    }

}
