package com.boman.web.core.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.TypeUtils;
import com.boman.common.core.utils.SecurityUtils;
import com.boman.common.core.utils.SpringUtils;
import com.boman.common.core.utils.StringUtils;
import com.boman.common.core.utils.array.ArrayUtils;
import com.boman.common.core.utils.collection.CollectionUtils;
import com.boman.common.core.utils.obj.ObjectUtils;
import com.boman.domain.GenTableColumn;
import com.boman.domain.constant.MysqlDataTypeConst;
import com.boman.domain.exception.UnknownColumnException;
import com.boman.web.core.domain.TableContext;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.web.client.RestTemplate;

import java.sql.Timestamp;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.boman.common.core.utils.obj.ObjectUtils.*;
import static com.boman.domain.constant.FormDataConstant.*;
import static com.boman.domain.constant.GenConstants.COLUMNTYPE_NUMBER;

/**
 * @author shiqian
 * @date 2021年04月13日 15:37
 **/
public class ColumnUtils {

    /**
     * 功能描述: 新增或者修改的时候，是否需要给修改人修改时间赋值
     *
     * @param columns     所有的列
     * @param jsonObject  需要返回的数据
     * @param currentTime currentTime
     * @param isSave      新增true、修改false
     */
    public static void packUpdateByAndTime(List<GenTableColumn> columns, JSONObject jsonObject, Timestamp currentTime, boolean isSave) {
        for (GenTableColumn column : columns) {
            String columnName = column.getColumnName();
            if (UPDATE_BY.equalsIgnoreCase(columnName)) {
                jsonObject.put(columnName, SecurityUtils.getUsername());
                continue;
            }

            if (UPDATE_TIME.equalsIgnoreCase(columnName)) {
                jsonObject.put(columnName, currentTime);
                continue;
            }

            if (isSave) {
                if (CREATE_TIME.equalsIgnoreCase(columnName)) {
                    jsonObject.put(columnName, currentTime);
                    continue;
                }
                if (CREATE_BY.equalsIgnoreCase(columnName)) {
                    jsonObject.put(columnName, SecurityUtils.getUsername());
                    continue;
                }
                if (DATETIME.equalsIgnoreCase(column.getColumnType())) {
                    Object value = jsonObject.getTimestamp(columnName);
                    if (isNotEmpty(value)) {
                        jsonObject.put(columnName, jsonObject.getTimestamp(columnName));
                    }
                }
            } else {
                // 修改时，修改非创建时间的其他时间类型
                if (DATETIME.equalsIgnoreCase(column.getColumnType())) {
                    if (!CREATE_TIME.equalsIgnoreCase(columnName) && isNotEmpty(jsonObject.get(columnName))) {
                        jsonObject.put(columnName, jsonObject.getTimestamp(columnName));
                    }
                }
            }
        }
    }

    /**
     * 功能描述: 把新增可见或者修改可见或者.....的列过滤出来
     *
     * @param allColumns 所有的列
     * @param maskIndex       那六个1的顺序 [1,1,1,1,1,1]
     *                   sort=0  =>  新增可见
     *                   sort=1  =>  新增可修改
     *                   sort=2  =>  修改可见
     *                   sort=3  =>  修改可修改
     *                   sort=4  =>  列表可见
     *                   sort=5  =>  列表可修改
     * @return List<GenTableColumn>
     */
    public static List<GenTableColumn> filterData(List<GenTableColumn> allColumns, int maskIndex, Predicate<String> predicate) {
        assert maskIndex < 6;
        List<GenTableColumn> returnData = Lists.newArrayListWithCapacity(16);
        for (GenTableColumn allColumn : allColumns) {
            if (HR.equals(allColumn.getHtmlType())) {
                continue;
            }

            if (checkMask(maskIndex, allColumn, predicate)) {
                returnData.add(allColumn);
            }
        }

        return returnData;
    }

