package com.boman.web.core.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.boman.common.core.utils.JSONArrayUtils;
import com.boman.domain.constant.CacheConstants;
import com.boman.common.core.utils.SecurityUtils;
import com.boman.common.core.utils.StringUtils;
import com.boman.common.core.utils.collection.CollectionUtils;
import com.boman.common.core.utils.obj.ObjectUtils;
import com.boman.domain.dto.AjaxResult;
import com.boman.common.redis.RedisKey;
import com.boman.common.redis.service.RedisService;
import com.boman.common.security.service.TokenService;
import com.boman.domain.GenTable;
import com.boman.domain.GenTableColumn;
import com.boman.domain.SysDictData;
import com.boman.domain.constant.*;
import com.boman.domain.dto.RoleMenuDto;
import com.boman.domain.exception.NoSuchFunctionException;
import com.boman.gen.api.RemoteGenTableColumnService;
import com.boman.gen.api.RemoteGenTableService;
import com.boman.jflow.api.RemoteJflowTaskService;
import com.boman.system.api.RemoteMenuService;
import com.boman.domain.SysMenu;
import com.boman.system.api.model.LoginUser;
import com.boman.web.core.domain.ActionType;
import com.boman.domain.dto.FormDataDto;
import com.boman.web.core.domain.RowResult;
import com.boman.web.core.domain.TableContext;
import com.boman.web.core.service.delete.IBaseDeleteService;
import com.boman.web.core.service.save.IBaseSaveService;
import com.boman.web.core.service.select.IBaseSelectService;
import com.boman.web.core.service.submit.IBaseSubmitService;
import com.boman.web.core.service.update.IBaseUpdateService;
import com.boman.web.core.utils.BillRuleUtils;
import com.boman.web.core.utils.ColumnUtils;
import com.boman.web.core.utils.IdUtils;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

import static com.boman.domain.constant.GenConstants.HTML_FILE_UPLOAD;
import static com.boman.domain.constant.GenConstants.HTML_IMAGE_UPLOAD;
import static com.boman.common.core.utils.obj.ObjectUtils.*;
import static com.boman.domain.constant.FormDataConstant.*;
import static com.boman.domain.constant.MaskConstant.*;
import static com.boman.web.core.utils.AuthUtils.*;
import static com.boman.web.core.utils.ColumnUtils.*;
import static com.boman.web.core.utils.HandlerFormDataUtils.*;
import static com.boman.web.core.utils.JSONObjectUtils.containsKeyIgnoreCase;

/**
 * @author shiqian
 * @description
 * @date 2021年03月22日 09:51
 **/
@Component
public class TableServiceCmdService {

    @Resource
    private IBaseDeleteService deleteService;
    @Resource
    private IBaseSaveService saveService;
    @Resource
    private RedisService redisService;
    @Resource
    private IBaseSelectService selectService;
    @Resource
    private IBaseSubmitService submitService;
    @Resource
    private IBaseUpdateService updateService;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private TableContext tableContext;
    @Resource
    private RemoteGenTableService remoteGenTableService;
    @Resource
    private RemoteGenTableColumnService remoteGenTableColumnService;
    @Resource
    private RemoteMenuService remoteMenuService;
    @Resource
    private RemoteJflowTaskService remoteJflowTaskService;

    private static final Logger LOGGER = LoggerFactory.getLogger(TableServiceCmdService.class);

    public final AjaxResult objectSave(FormDataDto dto) {
        // 权限校验在此方法中
        TableContext context = tableContext.createContext(dto);
        // 拿到pkName和maxId
        String pkName = context.getPkName();
        JSONObject commitData = context.getCommitData();

        checkColumn(commitData, context.getColumns());
        // 必填项
        handleRequired(commitData, context.getColumns());
        // 处理输入类型
        handleInputType(commitData, context.getColumns());
        //判断是否有字段翻译器,进行设置
        fieldTranslator(context);

        //判断时候有定制新增接口
        List<JSONObject> listData = new ArrayList<>();
        listData.add(context.getCommitData());
        isCustomized(context.getTableName(),listData,"trigger_create");

        AjaxResult result;
        // 新增
        if (ActionType.INSERT.equals(context.getActionType())) {
            List<GenTableColumn> columns = context.getColumns();
            Long maxId = IdUtils.getMaxId(dto.getTable(), pkName);
            ColumnUtils.packUpdateByAndTime(columns, commitData, new Timestamp(System.currentTimeMillis()), true);
            // 处理默认值
            handlerDefaultValue(commitData, columns);
            // 如果有单据、按照单据编号规则
            buildBillRule(context.getTableName(), commitData, columns);

            commitData.put(context.getPkName(), maxId);
            RowResult rowResult = saveService.insertRow(context.getTableName(), commitData);
            if (RowResult.checkSuccess(rowResult)) {
                LOGGER.info("保存成功，封装到数据库的数据为: {}", JSON.toJSONString(rowResult.getData()));
                result = AjaxResult.success(maxId);
            } else {
                LOGGER.error("保存失败，保持的原始数据为: {}", JSON.toJSONString(dto));
                result = AjaxResult.error();
            }
        } else {
            // 编辑
            int effective = updateService.updateByIdList(context, Collections.singletonList(context.getId()));
            if (effective > 0) {
                LOGGER.info("修改成功，封装到数据库的数据为: {}", JSON.toJSONString(commitData));
                result = AjaxResult.success(context.getId());
            } else {
                LOGGER.error("修改失败，前台传来的原始数据为: {}", JSON.toJSONString(commitData));
                result = AjaxResult.error();
            }
        }

        return result;

    }

