package cz.senslog.analytics.utils.newvalidator;

import cz.senslog.analytics.domain.AttributeType;
import cz.senslog.analytics.domain.VariableType;
import cz.senslog.analytics.utils.Tuple;
import org.apache.logging.log4j.util.Strings;

import java.time.DateTimeException;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
import static java.time.format.DateTimeFormatter.ofPattern;

public class MessageFormatter {

    private final Pattern attributePattern;
    private final Pattern variablePattern;

    public static MessageFormatter createOf(Set<VariableType> variables, Set<AttributeType> attributes) {
        if (variables.isEmpty() || attributes.isEmpty()) {
            return null;
        }
        return new MessageFormatter(variables, attributes);
    }

    public static MessageFormatter create() {
        return new MessageFormatter(Set.of(VariableType.values()), Set.of(AttributeType.values()));
    }

    private MessageFormatter(Set<VariableType> variables, Set<AttributeType> attributes) {
        final Function<Enum<?>, String> wrapWord = w -> String.format("\\b%s\\b", w.name().toLowerCase());

        Iterator<VariableType> varIter = variables.iterator();
        StringBuilder varBuilder = new StringBuilder(wrapWord.apply(varIter.next()));
        while (varIter.hasNext()) {
            varBuilder.append("|").append(wrapWord.apply(varIter.next()));
        }
        final String variableRegexStr = "(?<variable>\\$("+varBuilder+"))(\\((?<format>\\S*)\\))?";

        Iterator<AttributeType> attrIter = attributes.iterator();
        StringBuilder attrBuilder = new StringBuilder(wrapWord.apply(attrIter.next()));
        while (attrIter.hasNext()) {
            attrBuilder.append("|").append(wrapWord.apply(attrIter.next()));
        }
        final String attributeRegexStr = "(?<attribute>\\$("+attrBuilder+"))(\\((?<format>\\S*)\\))?";

        attributePattern = Pattern.compile(attributeRegexStr, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL );
        variablePattern = Pattern.compile(variableRegexStr, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL );
    }

    public String format(final String pattern, final Map<AttributeType, Double> attributeArgs, final Map<VariableType, Function<Optional<String>, String>> variableArgs) {

        String message = pattern;
        int iterationCount = 0;
        boolean found;
        do {
            Matcher matcher = variablePattern.matcher(message);
            found = matcher.find();
            if (found) {
                String variable = matcher.group("variable").substring(1);
                String format = matcher.group("format");

                message = matcher.replaceFirst(variableArgs.get(VariableType.of(variable)).apply(Optional.ofNullable(format)));
            }
        } while (found && iterationCount++ < 10);

        iterationCount = 0;
        do {
            Matcher matcher = attributePattern.matcher(message);
            found = matcher.find();
            if (found) {
                String attribute = matcher.group("attribute").substring(1);
                String format = matcher.group("format");

                double value = attributeArgs.get(AttributeType.of(attribute));
                message = matcher.replaceFirst(String.format(Strings.isBlank(format) ? "%f" : format, value));
            }
        } while (found && iterationCount++ < 10);

        return message;
    }

    /*
    public boolean isValid(final String patternMessage, List<ThresholdDimension> thresholdDimensions) {
        AttributeType[] types = new AttributeType[thresholdDimensions.size()];
        for (int i = 0; i < thresholdDimensions.size(); i++) {
            types[i] = thresholdDimensions.get(i).attributeType();
        }
        return isValid(patternMessage, types);
    }

    public boolean isValid(final String patternMessage, AttributeType[] attributeTypes) {

        if (patternMessage == null || Strings.isBlank(patternMessage) || patternMessage.isEmpty()) {
            return true;
        }

        AttributeType[] enableTypes = new AttributeType[AttributeType.values().length];
        for (AttributeType type : attributeTypes) {
            enableTypes[type.ordinal()] = type;
        }

        double testValue = 0.0;

        Matcher attrMatcher = attributePattern.matcher(patternMessage);
        while (attrMatcher.find()) {
            String attribute = attrMatcher.group("attribute").substring(1);
            String format = attrMatcher.group("format");

            AttributeType attrType = AttributeType.of(attribute);
            if (attrType == null || enableTypes[attrType.ordinal()] == null) {
                return false;
            }
            if (Strings.isNotBlank(format)) {
                try {
                    String.format(format, testValue);
                } catch (IllegalFormatException e) {
                    return false;
                }
            }
        }

        Matcher variableMatcher = variablePattern.matcher(patternMessage);
        while (variableMatcher.find()) {
            String variable = variableMatcher.group("variable").substring(1);
            String format = variableMatcher.group("format");

            if (variable.equalsIgnoreCase("timestamp") && Strings.isNotBlank(format)) {
                try {
                    OffsetDateTime.now().format(ofPattern(format));
                } catch (IllegalArgumentException | DateTimeException e) {
                    return false;
                }
            }
        }

        return true;
    }

    public boolean isNotValid(final String patternMessage, Map<AttributeType, Double> args) {
        return !isValid(patternMessage, args);
    }

     */
}