    /**
     * 功能描述: 判断mask  == 0 返回true
     *
     * @param maskIndex mask的索引
     * @param column    column
     * @param predicate predicate 注意传的是1还是0
     * @return boolean
     */
    public static boolean checkMask(int maskIndex, GenTableColumn column, Predicate<String> predicate) {
        String mask = column.getMask();
        String columnName = column.getColumnName();
        String tableName = column.getTableName();
        String errorMsg = String.format("mask is empty, tableName = [ %s ], columnName = [ %s ]", tableName, columnName);
        String[] maskArray = requireNonNull(mask, errorMsg).split("");
        assert maskArray.length == 6;
        String maskMark = maskArray[maskIndex];
        return predicate.test(maskMark);
    }

    /**
     * 功能描述: 把新增可见或者修改可见或者.....的列名过滤出来
     *
     * @param allColumns 所有的列
     * @param maskIndex  那六个1的顺序 [1,1,1,1,1,1]
     *                   sort=0  =>  新增可见
     *                   sort=1  =>  新增可修改
     *                   sort=2  =>  修改可见
     *                   sort=3  =>  修改可修改
     *                   sort=4  =>  列表可见
     *                   sort=5  =>  列表可修改
     * @return JSONArray
     */
    public static JSONArray filterMaskColumnName(List<GenTableColumn> allColumns, int maskIndex, Predicate<String> predicate) {
        assert maskIndex < 6;
        JSONArray returnData = new JSONArray(16);
        for (GenTableColumn allColumn : allColumns) {
            if (HR.equals(allColumn.getHtmlType())) {
                continue;
            }

            if (checkMask(maskIndex, allColumn, predicate)) {
                returnData.add(allColumn.getColumnName());
            }
        }

        return returnData;
    }

    /**
     * 功能描述: 按照predicate的规则过滤allColumns中包含data并且符合过滤规则的数据, <p>过滤掉hr !!!</p>
     *
     * @param allColumns 所有的列
     * @param maskIndex  同{@link ColumnUtils#filterData(java.util.List, int, java.util.function.Predicate)}
     * @param data       从其中过滤符合规则的数据
     * @param predicate  predicate
     * @return JSONArray
     */
    public static JSONArray filterData(List<GenTableColumn> allColumns, int maskIndex, JSONArray data, Predicate<String> predicate) {
        assert maskIndex < 6;
        JSONArray returnData = new JSONArray();
        boolean dataIsEmpty = isEmpty(data);
        if (dataIsEmpty) {
            // data为空，拿出所有列中列表可见的
            returnData = filterMaskColumnName(allColumns, maskIndex, predicate);
        } else {
            // data不为空，拿出前端传过来的列中可见的
            for (GenTableColumn allColumn : allColumns) {
                String columnName = allColumn.getColumnName();
                if (HR.equals(allColumn.getHtmlType())) {
                    continue;
                }

                // 如果需要展示的列中包含此列，并且此列又满足 predicate 的条件， 则添加进去
                if (data.contains(columnName) && checkMask(maskIndex, allColumn, predicate)) {
                    returnData.add(columnName);
                }
            }
        }

        return returnData;
    }

    /**
     * 功能描述: 判断hr的mask，如果为0则则移除
     *
     * @param columns columns
     */
    public static void removeColumnsByMask(List<GenTableColumn> columns, int maskIndex, Predicate<String> predicate) {
        columns.removeIf(next -> checkMask(maskIndex, next, predicate));
    }

    public static String parseVariables(String variables) {
        return variables.replaceAll("\\$", "");
    }

