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

import cn.hutool.core.util.ReUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.element.collections.enums.TypeConversionRemoveEnum;
import net.northking.cctp.element.collections.enums.TypeConversionSortEnum;
import net.northking.cctp.element.collections.enums.TypeCountEnum;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.*;
import net.northking.cctp.element.core.type.DataType;
import net.northking.cctp.element.core.type.InputType;
import net.northking.cctp.element.core.type.ParamScope;

import java.util.*;

/**
 * 集合数据类型组件
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
@Keywords
public class Collections {

    /**
     * <p>返回一个值在指定List集合的元素中出现的次数</p>
     * Example:
     * <pre>
     * list = ["a", "b", "c"]
     * countValuesInList(list, a)   = 1
     * </pre>
     *
     * @param src   List集合
     * @param value 元素值
     * @return 值出现次数
     */
    @Keyword(alias = "统计元素在List中出现次数")
    @Return(name = "result", comment = "出现次数", type = DataType.INTEGER)
    public int countValuesInList(
            @Argument(name = "list", comment = "List集合", type = DataType.LIST) List src,
            @Argument(name = "value", comment = "元素值", type = DataType.OBJECT) Object value,
            @Argument(name = "type", comment = "集合元素类型", type = DataType.ENUM, enumValues = TypeCountEnum.class, inputType = InputType.select) String type,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext executeContext) {
        TypeCountEnum typeConversionEnum = TypeCountEnum.from(type);
        int count = 0;
        if (typeConversionEnum==TypeCountEnum.Digit){
            double v = Double.parseDouble(value.toString());
            for (Object e : src) {
                if (e instanceof String) continue;
                double dto = Double.parseDouble(e.toString());
                if (Double.doubleToLongBits(dto) == Double.doubleToLongBits(v)) {
                    count++;
                }
            }
        }else {
            for (Object e : src) {
                if (e.equals(value)) {
                    count++;
                }
            }
        }
        save2ContentText(result, executeContext, count);
        return count;
    }

    /**
     * <p>返回指定Map集合的所有键名称</p>
     * Example:
     * <pre>
     * map = {"a":1,"b":2,"c":3}
     * getMapKeys(map)   = ["a", "b", "c"]
     * </pre>
     *
     * @param map Map集合
     * @return 所有键的名称
     */
    @Keyword(alias = "获取Map的所有键名")
    @Return(name = "result", comment = "所有键的名称", type = DataType.LIST)
    public Object getMapKeys(
            @Argument(name = "map", comment = "Map集合", type = DataType.MAP) Map map,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        List keys = new ArrayList();
        for (Iterator<Map.Entry> iterator = map.entrySet().iterator(); iterator.hasNext(); ) {
            keys.add(iterator.next().getKey());
        }
        Object data = toJsonString(keys);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>返回指定Map集合的所有键对应的值</p>
     * Example:
     * <pre>
     * map = {"a":1,"b":2,"c":3}
     * getMapValues(map)   = [1, 2, 3]
     * </pre>
     *
     * @param map Map集合
     * @return 所有键对应的值
     */
    @Keyword(alias = "获取Map所有键对应的值")
    @Return(name = "result", comment = "所有键对应的值", type = DataType.LIST)
    public Object getMapValues(
            @Argument(name = "map", comment = "Map集合", type = DataType.MAP) Map map,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        List values = new ArrayList();
        for (Iterator<Map.Entry> iterator = map.entrySet().iterator(); iterator.hasNext(); ) {
            values.add(iterator.next().getValue());
        }
        Object data = toJsonString(values);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>返回给定Map集合中键名对应的值, 如果键名称不存在则组件运行失败</p>
     * Example:
     * <pre>
     * map = {"a":1,"b":2,"c":3}
     * getFromMap(map, "a")   = 1
     * </pre>
     *
     * @param map Map集合
     * @param key Map键名
     * @return 键对应的值
     */
    @Keyword(alias = "获取Map中键对应的值")
    @Return(name = "result", comment = "键对应的值", type = DataType.OBJECT)
    public Object getFromMap(
            @Argument(name = "map", comment = "Map集合", type = DataType.MAP) Map map,
            @Argument(name = "key", comment = "Map键名", type = DataType.OBJECT) Object key,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        if (!map.containsKey(key)) {
            throw new IllegalArgumentException(String.format("不存在如此的键名称:%s", key));
        }
        Object data = toJsonString(map.get(key));
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>返回List集合对应下标元素的值</p>
     * 索引下标0表示第一个元素, 1表示第二个元素, 以此类推; 如果下标值超过List集合元素个数, 组件运行失败<br>
     * Example:
     * <pre>
     * list = ["a", "b"]
     * getFromList(list, 0)   = "a"
     * getFromList(list, 1)   = "b"
     * </pre>
     *
     * @param list  List集合
     * @param index 索引下标
     * @return 元素值
     */
    @Keyword(alias = "获取List中下标元素")
    @Return(name = "result", comment = "元素值", type = DataType.OBJECT)
    public Object getFromList(
            @Argument(name = "list", comment = "List集合", type = DataType.LIST) List list,
            @Argument(name = "index", comment = "索引下标", type = DataType.INTEGER) int index,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        Object data = toJsonString(list.get(index));
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>在List集合的指定索引下标位置插入元素</p>
     * 索引下标0表示第一个元素, 1表示第二个元素, 以此类推<br>
     * Example:
     * <pre>
     * list = ["a"]
     * insertIntoList(list, 0, "xxx") = ["xxx", "a"]
     * </pre>
     *
     * @param list  List集合
     * @param index 索引下标
     * @param value 元素值
     * @return List集合
     */
    @Keyword(alias = "在List下标位置插入元素")
    @Return(name = "result", comment = "List集合", type = DataType.LIST)
    public Object insertIntoList(
            @Argument(name = "list", comment = "List集合", type = DataType.LIST) List list,
            @Argument(name = "index", comment = "索引下标", type = DataType.INTEGER) int index,
            @Argument(name = "value", comment = "元素值", type = DataType.OBJECT) Object value,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext executeContext) {
        list.add(index, value);
        Object data = toJsonString(list);
        save2ContentText(result, executeContext, data);
        return data;
    }

    /**
     * <p>移除并返回List集合指定索引下标位置元素的值</p>
     * 索引下标0表示第一个元素, 1表示第二个元素, 以此类推<br>
     * Example:
     * <pre>
     * list = ["aa", "bb", "cc"]
     * list1 = ["aa", "bb"]
     * removeFromList("0",list,1,null)  = ["aa", "cc"]
     * removeFromList("1",list,0,list1) = ["cc"]
     * </pre>
     *
     * @param type  类型("0"-根据下标进行删除。"1"-根据元素值进行删除)
     * @param string  List集合
     * @param index 索引下标
     * @param values 元素值
     * @return 移除元素
     */
    @Keyword(alias = "删除List下标元素")
    @Return(name = "result", comment = "List集合", type = DataType.LIST)
    public Object removeFromList(
            @Argument(name = "type", comment = "类型", type = DataType.ENUM, enumValues = TypeConversionRemoveEnum.class, inputType = InputType.select) String type,
            @Argument(name = "list", comment = "List集合", type = DataType.STRING) String string,
            @Argument(name = "index", comment = "索引下标", type = DataType.INTEGER) int index,
            @Argument(name = "values", comment = "元素值", type = DataType.STRING) String values,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        List<String> list = JSON.parseArray(string, String.class);
        TypeConversionRemoveEnum typeConversionEnum = TypeConversionRemoveEnum.from(type);
        switch (typeConversionEnum) {
            case removeByIndex:
                list.remove(index);
                break;
            case removeByValue:
                List<String> valueList = JSON.parseArray(values, String.class);
                list.removeAll(valueList);
                break;
        }
        Object data = toJsonString(list);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>移除Map集合中给定的键名称及对应的值</p>
     * Example:
     * <pre>
     * map={"a": 1, "b": 2, "c": 3}
     * removeFromMap(map, ["b"])  = {"a": 1, "c": 3}
     * </pre>
     *
     * @param map  Map集合
     * @param keys 键名称
     * @return Map集合
     */
    @Keyword(alias = "删除Map中指定的键值对")
    @Return(name = "result", comment = "Map集合", type = DataType.MAP)
    public Object removeFromMap(
            @Argument(name = "map", comment = "Map集合", type = DataType.MAP) Map map,
            @Argument(name = "keys", comment = "键名称", type = DataType.JSON) JSONArray keys,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        for (Object key : keys) {
            map.remove(key);
        }
        Object data = toJsonString(map);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>设置List集合索引下标位置的元素值</p>
     * 索引下标0表示第一个元素, 1表示第二个元素, 以此类推<br>
     * Example:
     * <pre>
     * list=["a"]
     * setListValue(list, 0, "xxx")  = ["xxx"]
     * </pre>
     *
     * @param list  List集合
     * @param index 索引下标
     * @param value 元素值
     * @return List集合
     */
    @Keyword(alias = "设置List下标元素值")
    @Return(name = "result", comment = "List集合", type = DataType.LIST)
    public Object setListValue(
            @Argument(name = "list", comment = "List集合", type = DataType.LIST) List list,
            @Argument(name = "index", comment = "索引下标", type = DataType.INTEGER) int index,
            @Argument(name = "value", comment = "元素值", type = DataType.OBJECT) Object value,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        list.set(index, value);
        Object data = toJsonString(list);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>Map集合中添加键值对</p>
     * Example:
     * <pre>
     * map={"first":1}
     * setToMap(map, {"key":"value","second":2})  = {"first":1, "key":"value","second":2}
     * </pre>
     *
     * @param map Map集合
     * @param kvs 键值对
     * @return Map集合
     */
    @Keyword(alias = "Map中添加键值对")
    @Return(name = "result", comment = "Map集合", type = DataType.MAP)
    public Object setToMap(
            @Argument(name = "map", comment = "Map集合", type = DataType.MAP) Map map,
            @Argument(name = "kvs", comment = "键值对", type = DataType.MAP) JSONObject kvs,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        map.putAll(kvs);
        Object data = toJsonString(map);
        save2ContentText(result, context, data);
        return data;
    }

    /**
     * <p>排序List集合中的元素</p>
     * Example:
     * <pre>
     * list = ["a","b"]
     * sortList(list,"0")  = ["a", "b"]
     * sortList(list,"1")  = ["b", "a"]
     * </pre>
     *
     * @param list List集合
     * @param type 类型("0"-升序，"1"-降序)
     * @return List集合
     */
    @Keyword(alias = "List元素排序")
    @Return(name = "result", comment = "List集合", type = DataType.LIST)
    public Object sortList(
            @Argument(name = "list", comment = "List集合", type = DataType.LIST) List list,
            @Argument(name = "type", comment = "类型", type = DataType.ENUM, enumValues = TypeConversionSortEnum.class, inputType = InputType.select) String type,
            @Argument(name = "result", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String result,
            IExecuteContext context) {
        TypeConversionSortEnum typeConversionEnum = TypeConversionSortEnum.from(type);
        switch (typeConversionEnum) {
            case OrderByAsc:
                java.util.Collections.sort(list);
                break;
            case OrderByDesc:
                java.util.Collections.sort(list,java.util.Collections.reverseOrder());
                break;
        }
        Object data = toJsonString(list);
        save2ContentText(result, context, data);
        return data;
    }


    // ##########################################################################################
    // Internal Methods
    // ##########################################################################################

    private static Object toJsonString(Object value) {
        if (value instanceof String) {
            return value;
        }
        return JSON.toJSONString(value);
    }

    public static void save2ContentText(String value, IExecuteContext executeContext, Object s) {
        //将值塞入到组件返回值中
        if (value.contains("#{") && value.contains("}")) {
            String keyValue = ReUtil.get("(?<=\\#\\{)(.+?)(?=\\})", value, 1);
            // #{} 全局变量
            executeContext.setProjectGlobalVariable(keyValue, s.toString());
        }
        if (value.contains("${") && value.contains("}")) {
            String keyValue = ReUtil.get("(?<=\\$\\{)(.+?)(?=\\})", value, 1);
            // ${} 全局变量
            executeContext.setVariable(keyValue, s.toString());
        }
    }

}