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

import ru.yandex.autodoc.common.out.json.JsonValueWriter;
import ru.yandex.autodoc.common.out.json.builder.JsonValueBuilder;
import ru.yandex.autodoc.common.doc.params.ParamType;
import ru.yandex.autodoc.wmtools.util.Strings;
import ru.yandex.autodoc.common.util.enums.IEnumResolver;
import ru.yandex.common.util.collections.Cf;
import ru.yandex.autodoc.common.out.json.builder.ContainerBuilder;
import ru.yandex.autodoc.wmtools.params.fetch.ParamHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author avhaliullin
 */
public final class JsonValueViews {
    private JsonValueViews() {
    }

    public static JsonValueView<Integer, Void> intView(final Integer example) {
        return new AbstractJsonValueView<Integer, Void>() {
            @Override
            public ParamHolder<Integer> fetch(JsonValueContext context, Void ignored) {
                return context.asInt();
            }

            @Override
            public JsonValueWriter getExample(Set<JsonValueView> usedViews) {
                return new JsonValueWriter() {
                    @Override
                    public <C extends ContainerBuilder> C writeValue(JsonValueBuilder<C> builder) {
                        return builder.valueInt(example);
                    }
                };
            }

            @Override
            public List<String> getExtraInfo() {
                return Cf.list();
            }
        };
    }

    public static JsonValueView<Long, Void> longView(final Long example) {
        return new AbstractJsonValueView<Long, Void>() {
            @Override
            public ParamHolder<Long> fetch(JsonValueContext context, Void ignored) {
                return context.asLong();
            }

            @Override
            public JsonValueWriter getExample(Set<JsonValueView> usedViews) {
                return new JsonValueWriter() {
                    @Override
                    public <C extends ContainerBuilder> C writeValue(JsonValueBuilder<C> builder) {
                        return builder.valueLong(example);
                    }
                };
            }

            @Override
            public List<String> getExtraInfo() {
                return Cf.list();
            }
        };
    }

    public static class StringViewBuilder {
        private final String example;
        private Integer minLength;
        private Integer maxLength;
        private boolean trim = false;
        private boolean normalizeSpaces = false;
        private boolean escapeHTML = false;
        private boolean nullIfEmpty = false;

        public StringViewBuilder(String example) {
            this.example = example;
        }

        public StringViewBuilder minLength(Integer minLength) {
            this.minLength = minLength;
            return this;
        }

        public StringViewBuilder maxLength(Integer maxLength) {
            this.maxLength = maxLength;
            return this;
        }

        public StringViewBuilder escapeHTML(boolean escapeHTML) {
            this.escapeHTML = escapeHTML;
            return this;
        }

        public StringViewBuilder nullIfEmpty(boolean nullIfEmpty) {
            this.nullIfEmpty = nullIfEmpty;
            return this;
        }

        public StringViewBuilder nullIfEmpty() {
            return nullIfEmpty(true);
        }

        public StringViewBuilder trim(boolean trim) {
            this.trim = trim;
            return this;
        }

        public StringViewBuilder trim() {
            return trim(true);
        }

        public StringViewBuilder normalizeSpaces(boolean normalizeSpaces) {
            this.normalizeSpaces = normalizeSpaces;
            return this;
        }

        public StringViewBuilder normalizeSpaces() {
            return normalizeSpaces(true);
        }

        public StringViewBuilder normalizeAndTrimSpaces() {
            normalizeSpaces();
            return trim();
        }

        public JsonValueView<String, Void> build() {
            return new AbstractJsonValueView<String, Void>() {
                private final List<String> extraInfo = new ArrayList<String>() {{
                    if (minLength != null) add("Min length: " + minLength);
                    if (maxLength != null) add("Max length: " + maxLength);
                    if (escapeHTML) add("Escapes HTML");
                }};

                @Override
                public ParamHolder<String> fetch(JsonValueContext context, Void injected) {
                    ParamHolder<String> res = context.asString();
                    String value = res.getValue();

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

                    if (!res.hasError() && value != null && value.isEmpty() && nullIfEmpty) {
                        value = null;
                    }
                    if (!res.hasError() && value != null) {
                        if (escapeHTML) {
                            value = Strings.escapeXSS(value);
                        }
                        if (minLength != null && value.length() < minLength) {
                            res.errorValueTooShort(minLength);
                        }
                        if (maxLength != null && value.length() > maxLength) {
                            res.errorValueTooLong(maxLength);
                        }
                    }
                    return res.map(value);
                }

                @Override
                public JsonValueWriter getExample(Set<JsonValueView> usedViews) {
                    return new JsonValueWriter() {
                        @Override
                        public <C extends ContainerBuilder> C writeValue(JsonValueBuilder<C> builder) {
                            return builder.value(example);
                        }
                    };
                }

                @Override
                public List<String> getExtraInfo() {
                    return extraInfo;
                }
            };
        }
    }

    public static StringViewBuilder stringBuilder(String example) {
        return new StringViewBuilder(example);
    }

    public static JsonValueView<String, Void> stringView(final String example) {
        return stringBuilder(example).build();
    }


    public static JsonValueView<Boolean, Void> booleanView(final Boolean example) {
        return new AbstractJsonValueView<Boolean, Void>() {
            @Override
            public ParamHolder<Boolean> fetch(JsonValueContext context, Void ignored) {
                return context.asBoolean();
            }

            @Override
            public JsonValueWriter getExample(Set<JsonValueView> usedViews) {
                return new JsonValueWriter() {
                    @Override
                    public <C extends ContainerBuilder> C writeValue(JsonValueBuilder<C> builder) {
                        return builder.valueBoolean(example);
                    }
                };
            }

            @Override
            public List<String> getExtraInfo() {
                return Cf.list();
            }
        };
    }

    public static <T extends Enum<T>> JsonValueView<T, Void> enumView(final T example, final IEnumResolver<T> resolver) {
        return new AbstractJsonValueView<T, Void>() {
            @Override
            public ParamHolder<T> fetch(JsonValueContext context, Void ignored) {
                ParamHolder<String> holder = context.asString();

                T res = null;
                if (!holder.hasError() && holder.getValue() != null) {
                    String resString = holder.getValue();
                    for (T val : resolver.listAll()) {
                        if (val.name().equalsIgnoreCase(resString)) {
                            res = val;
                        }
                    }
                    if (res == null) {
                        holder.errorIllegalValueType(ParamType.JsonNodeType.enumName(resolver));
                    }
                }
                return holder.map(res);
            }

            @Override
            public JsonValueWriter getExample(Set<JsonValueView> usedViews) {
                return new JsonValueWriter() {
                    @Override
                    public <C extends ContainerBuilder> C writeValue(JsonValueBuilder<C> builder) {
                        return builder.valueEnum(example, resolver);
                    }
                };
            }

            @Override
            public List<String> getExtraInfo() {
                return Cf.list();
            }
        };
    }
}