    /**
     * 功能描述: 前台传过来的列是否都在表中
     *
     * @param target 前台传过来的列
     * @param origin 此表对应的所有的列
     */
    public static void checkColumn(Map<String, Object> target, List<GenTableColumn> origin) {
        if (MapUtils.isEmpty(target) || ObjectUtils.isEmpty(origin)) {
            return;
        }

        String tableName = origin.get(0).getTableName();
        List<String> all = map(origin, GenTableColumn::getColumnName);
        for (Map.Entry<String, Object> entry : target.entrySet()) {
            String columnName = entry.getKey();
            if (!all.contains(columnName.toLowerCase())) {
                throw new UnknownColumnException("此表 [" + tableName + "]中没有 [" + columnName + "] 字段");
            }
        }
    }

    /**
     * 功能描述: 前台传过来的列是否都在表中, List中的泛型必须为String
     *
     * @param target 前台传过来的列
     * @param origin 此表对应的所有的列
     */
    public static void checkColumn(List<Object> target, List<GenTableColumn> origin) {
        if (ObjectUtils.isEmpty(target) || ObjectUtils.isEmpty(origin)) {
            return;
        }

        String tableName = origin.get(0).getTableName();
        List<String> all = map(origin, GenTableColumn::getColumnName);
        for (Object obj : target) {
            String columnName = (String) obj;
            if (!CollectionUtils.containsIgnoreCase(all, columnName)) {
                throw new UnknownColumnException("此表 [" + tableName + "]中没有 [" + columnName + "] 字段");
            }
        }
    }

    /**
     * 功能描述: 处理必填项
     *
     * @param commitData 需要提交的数据
     * @param columns 所有的列
     */
    public static void handleRequired(JSONObject commitData, List<GenTableColumn> columns) {
        for (GenTableColumn column : columns) {
            if (GenTableColumn.IS_REQUIRED.equalsIgnoreCase(column.getIsRequired())) {
                Object value = commitData.get(column.getColumnName());
                if ((value instanceof String)) {
                    requireNonNull((String) value, column.getColumnComment() + " 为必填项");
                } else {
                    requireNonNull(value, column.getColumnComment() + " 为必填项");
                }
            }
        }
    }

    /**
     * 功能描述: 处理输入类型的不合法
     *
     * @param commitData 需要提交的数据
     * @param columns 所有的列
     */
    public static void handleInputType(JSONObject commitData, List<GenTableColumn> columns) {
        for (Map.Entry<String, Object> entry : commitData.entrySet()) {
            Object value = entry.getValue();
            for (GenTableColumn column : columns) {
                // 对必填项做校验, 非必填项不管他
                if (!GenTableColumn.IS_REQUIRED.equalsIgnoreCase(column.getIsRequired())) {
                    continue;
                }

                if (!entry.getKey().equals(column.getColumnName())) {
                    continue;
                }

                if (!ArrayUtils.arraysContains(COLUMNTYPE_NUMBER, getDbType(column.getColumnType()))) {
                    continue;
                }

                if ((value instanceof String) && !NumberUtils.isCreatable(((String) value))) {
                    throw new IllegalArgumentException(column.getColumnComment() + "只能输入数字");
                }
            }
        }
    }

    /**
     * 功能描述: 封装成查询条件 key: 列名，  value：查询条件_查询类别
     * eg: [{"config_name": ["系统配置", "EQ", "varchar(100)"]}]
     *
     * @param columns columns
     * @return com.alibaba.fastjson.JSONObject
     */
    public static JSONObject packColCondition(List<GenTableColumn> columns, JSONObject condition) {
        if (isEmpty(condition)) {
            return condition;
        }

        JSONObject packColCondition = new JSONObject(columns.size());
        for (Map.Entry<String, Object> entry : condition.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            for (GenTableColumn column : columns) {
                // long string collection 暂时只作此三种类型判断
                if (column.getColumnName().equalsIgnoreCase(key) && ObjectUtils.isNotEmpty(value)) {
                    // columnType 作为判断需不需要转义的一个标准，防止索引失效
                    packColCondition.put(key, Lists.newArrayList(value, column.getQueryType(), column.getColumnType()));
                    break;
                }
            }
        }

        return packColCondition;
    }