    /**
     * 功能描述: 通用删除接口 (真的删除)
     *
     * @param dto 前台传过来的dto
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult objectDelete(FormDataDto dto) {
        requireNonNull(dto.getTable(), "tableName = [" + dto.getTable() + "] 此表不存在");

        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        String pkName = IdUtils.getPkName(genTable.getColumns());

        Long[] idArr = CollectionUtils.listToArray(dto.getIdList());
        requireNonNull(idArr, "删除时，所传idList为空");

        RowResult rowResult;
        List<RowResult> result = Lists.newArrayListWithCapacity(idArr.length);
        for (Long id : idArr) {
            // 校验权限
            if (BooleanUtils.isTrue(checkAuthObjectDelete(genTable, id, pkName))) {
                rowResult = deleteService.deleteById(dto.getTable(), pkName, id);
                if (rowResult.isOk()) {
                    LOGGER.info("删除成功, tableName: {}, id: {}, 操作人: {}", tableContext.getTableName(), id, getLoginUser().getUsername());
                    continue;
                }
                LOGGER.info("删除失败, tableName: {}, id: {}, 操作人: {}", tableContext.getTableName(), id, getLoginUser().getUsername());
            } else {
                rowResult = RowResult.create(RowResult.FAIL, "无操作权限");
            }

            result.add(rowResult);
        }

        return AjaxResult.success(result);
    }

    /**
     * 功能描述: 通用删除接口 (真的删除)
     *
     * @param dto 前台传过来的dto
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult objectLogicDelete(FormDataDto dto) {
        requireNonNull(dto.getTable(), "tableName = [" + dto.getTable() + "] 此表不存在");
        Long[] idArr = CollectionUtils.listToArray(dto.getIdList());
        requireNonNull(idArr, "objectLogicDelete idArr is empty");

        // 拿到pkName
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        String pkName = IdUtils.getPkName(genTable.getColumns());

        List<RowResult> result = Lists.newArrayListWithCapacity(idArr.length);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(dto.getLogicDelName(), dto.getLogicDelValue());

        RowResult rowResult;
        for (Long id : idArr) {
            // 校验权限
            if (BooleanUtils.isTrue(checkAuthObjectDelete(genTable, id, pkName))) {
                rowResult = deleteService.objectLogicDelete(new Long[]{id}, dto.getTable(), pkName, jsonObject);
                if (rowResult.isOk()) {
                    LOGGER.info("逻辑删除成功, tableName: {}, id: {}, 操作人: {}", tableContext.getTableName(), id, getLoginUser().getUsername());
                    continue;
                }
                LOGGER.info("逻辑删除失败, tableName: {}, id: {}, 操作人: {}", tableContext.getTableName(), id, getLoginUser().getUsername());
            } else {
                rowResult = RowResult.create(RowResult.FAIL, "无操作权限");
            }

            result.add(rowResult);
        }

        return AjaxResult.success(result);
    }

    /**
     * 功能描述: 获取单表单数据
     *
     * @param dto condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult queryList(FormDataDto dto) {
        requireNonNull(dto.getTable(), "tableName = [" + dto.getTable() + "] 此表不存在");
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        String tableName = genTable.getTableName();
        JSONObject fixedData = ifNullSetEmpty(dto.getFixedData());
        List<GenTableColumn> columns = genTable.getColumns();

        // 查询条件
        JSONObject condition = ifNullSetEmpty(fixedData.getJSONObject(CONDITION));
        // 权限
        checkAuthQueryList(genTable, condition);
        // 检查列
        checkColumn(condition, columns);
        // 封装好以后的查询条件
        JSONObject packCondition = ifNullSetEmpty(selectService.packColCondition(columns, condition));
        JSONArray showData = fixedData.getJSONArray(SHOW_DATA);
        // 检查列
        checkColumn(showData, genTable.getColumns());
        // 需要返回到前台的列, 需要判断是否是列表展示, 4为判断列表是否可见
        showData = filterData(columns, 4, showData, MaskConstant.LIST_VISIBLE::equals);
        // 表的id单独处理，不管mask，都查出来返给前台
        IdUtils.putIfNotContains(showData, IdUtils.getPkName(columns));

        JSONObject rows = new JSONObject();
        int total = selectService.countByCondition(genTable.getTableName(), condition, packCondition);
        rows.put(FormDataConstant.PAGE_TOTAL, total);
        rows.put(TABLE_HEAD_LIST, getTableHeadList(genTable));
        if (total <= 0) {
            rows.put(FormDataConstant.PAGE_ROWS, Collections.EMPTY_LIST);
            return AjaxResult.success(rows);
        }

        List<JSONObject> result = selectService.selectByCondition(tableName, condition, packCondition, JSONArrayUtils.toList(showData), dto);
        result = filter(result, ObjectUtils::isNotEmpty);

        // 查询时为null的列不显示的处理
        handleNullColumnValue(result, showData);

        // 处理blob
        handleBlob(result, genTable.getIsContainsBlob());
        // 处理日期、外键、字典值
        handleDictForQueryList(result, columns);
        handleDateTimeForQueryList(result, columns);
        handleFkForQueryList(result, columns);

        // 定制接口
        result = isCustomized(dto.getTable(), result, "trigger_retrieve");

        rows.put(PAGE_ROWS, result);
        return AjaxResult.success(rows);
    }

    /**
     * 功能描述: 获取单表单数据
     *
     * @param dto condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult getObject(FormDataDto dto) {
        String tableName = requireNonNull(dto.getTable(), "tableName = [" + dto.getTable() + "] 此表不存在");
        Boolean isUi = requireNonNull(dto.getIsUi(), "未传isUi这个参数");
        Long id = dto.getFixedData().getLong(FormDataConstant.ID);
        requireNonNull(id, "如果是回显，则传参为此行记录的id， 如为新增，则传-1，传null可不行");

        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, tableName);
        List<GenTableColumn> columns = genTable.getColumns();

        if (BooleanUtils.isFalse(checkAuthGetObject(genTable, id))) {
            throw new NoSuchFunctionException("不好意思，您无权限操作");
        }

        // id = -1时，查询该表单对应的字段名称
        if (ltZero(id)) {
            return getByTableName(genTable, isUi);
        }

        // extend的字段标记为true
        markTrueByExtend(columns);
        List<GenTableColumn> updateVisibleColumns = filterData(columns, 2, UPDATE_VISIBLE::equals);
        List<String> showData = map(updateVisibleColumns, GenTableColumn::getColumnName);
        String pkName = IdUtils.getPkName(genTable.getColumns());

        List<JSONObject> jsonList = selectService.selectByIdList(tableName, pkName, Lists.newArrayList(id), showData);
        requireNonNull(jsonList, "id 为[" + id + "]的数据不存在, 模块为[" + genTable.getFunctionName() + "]");
        // 查询时为null的列不显示的处理
        handleNullColumnValue(jsonList, showData);
        JSONObject json = jsonList.get(0);

        // 处理blob
        handleBlob(jsonList, genTable.getIsContainsBlob());
        List<GenTableColumn> parentColumns = filterHrAndSort(columns);
        removeColumnsByMask(parentColumns, 2, UPDATE_NOT_VISIBLE::equals);

        //接收可能存在的cssClass
        String cssClass = null;
        // 处理成hr的形式
        for (GenTableColumn hrColumn : parentColumns) {
            List<GenTableColumn> children = Lists.newArrayListWithCapacity(16);
            for (GenTableColumn column : updateVisibleColumns) {
                if (hrColumn.getId().equals(column.getHrParentId())) {
                    String columnName = column.getColumnName();
                    String columnType = column.getColumnType();
                    String htmlType = column.getHtmlType();
                    String dictType = column.getDictType();
                    if (containsKeyIgnoreCase(json, columnName)) {
                        column.setColumnValue(json.get(columnName));
                        // sysDict
                        if (isNotEmpty(dictType)) {
                            String value = json.getString(columnName);
                            List<SysDictData> sysDictData = column.getSysDictData();
                            if (sysDictData != null && sysDictData.size() > 0) {
                                for (SysDictData sysDictDatum : sysDictData) {
                                    if (sysDictDatum.getDictValue().equals(value)) {
                                        cssClass = sysDictDatum.getCssClass();
                                        break;
                                    }
                                }
                            }
                        }
                        // dateTime
                        if (DATE.equals(columnType)) {
                            column.setColumnValue(getStrDate(json.getTimestamp(columnName)));
                        } else if (DATETIME.equals(columnType)) {
                            column.setColumnValue(getStrTimeStamp(json.getTimestamp(columnName)));
                        }

                        // fk
                        if (isNotEmpty(column.getForeignKey())) {
                            Object value = json.get(columnName);
                            column.setFkInfo(getFkInfoForGetObject(column.getFkInfo(), value));
                            // 转换类型
                            column.setColumnValue(castNumberValue(columns, columnName, value));
                        }
                        // annex
                        if (HTML_IMAGE_UPLOAD.equalsIgnoreCase(htmlType) || HTML_FILE_UPLOAD.equalsIgnoreCase(htmlType)) {
                            column.setAnnex(getAnnex(json.getString(columnName)));
                        }
                        //column.setReadonly(SubmitConstant.STATUS.equals(columnName));
                    }

                    children.add(column);
                }
            }
            hrColumn.setHrChildren(children);
        }

        //给基本属性和日志信息添加上cssClass
        if (StringUtils.isNotBlank(cssClass)) {
            for (GenTableColumn parentColumn : parentColumns) {
                parentColumn.setCssClass(cssClass);
            }
        }
        JSONObject result = new JSONObject();
        result.put(SHOW_DATA, removeHr(parentColumns));
        result.put(BUTTON_LIST, getButton(tableName, json.getString(SubmitConstant.STATUS)));
        Integer tableColumn = genTable.getTableColumn();
        if (tableColumn != null) {
            result.put(TABLE_COLUMN, tableColumn);
        }
        return AjaxResult.success(result);
    }


    /**
     * 功能描述: 获取单表单所有数据
     *
     * @param dto condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult getObjectAllColumns(FormDataDto dto) {

        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());

        JSONObject jsonObject = selectService.selectById(dto.getTable(), IdUtils.getPkName(genTable.getColumns()), dto.getObjId(), dto.getOther());
        List<GenTableColumn> columns = genTable.getColumns();
        JSONArray addcolums = new JSONArray();
        this.addOtherColumn(columns, dto.getOther());
        for (GenTableColumn column : columns) {
            JSONObject jo = new JSONObject();
            jo.put("col", column.getId());
            jo.put("colid", column.getId());
            jo.put("colname", column.getColumnName());
            jo.put("datelimit", "all");
            jo.put("display", "text");
            if (column.getIsRequired() != null && Integer.parseInt(column.getIsRequired()) == 1) {
                jo.put("isnotnull", true);
            }
            jo.put("name", column.getColumnComment());
            jo.put("readonly", false);
            jo.put("type", "STRING");
//            jo.put("valuedata", jsonObject.get(column.getColumnName()));
            // 处理字典值
            this.genDictValue(column, jsonObject, jo, dto.getOther());
            addcolums.add(jo);
        }
        JSONObject result = new JSONObject();
        result.put("childs", addcolums);
        result.put("size",columns.size());

        JSONArray adc = new JSONArray();
        adc.add(result);
        JSONObject jo = new JSONObject();
        jo.put("addcolums", adc);
        return AjaxResult.successZero(jo);

    }

    /**
     * 判断hr字段里面是否有值
     *
     * @param parentColumns
     * @return
     */
    private List<GenTableColumn> removeHr(List<GenTableColumn> parentColumns) {
        Iterator<GenTableColumn> iterator = parentColumns.iterator();
        while (iterator.hasNext()) {
            GenTableColumn next = iterator.next();
            List<GenTableColumn> hrChildren = next.getHrChildren();
            if (hrChildren.size() == 0) {
                iterator.remove();
            }
        }
        return parentColumns;
    }

