package ru.yandex.autodoc.wmtools.params.fetch.impl;


import ru.yandex.autodoc.common.doc.params.ParamType;
import ru.yandex.autodoc.wmtools.util.Strings;
import ru.yandex.autodoc.common.doc.ExtraInfoItem;
import ru.yandex.autodoc.common.doc.abstracts.ParamsDocRegistrar;
import ru.yandex.autodoc.common.doc.params.ParamDescriptor;
import ru.yandex.autodoc.wmtools.errors.CommonUserException;
import ru.yandex.autodoc.wmtools.params.QueryContext;
import ru.yandex.autodoc.wmtools.params.fetch.MultiParamView;
import ru.yandex.autodoc.wmtools.params.fetch.ParamFetchUtils;
import ru.yandex.autodoc.wmtools.params.fetch.ParamHolder;
import ru.yandex.autodoc.wmtools.params.fetch.ParamUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @author avhaliullin
 */
public class PseudoMultiParamView implements MultiParamView<String> {
    private final String name;
    private final boolean required;
    private final Integer minValues;
    private final Integer maxValues;
    private final Integer minLength;
    private final Integer maxLength;
    private final boolean normalizeSpaces;
    private final boolean trim;
    private final boolean skipEmpty;
    private final String delimiters;
    private final boolean escapeHTML;

    public PseudoMultiParamView(ParamsDocRegistrar registrar, String name, boolean required, String delimiters,
                                Integer minValues, Integer maxValues, Integer minLength, Integer maxLength, boolean trim,
                                boolean normalizeSpaces, boolean skipEmpty, boolean escapeHTML, String desc) {
        this.name = name;
        this.required = required;
        this.minValues = minValues;
        this.maxValues = maxValues;
        this.minLength = minLength;
        this.maxLength = maxLength;
        this.trim = trim;
        this.normalizeSpaces = normalizeSpaces;
        this.skipEmpty = skipEmpty;
        this.escapeHTML = escapeHTML;
        this.delimiters = delimiters;
        registrar.register(new ParamDescriptor(name, required, ParamType.separated(delimiters), null,
                desc, extraInfos(minValues, maxValues, minLength, maxLength, escapeHTML)));
    }

    public List<String> fetchValues(QueryContext context) {
        return ParamUtils.unwrap(fetch(context));
    }

    public ParamHolder<List<ParamHolder<String>>> fetch(QueryContext context) {
        String separated = ParamFetchUtils.getNullableParam(context.getRequest(), name);
        boolean hasErrorOnCollection = false;
        if (separated == null || separated.trim().isEmpty()) {
            if (required) {
                hasErrorOnCollection = true;
                context.addError(CommonUserException.createRequiredParamMissed(name));
            }
            return new ParamHolder<List<ParamHolder<String>>>(name, new ArrayList<ParamHolder<String>>(), context, hasErrorOnCollection, separated);
        }

        StringTokenizer st = new StringTokenizer(separated, delimiters);

        List<ParamHolder<String>> result = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String value = st.nextToken();

            if (trim) {
                value = value.trim();
            }
            if (normalizeSpaces) {
                value = Strings.normalizeSpaces(value);
            }
            if (escapeHTML) {
                value = Strings.escapeXSS(value);
            }

            ParamHolder<String> ph = new ParamHolder<>(name, value, context, false, value);
            if (minLength != null && value.length() < minLength) {
                ph.errorValueTooShort(minLength);
            }
            if (maxLength != null && value.length() > maxLength) {
                ph.errorValueTooLong(maxLength);
            }

            if (!(skipEmpty && value.isEmpty())) {
                result.add(ph);
            }
        }

        if (minValues != null && result.size() < minValues) {
            hasErrorOnCollection = true;
            context.addError(CommonUserException.createTooFewValues(name, minValues, result.size()));
        }
        if (maxValues != null && result.size() > maxValues) {
            hasErrorOnCollection = true;
            context.addError(CommonUserException.createTooManyValues(name, maxValues, result.size()));
        }

        return new ParamHolder<>(name, result, context, hasErrorOnCollection, separated);
    }

    private static List<ExtraInfoItem> extraInfos(Integer minValues, Integer maxValues, Integer minLength, Integer maxLength, boolean escapeXML) {
        List<ExtraInfoItem> res = new ArrayList<>();
        if (minValues != null) {
            res.add(ExtraInfoItem.createSimple("Min values count: " + minValues));
        }
        if (maxValues != null) {
            res.add(ExtraInfoItem.createSimple("Max values count: " + maxValues));
        }
        if (minLength != null) {
            res.add(ExtraInfoItem.createSimple("Min value length: " + minLength));
        }
        if (maxLength != null) {
            res.add(ExtraInfoItem.createSimple("Max value length: " + maxLength));
        }
        if (escapeXML) {
            res.add(ExtraInfoItem.createSimple("Escapes HTML"));
        }
        return res;
    }
}