    /**
     * 功能描述: 数字小的在前面，数字大的在后面
     *
     * @param allColumns allColumns
     * @return java.util.List<com.boman.domain.GenTableColumn>
     */
    public static List<GenTableColumn> filterHrAndSort(List<GenTableColumn> allColumns) {
        return allColumns.stream().filter(col -> HR.equals(col.getHtmlType()))
                .sorted(Comparator.comparing(GenTableColumn::getSort))
                .collect(Collectors.toList());
    }


    public static List<GenTableColumn> withHr(List<GenTableColumn> columns) {
        return filter(columns, col -> HR.equals(col.getHtmlType()));
    }

    public static List<GenTableColumn> withoutHr(List<GenTableColumn> columns) {
        return filter(columns, col -> !HR.equals(col.getHtmlType()));
    }

    public static List<String> withoutHrReturnColumnNames(List<GenTableColumn> columns) {
        List<String> result = new ArrayList<>(requireNonNull(columns, "columns is null").size());
        for (GenTableColumn column : columns) {
            if (HR.equals(column.getHtmlType())) {
                continue;
            }

            result.add(column.getColumnName());
        }

        return result;
    }

    /**
     * varchar(20) => varchar
     *
     * @param columnType 列类型
     * @return 截取后的列类型
     */
    public static String getDbType(String columnType) {
        if (StringUtils.indexOf(columnType, "(") > 0) {
            return StringUtils.substringBefore(columnType, "(");
        } else {
            return columnType;
        }
    }

    /**
     * 连接字符串数组集合
     * eg: System.out.println(joinList(Lists.newArrayList(1, 3, 2, 5))); 输出 "'1', '3', '2', '5'"
     *
     * @param iterable 集合
     * @return 连接结果
     */
    public static <T> String joinList(Iterable<T> iterable) {
        Iterator<T> iterator = iterable.iterator();
        StringBuilder stringBuilder = new StringBuilder();
        while (iterator.hasNext()) {
            T next = iterator.next();
            String s = "'" + next + "', ";
            stringBuilder.append(s);
        }

        String result = stringBuilder.toString();
        return result.substring(0, result.length() - 2);
    }

    /**
     * 连接字符串数组集合
     * eg: System.out.println(joinList(Lists.newArrayList(1, 3, 2, 5))); 输出 1, 3, 2, 5
     *
     * @param iterable 集合
     * @return 连接结果
     */
    public static <T> String joinList1(Iterable<T> iterable) {
        Joiner joiner = Joiner.on(", ").skipNulls();
        return joiner.join(iterable);
    }

    public static String getColumnTypeByColumnName(List<GenTableColumn> columns, String columnName){
        String result = "";
        for (GenTableColumn column : columns) {
            if (column.getColumnName().equals(columnName)) {
                String columnType = column.getColumnType();
                result = getDbType(columnType);
                break;
            }
        }

        return result;
    }

    public static Object castNumberValue(List<GenTableColumn> columns, String columnName, Object value){
        String columnType = getColumnTypeByColumnName(columns, columnName);
        if (isEmpty(columnType)) {
            return value;
        }

        if (MysqlDataTypeConst.NUMBER.contains(columnType)) {
            return TypeUtils.castToLong(value);
        }

        return value;
    }

    /**
     * 处理字段翻译器
     */
    public static void fieldTranslator (TableContext context) {
        JSONObject commitData = context.getCommitData();
        List<GenTableColumn> genTableColumnList = context.getColumns();
        for (GenTableColumn genTableColumn : genTableColumnList) {
            String fieldTranslator = genTableColumn.getFieldTranslator();
            if (StringUtils.isNotBlank(fieldTranslator)){
                String url = "http://" + fieldTranslator;
                System.out.println("访问地址:"+url);
                commitData = SpringUtils.getBean(RestTemplate.class).postForObject(url, commitData, JSONObject.class);
            }
        }
        context.setCommitData(commitData);
    }
}