package net.northking.cctp.element.database.keywords;


import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.Argument;
import net.northking.cctp.element.core.annotation.Keyword;
import net.northking.cctp.element.core.annotation.Keywords;
import net.northking.cctp.element.core.annotation.Return;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.type.DataType;
import net.northking.cctp.element.core.type.ParamScope;
import net.northking.cctp.element.database.keywords.constants.ConfigPath;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Keywords
public class DatabaseTools {

    /**
     * 日志
     */
    private final static Logger logger = LoggerFactory.getLogger(DatabaseTools.class);

    /**
     * <p>执行sql语句</p>
     * Example:
     * <pre>
     *     	Map<String, Object> objectObjectHashMap = new HashMap<>();
     * 		objectObjectHashMap.put("useSSL",false);
     * 		objectObjectHashMap.put("allowMultiQueries",true);
     * 		objectObjectHashMap.put("serverTimezone","Asia/Shanghai");
     * 		objectObjectHashMap.put("useUnicode",true);
     * 		objectObjectHashMap.put("characterEncoding","utf-8");
     *
     *     executeSQL(1, "192.168.77.26", "3306", "atu_script_case", "northking", "northking@002987", "select * from atu_case_info","224f6e97b3d5453f8f392997e625e65b","777e064611ea44ad90996540faa2d559","1",objectObjectHashMap,"atu_script_case")
     *
     *     result=
     *     [
     *     {"tenant_id":"11111111","created_time":"2022-11-02 10:04:51","updated_time":"2022-11-02 10:09:08","bug_log_id":"2222","updated_by":"22222","message":"666","bug_id":"2222222","created_by":"222222222"},
     *     {"tenant_id":"null","created_time":"2022-11-02 10:06:50","updated_time":"2022-11-02 10:09:08","bug_log_id":"3333","updated_by":"null","message":"666","bug_id":"33333","created_by":"null"}
     *     ]
     *
     * </pre>
     *
     * @param type 数据源类型：0-MongoDB，1-mysql，2-Oracle，3-达梦，4-OceanBase，5-db2
     * @param host 主机
     * @param port 端口号
     * @param dbName 数据库名
     * @param username 用户名
     * @param password 密码
     * @param sqlContent SQL语句
     * @param projectId 項目id
     * @param envId 环境id
     * @param sourceType 来源类型 1-静态数据源，2-动态数据源
     * @param advancedConfigMap 高级属性
     * @param name 数据源名称
     * @param value 组件返回值
     * @param variableNames 自定义变量名集合
     * @param executeContext 上下文对象
     * @return hash表
     */
    @Keyword(alias = "执行sql",category = "0",attributes = "-1")
    @Return(name = "map", comment = "hash表", type = DataType.MAP)
    public String executeSQL(
            @Argument(name = "type", comment = "数据源类型：0-MongoDB，1-mysql，2-Oracle，3-达梦，4-OceanBase，5-db2", type = DataType.INTEGER,required = false,notNull = false) Integer type,
            @Argument(name = "host", comment = "主机", type = DataType.STRING,required = false,notNull = false) String host,
            @Argument(name = "port", comment = "端口号", type = DataType.STRING,required = false,notNull = false) String port,
            @Argument(name = "dbName", comment = "数据库名", type = DataType.STRING,required = false,notNull = false) String dbName,
            @Argument(name = "username", comment = "用户名", type = DataType.STRING,required = false,notNull = false) String username,
            @Argument(name = "password", comment = "密码", type = DataType.STRING,required = false,notNull = false) String password,
            @Argument(name = "sqlContent", comment = "SQL语句", type = DataType.STRING) String sqlContent,
            @Argument(name = "projectId", comment = "項目id", type = DataType.STRING) String projectId,
            @Argument(name = "envId", comment = "环境id", type = DataType.STRING) String envId,
            @Argument(name = "sourceType", comment = "来源类型 1-静态数据源，2-动态数据源", type = DataType.STRING) String sourceType,
            @Argument(name = "advancedConfigMap", comment = "高级属性", type = DataType.MAP,required = false,notNull = false) Map<String, Object> advancedConfigMap,
            @Argument(name = "name", comment = "数据源名称", type = DataType.STRING) String name,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "variableNames", comment = "自定义变量名集合", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String variableNames,
            IExecuteContext executeContext)
    {
        HashMap<String, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("dbName",dbName);
        objectObjectHashMap.put("host",host);
        objectObjectHashMap.put("password",password);
        objectObjectHashMap.put("port",port);
        objectObjectHashMap.put("type",type);
        objectObjectHashMap.put("username",username);
        objectObjectHashMap.put("sqlContent",sqlContent);
        objectObjectHashMap.put("projectId",projectId);
        objectObjectHashMap.put("envId",envId);
        objectObjectHashMap.put("name",name);
        objectObjectHashMap.put("advancedConfigMap",advancedConfigMap);
        objectObjectHashMap.put("sourceType",sourceType);
        objectObjectHashMap.put("tenantId", executeContext.getContextVariable("__tenantId"));

        String result = HttpUtil.createPost(executeContext.getProperty(ConfigPath.ATU_SERVER_SERVERURL) +
                executeContext.getProperty(ConfigPath.DATABASE_EXECUTE_SQL_PATH,
                        "/api/cctp-projects/pub/CpProjectEnvDss/execute")).contentType("application/json")
                .body(JSON.toJSONString(objectObjectHashMap)).execute().body();

        logger.debug("sql execute result => {}", result);
        JSONObject resultJson = JSONObject.parseObject(result);
        if (!"000000".equals(resultJson.getString("code"))){
            throw new ExecuteException("sql执行异常，" + resultJson.getString("message"));
        }
        String dataStr = resultJson.getString("data");
        if (StrUtil.isBlank(value) && StrUtil.isBlank(variableNames)){
            logger.debug("执行完成，不需要设置执行结果");
            if (dataStr != null){
                return resultJson.getString("data");
            }
            return "";
        }

        // 数据库写操作，返回影响行数
        if (sqlContent.startsWith("insert") || sqlContent.startsWith("update") || sqlContent.startsWith("delete") ) {
            String num = "0";
            if (dataStr != null){
                num = resultJson.getString("data");
            }
            setValue2Context(value, num, executeContext);
            return num;
        }

        // JSON默认排序
        int defaultParserFeature = JSON.DEFAULT_PARSER_FEATURE;
        // 设置Feature.OrderedField，按照字符串中的顺序排序
        JSON.DEFAULT_PARSER_FEATURE = Feature.config(JSON.DEFAULT_PARSER_FEATURE, Feature.OrderedField, true);
        List sqlResult = JSONObject.parseArray(dataStr);
        // JSON.DEFAULT_PARSER_FEATURE为全局设置，使用完还原默认值
        JSON.DEFAULT_PARSER_FEATURE = defaultParserFeature;

        String key = value;
        // 自定义变量名集合
        List<String> scriptVarByAlias = new ArrayList<>();
        if (StringUtils.isNotBlank(variableNames)) {
            String[] split = variableNames.split(",");
            scriptVarByAlias = Arrays.asList(split);
        }

        if (!CollectionUtils.isEmpty(sqlResult)) {
            int size = sqlResult.size();
            for (int i = 0; i < size; i++) {
                LinkedHashMap<String, Object> jsonObject = JSONObject.parseObject(sqlResult.get(i).toString(),LinkedHashMap.class);
                List<String> keys = new ArrayList<>();
                for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                    if (StrUtil.isNotBlank(value)) {
                        executeContext.setVariable(key + "[" + i + "]." + entry.getKey(), String.valueOf(entry.getValue()));
                        logger.info("脚本变量:" + key + "[" + i + "]." + entry.getKey() + "：{}", entry.getValue());
                    }
                    // 记录字段名称
                    keys.add(entry.getKey());
                }
                if (!CollectionUtils.isEmpty(scriptVarByAlias)) {
                    for (int j = 0; j < scriptVarByAlias.size(); j++) {
                        ArrayList<String> list = new ArrayList<>(keys);
                        executeContext.setVariable(scriptVarByAlias.get(j) + "_" + (i + 1) , String.valueOf(jsonObject.get(list.get(j))));
                        logger.info("变量名:"+scriptVarByAlias.get(j) + "_" + (i + 1) + "：{}", String.valueOf(jsonObject.get(list.get(j))));
                    }
                }
            }
        }

        String resultData = JSONObject.parseObject(result).getString("data");
        setValue2Context(value, resultData, executeContext);
        return null==result?"":resultData;
    }

    private void setValue2Context(String value, String result, IExecuteContext executeContext){
        //将值塞入到组件返回值中
        if (value.contains("#{") && value.contains("}")){
            String keyValue = ReUtil.get("(?<=\\#\\{)(.+?)(?=\\})", value, 1);
            // #{} 全局变量
            executeContext.setProjectGlobalVariable(keyValue, null==result?"":result);
        }
        if (value.contains("${") && value.contains("}")){
            String keyValue = ReUtil.get("(?<=\\$\\{)(.+?)(?=\\})", value, 1);
            // ${} 脚本变量
            executeContext.setVariable(keyValue, null==result?"":result);
        }
    }
}