    /**
     * 功能描述: 获取表单查询字段、按钮、表头
     * 注意: 都是从redis中拿的，如果数据库和redis不一致，则需刷新一下redis
     *
     * <p>
     * eg:{
     * "table": "sys_config",
     * }
     *
     * @param condition condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult getTableQuery(FormDataDto condition) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, condition.getTable());
        List<GenTableColumn> columns = genTable.getColumns();
        JSONObject jsonObject = new JSONObject();

        // 查询字段
        jsonObject.put(FormDataConstant.QUERY_LIST, getQueryColumns(columns));
        // BUTTON_LIST
        List<String> btns = buildBtnList(genTable.getTableName());
        jsonObject.put(FormDataConstant.BUTTON_LIST, Strings.nullToEmpty(CollectionUtils.listToString(btns)));
        jsonObject.put(ViewTypeConst.VIEW_TYPE, Strings.nullToEmpty(genTable.getTplCategory()));
        jsonObject.put(RULES, packRequireColumn(columns));
        return AjaxResult.success(jsonObject);
    }

    /**
     * 功能描述:  查找表作为查询字段的列，如果有字典值并把字典值赋值
     *
     * @param allColumns allColumns
     * @return java.util.List<com.boman.domain.GenTableColumn>
     */
    private List<GenTableColumn> getQueryColumns(List<GenTableColumn> allColumns) {
        List<GenTableColumn> queryList = Lists.newArrayListWithCapacity(16);
        for (GenTableColumn column : allColumns) {
            if (GenTableColumn.IS_QUERY.equalsIgnoreCase(column.getIsQuery())) {
                String dictType = column.getDictType();
                if (ObjectUtils.isNotEmpty(dictType)) {
                    column.setSysDictData(listSysDictDataByType(dictType));
                }

                queryList.add(column);
            }
        }
        return queryList;
    }

