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

import com.fasterxml.jackson.databind.JsonNode;
import ru.yandex.autodoc.common.doc.params.ParamType;
import ru.yandex.autodoc.wmtools.errors.CommonUserException;
import ru.yandex.autodoc.wmtools.params.fetch.ParamHolder;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.autodoc.wmtools.params.QueryContext;


/**
 * @author avhaliullin
 */
public class JsonValueContext {
    protected final QueryContext queryContext;
    protected final String paramName;
    protected final String jsonPath;
    protected final JsonNode node;
    protected final JsonContainerContext parent;
    private UserException error;

    public JsonValueContext(QueryContext queryContext, String paramName, JsonNode node, String jsonPath, JsonContainerContext parent) {
        this.queryContext = queryContext;
        this.paramName = paramName;
        this.jsonPath = jsonPath;
        this.node = node;
        this.parent = parent;
    }

    public boolean hasError() {
        return error != null;
    }

    public void addError(UserException error) {
        if (this.error == null) {
            this.error = error;
            if (parent != null) {
                parent.reportChildError();
            }
            queryContext.addError(error);
        }
    }

    protected String jsonParamName() {
        return jsonParamName(jsonPath);
    }

    protected String jsonParamName(String jsonPath) {
        return paramName + jsonPath;
    }

    public boolean isNull() {
        return node == null || node.isNull() || node.isMissingNode();
    }

    private String nodeString() {
        return isNull() ? null : String.valueOf(node);
    }

    public ParamHolder<String> asString() {
        String value = null;
        boolean hasError = hasError();
        if (!isNull() && !hasError) {
            if (!node.isTextual()) {
                addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.STRING));
                hasError = true;
            } else {
                value = node.asText();
            }
        }
        return new ParamHolder<>(jsonParamName(), value, queryContext, hasError, (node != null && node.isTextual()) ? node.asText() : nodeString());
    }

    public ParamHolder<Boolean> asBoolean() {
        Boolean value = null;
        boolean hasError = hasError();
        if (!isNull() && !hasError) {
            if (!node.isBoolean()) {
                addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.BOOLEAN));
                hasError = true;
            } else {
                value = node.asBoolean();
            }
        }
        return new ParamHolder<>(jsonParamName(), value, queryContext, hasError, nodeString());
    }

    public ParamHolder<Integer> asInt() {
        Integer value = null;
        boolean hasError = hasError();
        if (!isNull() && !hasError) {
            if (!node.isInt()) {
                addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.INTEGER));
                hasError = true;
            } else {
                value = node.asInt();
            }
        }
        return new ParamHolder<>(jsonParamName(), value, queryContext, hasError, nodeString());
    }

    public ParamHolder<Long> asLong() {
        Long value = null;
        boolean hasError = hasError();
        if (!isNull() && !hasError) {
            if (!node.isLong()) {
                addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.INTEGER));
                hasError = true;
            } else {
                value = node.asLong();
            }
        }
        return new ParamHolder<>(jsonParamName(), value, queryContext, hasError, nodeString());
    }

    public JsonObjectContext asObject() {
        JsonNode resNode = node;
        if (!isNull() && !node.isObject()) {
            addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.OBJECT));
            resNode = null;
        }
        return new JsonObjectContext(queryContext, paramName, resNode, jsonPath, parent);
    }

    public JsonArrayContext asArray() {
        JsonNode resNode = node;
        if (!isNull() && !node.isArray()) {
            addError(CommonUserException.createIllegalValueType(jsonParamName(), nodeString(), ParamType.JsonNodeType.ARRAY));
            resNode = null;
        }
        return new JsonArrayContext(queryContext, paramName, resNode, jsonPath, parent);
    }
}
