/**
 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.easyagents.flow.core.parser;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.easyagents.flow.core.chain.DataType;
import com.easyagents.flow.core.chain.JsCodeCondition;
import com.easyagents.flow.core.chain.Node;
import com.easyagents.flow.core.chain.NodeJoinMode;
import com.easyagents.flow.core.chain.Parameter;
import com.easyagents.flow.core.chain.RefType;
import com.easyagents.flow.core.node.BaseNode;
import com.easyagents.flow.core.util.StringUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public abstract class BaseNodeParser<T extends BaseNode> implements NodeParser<T> {

    private static final JSONObject EMPTY_JSON_OBJECT = new JSONObject(Collections.emptyMap());

    private ChainParser chainParser;

    @Override
    public ChainParser getChainParser() {
        return chainParser;
    }

    public JSONObject getData(JSONObject nodeObject) {
        JSONObject jsonObject = nodeObject.getJSONObject("data");
        return jsonObject != null ? jsonObject : EMPTY_JSON_OBJECT;
    }

    public void addParameters(BaseNode node, JSONObject data) {
        List<Parameter> inputParameters = getParameters(data, "parameters");
        node.setParameters(inputParameters);
    }

    public List<Parameter> getParameters(JSONObject data, String key) {
        return getParameters(data.getJSONArray(key));
    }

    public List<Parameter> getParameters(JSONArray parametersJsonArray) {
        if (parametersJsonArray == null || parametersJsonArray.isEmpty()) {
            return Collections.emptyList();
        }
        List<Parameter> parameters = new ArrayList<>(parametersJsonArray.size());
        for (int i = 0; i < parametersJsonArray.size(); i++) {
            JSONObject parameterJsonObject = parametersJsonArray.getJSONObject(i);
            Parameter parameter = new Parameter();
            parameter.setId(parameterJsonObject.getString("id"));
            parameter.setName(parameterJsonObject.getString("name"));
            parameter.setDescription(parameterJsonObject.getString("description"));
            parameter.setDataType(DataType.ofValue(parameterJsonObject.getString("dataType")));
            parameter.setRef(parameterJsonObject.getString("ref"));
            parameter.setValue(parameterJsonObject.getString("value"));
            parameter.setDefaultValue(parameterJsonObject.getString("defaultValue"));
            parameter.setRefType(RefType.ofValue(parameterJsonObject.getString("refType")));
            parameter.setDataType(DataType.ofValue(parameterJsonObject.getString("dataType")));
            parameter.setRequired(parameterJsonObject.getBooleanValue("required"));
            parameter.setDefaultValue(parameterJsonObject.getString("defaultValue"));
            parameter.setFlattenAggregation(
                    parameterJsonObject.getBooleanValue("flattenAggregation"));

            //新增
            parameter.setContentType(parameterJsonObject.getString("contentType"));
            parameter.setEnums(parameterJsonObject.getJSONArray("enums"));
            parameter.setFormType(parameterJsonObject.getString("formType"));
            parameter.setFormLabel(parameterJsonObject.getString("formLabel"));
            parameter.setFormPlaceholder(parameterJsonObject.getString("formPlaceholder"));
            parameter.setFormAttrs(parameterJsonObject.getString("formAttrs"));
            parameter.setFormDescription(parameterJsonObject.getString("formDescription"));

            JSONArray children = parameterJsonObject.getJSONArray("children");
            if (children != null && !children.isEmpty()) {
                parameter.addChildren(getParameters(children));
            }

            parameters.add(parameter);
        }

        return parameters;
    }


    public void addOutputDefs(BaseNode node, JSONObject data) {
        List<Parameter> outputDefs = getParameters(data, "outputDefs");
        if (outputDefs == null || outputDefs.isEmpty()) {
            return;
        }
        node.setOutputDefs(outputDefs);
    }


    @Override
    public T parse(JSONObject nodeJSONObject, JSONObject chainJSONObject, ChainParser chainParser) {
        this.chainParser = chainParser;
        JSONObject data = getData(nodeJSONObject);
        T node = doParse(nodeJSONObject, data, chainJSONObject);
        if (node != null) {

            node.setId(nodeJSONObject.getString("id"));
            node.setParentId(nodeJSONObject.getString("parentId"));
            node.setName(nodeJSONObject.getString("label"));
            node.setDescription(nodeJSONObject.getString("description"));

            if (!data.isEmpty()) {

                if (data.containsKey("joinMode")) {
                    node.setJoinMode(NodeJoinMode.ofValue(data.getString("joinMode")));
                }

                addParameters(node, data);
                addOutputDefs(node, data);

                String conditionString = data.getString("condition");

                if (StringUtil.hasText(conditionString)) {
                    node.setCondition(new JsCodeCondition(conditionString.trim()));
                }

//                Boolean async = data.getBoolean("async");
//                if (async != null) {
//                    node.setAsync(async);
//                }

                String name = data.getString("title");
                if (StringUtil.hasText(name)) {
                    node.setName(name);
                }

                String description = data.getString("description");
                if (StringUtil.hasText(description)) {
                    node.setDescription(description);
                }

                // 循环执行 start =======
                Boolean loopEnable = data.getBoolean("loopEnable");
                if (loopEnable != null) {
                    node.setLoopEnable(loopEnable);
                }

                if (loopEnable != null && loopEnable) {
                    Long loopIntervalMs = data.getLong("loopIntervalMs");
                    if (loopIntervalMs == null) {
                        loopIntervalMs = 3000L;
                    }
                    node.setLoopIntervalMs(loopIntervalMs);

                    node.setMaxLoopCount(parseMaxLoopCount(data.get("maxLoopCount")));

                    String loopBreakCondition = data.getString("loopBreakCondition");
                    if (StringUtil.hasText(loopBreakCondition)) {
                        node.setLoopBreakCondition(new JsCodeCondition(loopBreakCondition.trim()));
                    }
                }
                // 循环执行 end =======


                // 错误重试 start =======
                Boolean retryEnable = data.getBoolean("retryEnable");
                if (retryEnable != null) {
                    node.setRetryEnable(retryEnable);
                }

                if (retryEnable != null && retryEnable) {
                    Boolean resetRetryCountAfterNormal = data.getBoolean("resetRetryCountAfterNormal");
                    if (resetRetryCountAfterNormal == null) {
                        resetRetryCountAfterNormal = true;
                    }
                    node.setResetRetryCountAfterNormal(resetRetryCountAfterNormal);

                    Long retryIntervalMs = data.getLong("retryIntervalMs");
                    if (retryIntervalMs == null) {
                        retryIntervalMs = 1000L;
                    }
                    node.setRetryIntervalMs(retryIntervalMs);

                    Integer maxRetryCount = data.getInteger("maxRetryCount");
                    if (maxRetryCount == null) {
                        maxRetryCount = 3;
                    }
                    node.setMaxRetryCount(maxRetryCount);
                }
                // 错误重试 end =======
            }
        }

        return node;
    }

    /**
     * 解析节点循环总次数，并拒绝小数、零值和超出上限的配置。
     *
     * @param value 原始循环次数；为空时使用默认值 1
     * @return 合法的循环总次数
     * @throws IllegalArgumentException 循环次数不是 1～300 的整数
     */
    private int parseMaxLoopCount(Object value) {
        if (value == null) {
            return Node.MIN_LOOP_COUNT;
        }
        final int count;
        try {
            count = new BigDecimal(String.valueOf(value).trim()).intValueExact();
        } catch (ArithmeticException | NumberFormatException exception) {
            throw new IllegalArgumentException(
                    "maxLoopCount must be an integer between "
                            + Node.MIN_LOOP_COUNT
                            + " and "
                            + Node.MAX_LOOP_COUNT,
                    exception);
        }
        return count;
    }

    protected abstract T doParse(JSONObject nodeJSONObject, JSONObject data, JSONObject chainJSONObject);
}