    /**
     * 功能描述: 查找当前登陆人操作的表对应的btn
     *
     * @param tableName tableName
     * @return java.util.List<java.lang.String>
     */
    private List<String> buildBtnList(String tableName) {
        List<SysMenu> menus = remoteMenuService.listBtnByUserId(SecurityUtils.getUserId());
        if (isEmpty(menus)) {
            return Collections.emptyList();
        }

        List<String> permses = map(menus, SysMenu::getPerms);
        List<String> btns = Lists.newArrayListWithCapacity(permses.size());
        for (String perms : permses) {
            String[] split = perms.split(":");
            if (split[0].equals(tableName)) {
                btns.add(RoleMenuDto.getBtnFromPerms(perms));
            }
        }
        // 删除Q
        btns.remove(GlobalBtn.Q);
        return btns;
    }

    /**
     * 功能描述: 获取表单查询字段
     * 注意: 都是从redis中拿的，如果数据库和redis不一致，则需刷新一下redis
     *
     * <p>
     * eg:{
     * "table": "sys_config"
     * }
     *
     * @param condition condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult getQueryParam(FormDataDto condition) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, condition.getTable());
        List<GenTableColumn> columns = genTable.getColumns();
        // 查询字段
        List<GenTableColumn> queryList = getQueryColumns(columns);

        return AjaxResult.success(queryList);
    }

    /**
     * 功能描述:  单独获取按钮
     * 注意: 都是从redis中拿的，如果数据库和redis不一致，则需刷新一下redis
     *
     * @param tableName tableName
     * @return com.boman.domain.dto.AjaxResult
     */
//    public String getButton(String tableName) {
//        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, tableName);
//        List<String> btns = buildBtnList(genTable.getTableName());
//        return Strings.nullToEmpty(CollectionUtils.listToString(btns));
//    }


