package com.boman.system.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.boman.common.core.utils.collection.CollectionUtils;
import com.boman.common.core.utils.obj.ObjectUtils;
import com.boman.common.core.web.domain.AjaxResult;
import com.boman.common.redis.RedisKey;
import com.boman.common.redis.service.RedisService;
import com.boman.gen.controller.MyController;
import com.boman.gen.domain.GenTable;
import com.boman.gen.domain.GenTableColumn;
import com.boman.system.mapper.StandardlyMapper;
import com.boman.system.service.IBaseDeleteService;
import com.boman.system.service.IBaseSaveService;
import com.boman.system.service.IBaseSelectService;
import com.boman.system.utils.IdUtils;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.boman.common.core.utils.obj.ObjectUtils.requireNonNull;
import static com.boman.common.core.utils.obj.ObjectUtils.requiredNonNull;
import static com.boman.system.common.FormDataConstant.CONDITION;
import static com.boman.system.common.FormDataConstant.SHOW_DATA;

/**
 * @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;

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

    private BaseTableDTO packTableDTO(BaseTableSaveDTO baseTableSaveDTO) {
        BaseTableDTO baseTableDTO = new BaseTableDTO();
        baseTableDTO.setFixedData(baseTableSaveDTO.getFixedData());
        baseTableDTO.setObjId(baseTableSaveDTO.getObjId());
        baseTableDTO.setTable(baseTableSaveDTO.getTable());
        return baseTableDTO;
    }

    public final AjaxResult objectSave(BaseTableSaveDTO baseTableSaveDTO) {
        BaseTableDTO baseTableDTO = packTableDTO(baseTableSaveDTO);
        TableServiceContext context = TableServiceContext.createFrom(baseTableDTO);
        // 拿到pkName和maxId
        List<GenTableColumn> columns = context.getTable().getColumns();
        String pkName = IdUtils.getPkName(columns);
        requireNonNull(pkName);

        Long maxId = IdUtils.getMaxId(baseTableDTO.getTable(), pkName);
        RowResult rowResult = saveService.insertRow(context.getRealTableName(), pkName, maxId, context.getRows().get(0));
        if (RowResult.checkSuccess(rowResult)) {
            LOGGER.info("保存成功，封装到数据库的数据为: {}", JSON.toJSONString(rowResult.getData()));
        } else {
            LOGGER.error("保存失败，保持的原始数据为: {}", JSON.toJSONString(baseTableSaveDTO));
        }

        return AjaxResult.success(rowResult);
    }

    /**
     * 功能描述: 通用删除接口 (真的删除)
     *
     * @param dto 前台传过来的dto
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult objectDelete(BaseTableSaveDTO dto) {
        requireNonNull(dto.getTable());
        Long[] idArr = CollectionUtils.listToArray(dto.getIdList());
        requiredNonNull(idArr);
        // 拿到pkName
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        String pkName = IdUtils.getPkName(genTable.getColumns());

        RowResult rowResult = deleteService.objectDelete(idArr, dto.getTable(), requireNonNull(pkName));
        LOGGER.info(rowResult.getMessage() + ", id: {}", Arrays.toString(idArr));
        return AjaxResult.success(rowResult);
    }


    /**
     * 功能描述: 通用删除接口 (真的删除)
     *
     * @param dto 前台传过来的dto
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult objectLogicDelete(BaseTableSaveDTO dto) {
        requireNonNull(dto.getTable());
        Long[] idArr = CollectionUtils.listToArray(dto.getIdList());
        requiredNonNull(idArr);

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

        JSONObject jsonObject= new JSONObject();
        jsonObject.put(dto.getLogicDelName(), dto.getLogicDelValue());

        RowResult rowResult = deleteService.objectLogicDelete(idArr, dto.getTable(), requireNonNull(pkName), jsonObject);
        LOGGER.info(rowResult.getMessage() + ", id: {}", Arrays.toString(idArr));
        return AjaxResult.success(rowResult);
    }

    /**
     * 功能描述: 获取单表单数据
     *
     * @param dto condition
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult queryList(BaseTableSaveDTO dto) {
        requireNonNull(dto.getTable());

        // 拿到每个字段对应的查询类型，=、 like、 >、 <
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        JSONObject fixedData = dto.getFixedData();
        requireNonNull(fixedData);
        // 查询条件
        JSONObject condition = fixedData.getJSONObject(CONDITION);
        List<GenTableColumn> columns = genTable.getColumns();
        // 封装好以后的查询条件
        JSONObject packCondition =  packColCondition(columns, condition);
        // 需要返回到前台的列
        JSONArray showData = fixedData.getJSONArray(SHOW_DATA);
        JSONObject rows = new JSONObject();
        int total = selectService.countByCondition(genTable.getTableName(), condition, packCondition);
        rows.put(FormDataConstant.PAGE_TOTAL, total);
        if (total <= 0) {
            rows.put(FormDataConstant.PAGE_ROWS, null);
            return AjaxResult.success(rows);
        }

        List<JSONObject> result = selectService.selectByCondition(genTable.getTableName(), condition, packCondition
                , showData, dto.getOrderBy(), dto.getLimit(), dto.getOffset());
        rows.put(FormDataConstant.PAGE_ROWS, result);
        return AjaxResult.success(rows);
    }

 /**
     * 功能描述: 获取单表单数据
     *
     * @param dto condition
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult getObject(BaseTableSaveDTO dto) {
        requireNonNull(dto.getTable());

        // 拿到每个字段对应的查询类型，=、 like、 >、 <
        // 拿到pkName
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, dto.getTable());
        String pkName = IdUtils.getPkName(genTable.getColumns());

        JSONObject fixedData = dto.getFixedData();
        requireNonNull(fixedData);
        Long id = fixedData.getLong(FormDataConstant.ID);
        requireNonNull(id);

        // 默认查所有字段，不支持自定义
        JSONObject jsonObject = selectService.selectById(genTable.getTableName(), pkName, id);
        return AjaxResult.success(jsonObject);
    }

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

        JSONObject result = 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 作为判断需不需要转义的一个标准，防止索引失效
                    result.put(key, packValue(String.valueOf(value), column.getQueryType(), column.getColumnType()));
                    break;
                }
            }
        }

        return result;
    }

    /**
     * 功能描述: 获取表单查询字段、按钮、表头
     * 注意: 都是从redis中拿的，如果数据库和redis不一致，则需刷新一下redis
     * 刷新的入口为 {@link MyController#loadTable(com.boman.gen.domain.GenTable)}
     *
     *                    eg:{
     *                          "table": "sys_config",
     *                        }
     *
     * @param condition condition
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult getTableQuery(BaseTableSaveDTO condition) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.TABLE_INFO, condition.getTable());

        List<GenTableColumn> columns = genTable.getColumns();
        JSONObject jsonObject = new JSONObject();
        // 查询字段
        ArrayList<GenTableColumn> queryList = Lists.newArrayListWithCapacity(16);
        for (GenTableColumn column : columns) {
            if (GenTableColumn.IS_QUERY.equalsIgnoreCase(column.getIsQuery())) {
                queryList.add(column);
            }
        }

        jsonObject.put(FormDataConstant.QUERY_LIST, queryList);
        // genTable.getMenuRole() 暂时数据库没有数据，
        jsonObject.put(FormDataConstant.BUTTON_LIST, genTable.getMenuRole());

        // 表头
        List<GenTableColumn> tableHeadList = columns.stream()
                .filter(genTableColumn -> GenTableColumn.IS_LIST.equals(genTableColumn.getIsList()))
                .collect(Collectors.toList());
        jsonObject.put(FormDataConstant.TABLE_HEAD_LIST, tableHeadList);
        return AjaxResult.success(jsonObject);
    }

    /**
     * 功能描述: 封装查询条件，在拼sql的时候按照此规则去取值
     * {@link StandardlyMapper.SqlProvider#selectByCondition(java.util.Map)}
     *
     * @param value      查询的值
     * @param queryType  queryType
     * @param columnType columnType
     * @return java.lang.String
     */
    public String packValue(String value, String queryType, String columnType) {
        requireNonNull(value);
        requireNonNull(queryType);
        return value + "_" + queryType + "_" + columnType;
    }

    /**
     * 功能描述: 获取表单子表
     *
     * @param condition condition
     * @return com.boman.common.core.web.domain.AjaxResult
     */
    public AjaxResult objectTab(BaseTableSaveDTO condition) {
        GenTable genTable = getTableFromRedisByTableName(RedisKey.RELATION, condition.getTable());

        List<GenTable> childTableList = genTable.getRelationList();
        // 此表没有关联子表，查啥查
        requireNonNull(childTableList);

        for (GenTable childTable : childTableList) {
            String childTableName = childTable.getTableName();


        }


        return AjaxResult.success();
    }

    private GenTable getTableFromRedisByTableName(String redisKeyPrefix, String tableName){
        GenTable genTable = redisService.getCacheObject(redisKeyPrefix + requireNonNull(tableName));
        return requireNonNull(genTable);
    }

}