    /**
     * 功能描述:  单独获取按钮
     * 注意: 都是从redis中拿的，如果数据库和redis不一致，则需刷新一下redis
     *
     * @param tableName tableName
     * @param status    数据的审核状态
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public String getButton(String tableName, String status) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, tableName);
        List<String> btns = buildBtnList(genTable.getTableName());
        if (isEmpty(status)) {
            return Strings.nullToEmpty(CollectionUtils.listToString(btns));
        }

        // 非未提交状态
        if (!SubmitConstant.NOT_SUBMIT.equals(status)) {
            btns.remove(GlobalBtn.S);
            btns.remove(GlobalBtn.D);
            btns.remove(GlobalBtn.A);
            btns.remove(GlobalBtn.U);
            btns.remove(GlobalBtn.M);
        }

        return Strings.nullToEmpty(CollectionUtils.listToString(btns));
    }


    /**
     * 根据表对象获取要显示的表头信息
     *
     * @param genTable
     * @return
     */
    public List<GenTableColumn> getTableHeadList(GenTable genTable) {
        List<GenTableColumn> columns = genTable.getColumns();
        // 表头
        return filterData(columns, 4, MaskConstant.LIST_VISIBLE::equals);
    }

    /**
     * 功能描述: 获取表单子表
     *
     * @param condition condition
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult objectTab(FormDataDto condition) {
        //获取到主表对象
        GenTable genTable = getTableFromRedisByTableName(RedisKey.RELATION, condition.getTable());
        //从主表对象的relationList中获取出所有子表表对象
        List<GenTable> childTableList = genTable.getRelationList();
        // 表头
        List<GenTableColumn> tableHeadList = getTableHeadList(genTable);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(FormDataConstant.REF, childTableList);
        jsonObject.put(FormDataConstant.TABLE_HEAD_LIST, tableHeadList);
        return AjaxResult.success(jsonObject);
    }

    /**
     * 功能描述: 表单提交接口
     *
     * @param condition condition
     * @param isSubmit  提交true， 反提交false
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult objectSubmit(FormDataDto condition, boolean isSubmit) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, condition.getTable());
        List<JSONObject> commitData = condition.getCommitData();
        requireNonNull(commitData, "啥都不提交, 调什么接口？？");

        String tableName = requireNonNull(genTable.getTableName(), "tableName = [" + genTable.getTableName() + "] 此表不存在");
        String pkName = IdUtils.getPkName(genTable.getColumns());

        // 先去查出之前的状态
        List<Long> idList = map(commitData, jsonObject -> jsonObject.getLong(FormDataConstant.ID));

//        for (Long id : idList) {
//            ProcessDto dto = new ProcessDto();
//            dto.setDbname(condition.getTable());
//            dto.setBusinessCode(id);
//            JSONObject process = remoteJflowService.createProcess(dto);
//
//        }


        List<JSONObject> beforeList = selectService.selectByIdList(tableName, pkName, idList, Lists.newArrayList(pkName, STATUS));
        requireNonNull(beforeList, "beforeList is empty");

        for (JSONObject commitDatum : commitData) {
            String dbStatus = getStatusFromFormData(commitDatum, beforeList);
            // 只能是未提交的状态下，才能调用提交
            if(StringUtils.isNotEmpty(condition.getSubmitSource())) {
                handlerSubmit(tableName, pkName, commitDatum);
            }else {
                if (isSubmit) {
                    // 只能是未提交的状态下，才能调用提交, 下面是不允许
                    if (!SubmitConstant.NOT_SUBMIT.equals(dbStatus)) {
                        commitDatum.put(SubmitConstant.SUBMIT_RESULT, SubmitConstant.NOT_ALLOWED);
                    } else {
                        commitDatum.put(SubmitConstant.STATUS, SubmitConstant.CHECKING);
                        handlerSubmit(tableName, pkName, commitDatum);
                    }
                } else {
                    // 只有在已提交的情况下才能反提交, 下面是不允许
                    if (!SubmitConstant.SUBMITTED.equals(dbStatus)) {
                        commitDatum.put(SubmitConstant.SUBMIT_RESULT, SubmitConstant.NOT_ALLOWED);
                    } else {
                        commitDatum.put(SubmitConstant.STATUS, SubmitConstant.NOT_SUBMIT);
                        handlerSubmit(tableName, pkName, commitDatum);
                    }
                }
            }

        }
        return AjaxResult.success(commitData);
    }

    /**
     * 功能描述: 处理提交和反提交
     *
     * @param tableName   tableName
     * @param pkName      pkName
     * @param commitDatum 返回给前台的数据
     */
    private void handlerSubmit(String tableName, String pkName, JSONObject commitDatum) {
        //commitDatum.put(FormDataConstant.CREATE_BY, SecurityUtils.getUserId());
        //commitDatum.put(FormDataConstant.CREATE_TIME, new Timestamp(System.currentTimeMillis()));
        int effective = submitService.handlerSubmit(tableName, pkName, commitDatum, commitDatum.getLong(FormDataConstant.ID));
        if (effective > 0) {
            commitDatum.put(SubmitConstant.SUBMIT_RESULT, SubmitConstant.SUCCESS);
        } else {
            commitDatum.put(SubmitConstant.SUBMIT_RESULT, SubmitConstant.FAIL);
        }
    }

    private String getStatusFromFormData(JSONObject next, List<JSONObject> commitData) {
        for (JSONObject jsonObject : commitData) {
            if (jsonObject.getLong(FormDataConstant.ID).equals(next.getLong(FormDataConstant.ID))) {
                return jsonObject.getString(FormDataConstant.STATUS);
            }
        }
        throw new IllegalArgumentException("不可能没有一个合适的");
    }


    /**
     * 是否定制
     * 判断是否需要转发到定制接口
     *
     * @param tableName 表明
     * @param result    结果
     * @param action    动作
     * @return 结果
     */
    public List<JSONObject> isCustomized(String tableName, List<JSONObject> result, String action) {
        requireNonNull(tableName, "tableName is empty");
        requireNonNull(action, "action is empty");
        if (result != null && result.size() > 0) {
            //获取到服务名称
            String triggerName = getTriggerName(tableName, action);
            if (triggerName != null) {
                //组织请求 acos + RestTemplate
                return restTemplate.postForObject("http://" + triggerName, result, List.class);
            }
        }
        return result;
    }

    /**
     * 判断是什么action
     *
     * @param tableName 表名
     * @param action    动作
     * @return
     */
    private String getTriggerName(String tableName, String action) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, tableName);
        requireNonNull(genTable, "genTable empty");
        if (action.equals(TriggerActionConstant.ACTION_CREATE)) {
            return genTable.getTriggerCreate();
        } else if (action.equals(TriggerActionConstant.ACTION_RETRIEVE)) {
            return genTable.getTriggerRetrieve();
        } else if (action.equals(TriggerActionConstant.ACTION_UPDATE)) {
            return genTable.getTriggerUpdate();
        } else if (action.equals(TriggerActionConstant.ACTION_DELETE)) {
            return genTable.getTriggerDelete();
        } else if (action.equals(TriggerActionConstant.ACTION_SUBMIT)) {
            return genTable.getTriggerSubmit();
        }
        return null;
    }

    /**
     * 功能描述: 查询表从redis中
     *
     * @param redisKeyPrefix 前缀
     * @param tableName      表名
     * @return com.boman.domain.GenTable
     */
    public GenTable getTableFromRedisByTableName(String redisKeyPrefix, String tableName) {
        tableName = tableName.trim().toLowerCase();
        String key = requireNonNull(redisKeyPrefix, "redisKeyPrefix is empty") + requireNonNull(tableName, "tableName is empty");
        GenTable genTable = redisService.getCacheObject(key);
        if (ObjectUtils.isEmpty(genTable)) {
            genTable = remoteGenTableService.getByTableName(tableName);
            requireNonNull(genTable, "数据库中，未找到表对应的信息, tableName = " + tableName);
            // {@link com.boman.gen.controller.MyController.packTableAndInsertToRedis} 失效时间
            redisService.setCacheObject(key, genTable, 12L, TimeUnit.DAYS);
        }

        return genTable;
    }

    /**
     * 功能描述: 根据表名封装列的对应关系, 同时封装好字典值
     *
     * @param genTable genTable
     * @param isUi     带折叠true, 不带折叠false
     * @return com.boman.domain.dto.AjaxResult
     */
    public AjaxResult getByTableName(GenTable genTable, Boolean isUi) {
        int insertVisibleMaskMark = 0;
        String tableName = genTable.getTableName();
        List<GenTableColumn> allColumns = genTable.getColumns();
        List<GenTableColumn> hrs = filterHrAndSort(allColumns);
        List<GenTableColumn> resultCols;
        markTrueByExtend(allColumns);
        List<GenTableColumn> insertVisibleColumns = filterData(allColumns, insertVisibleMaskMark, INSERT_VISIBLE::equals);
        // 带折叠
        if (BooleanUtils.isTrue(isUi)) {
            removeColumnsByMask(hrs, insertVisibleMaskMark, INSERT_NOT_VISIBLE::equals);
            for (GenTableColumn hr : hrs) {
                Long parentId = hr.getId();
                List<GenTableColumn> children = Lists.newArrayListWithCapacity(16);
                for (GenTableColumn column : insertVisibleColumns) {
                    if (parentId.equals(column.getHrParentId())) {
                        children.add(column);
                    }
                }

                hr.setHrChildren(children);
            }

            resultCols = hrs;
        } else {
            // 不带折叠
            resultCols = insertVisibleColumns;
        }

        JSONObject result = new JSONObject();
        result.put(BUTTON_LIST, getButton(tableName, null));
        result.put(SHOW_DATA, resultCols);
        result.put(RULES, packRequireColumn(insertVisibleColumns));
        Integer tableColumn = genTable.getTableColumn();
        if (tableColumn != null) {
            result.put(TABLE_COLUMN, tableColumn);
        }
        return AjaxResult.success(result);
    }

    private JSONObject packRequireColumn(List<GenTableColumn> allColumns) {
        JSONObject jsonObject = new JSONObject();
        String msg = "请输入正确的";
        for (GenTableColumn column : allColumns) {
            List<JSONObject> list = Lists.newArrayList();
            if (GenTableColumn.IS_REQUIRED.equals(column.getIsRequired())) {
                JSONObject inner1 = new JSONObject();
                inner1.put(TRIGGER, BLUR);
                inner1.put(MESSAGE, msg + column.getColumnComment());
                inner1.put(REQUIRED, true);
                list.add(inner1);
                if (isNotEmpty(column.getRegular())) {
                    JSONObject inner2 = new JSONObject();
                    inner2.put(PATTERN, column.getRegular());
                    inner2.put(TRIGGER, BLUR);
                    inner2.put(MESSAGE, msg + column.getColumnComment());
                    list.add(inner2);
                }

                jsonObject.put(column.getColumnName(), list);
            }
        }

        return jsonObject;
    }

    public void packDictDataToColumns(List<GenTableColumn> columns, Predicate<String> predicate) {
        if (isEmpty(columns)) {
            return;
        }
        for (GenTableColumn column : columns) {
            String dictType = column.getDictType();
            if (predicate.test(dictType)) {
                column.setSysDictData(listSysDictDataByType(dictType));
            }
        }
    }


    public AjaxResult listAllColumnsByTableId(GenTable table) {
        AjaxResult ajaxResult = remoteGenTableColumnService.listColumnsByTableId(table.getId());
        return ajaxResult;
    }

    /**P
     * 功能描述: 新增的时候处理默认值， 从redis中拿到存在token中的userEnv对象，根据数据库中存的默认值的key，和userEnv中的key做比对，
     * 从中取出作为字段的默认值，redis中存userEnv的位置：{@link TokenService#packUserEnv(com.boman.system.api.model.LoginUser))}
     *
     * @param commitData commitData
     * @param columns 所有的列
     */
    private void handlerDefaultValue(JSONObject commitData, List<GenTableColumn> columns) {
        String token = SecurityUtils.getToken();
        LoginUser loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + token);
        JSONObject userEnv = loginUser.getUserEnv();
        for (GenTableColumn column : columns) {
            // 有默认值的列, 并且前台传过来的key中不包含
            if (isNotEmpty(column.getDefaultValue()) && !commitData.containsKey(column.getColumnName())) {
                String dbDefaultValue = column.getDefaultValue();
                String defaultValue = ColumnUtils.parseVariables(dbDefaultValue);
                String variables = dbDefaultValue.equalsIgnoreCase(defaultValue) ? dbDefaultValue : userEnv.getString(defaultValue);
                commitData.put(column.getColumnName(), variables);
            }
        }
    }

    /*
     * 功能描述: 单据规则
     *
     * @param commitData commitData
     * @param allColumns allColumns
     * @return void
     */
    private void buildBillRule(String tableName, JSONObject commitData, List<GenTableColumn> allColumns) {
        requireNonNull(tableName, "tableName is empty");
        for (GenTableColumn column : allColumns) {
            String columnName = column.getColumnName();
            String seqName = column.getSeqName();
            if (isEmpty(seqName)) {
                continue;
            }

            String rule = BillRuleUtils.getRules(tableName, seqName, columnName);
            commitData.put(columnName, rule);
        }
    }

    private void addOtherColumn(List<GenTableColumn> columns, String other) {
        if(StringUtils.isEmpty(other)) {
            return;
        }

        // 此为请假表单数据
        if (other.equals("leave")) {
            GenTableColumn userNameColum = new GenTableColumn();
            userNameColum.setColumnName("nick_name");
            columns.add(userNameColum);
        }
    }

    private void genDictValue(GenTableColumn column, JSONObject jsonObject, JSONObject jo, String other) {

        String dictType = column.getDictType();
        String columnName = column.getColumnName();
        if(StringUtils.isEmpty(other)) {
            jo.put("valuedata", jsonObject.get(columnName));
            return;
        }

        if (isNotEmpty(dictType)) {
            // 列表需要字典值的dictLabel, 不放入sysDictData
            List<SysDictData> sysDictData = column.getSysDictData();
            //获取到字段的值
            String value = String.valueOf(jsonObject.get(columnName));
            if (sysDictData != null && sysDictData.size() > 0){
                for (SysDictData sysDictDatum : sysDictData) {
                    if (sysDictDatum.getDictValue().equals(value)) {
                        jo.put("valuedata", sysDictDatum.getDictLabel());
                        jsonObject.put(columnName, sysDictDatum.getDictLabel());
                    }
                }
            }
        }else{
            jo.put("valuedata", jsonObject.get(columnName));
        }

    }
}

