package org.jeecg.modules.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.jeecg.modules.system.entity.SysSerialMax;
import org.jeecg.modules.system.entity.SysSerialPattern;
import org.jeecg.modules.system.mapper.SysSerialMaxMapper;
import org.jeecg.modules.system.mapper.SysSerialPatternMapper;
import org.jeecg.modules.system.service.ISysSerialPatternService;
import org.jeecg.modules.system.vo.CallResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.regex.*;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * @Copyright: 上海萃颠信息科技有限公司. All rights reserved.
 * @Title:SysSerialPatternServiceImpl.java
 * @Description:编号模式设置服务，包含编号值获取
 * @author 冯海夫
 * @date: 2019/5/27 15:30
 * @version V1.0
 * @updatehistory:
 *   2019/5/27 15:30 新增
 */
@Service
public class SysSerialPatternServiceImpl extends ServiceImpl<SysSerialPatternMapper, SysSerialPattern> implements ISysSerialPatternService {
    @Autowired
    SysSerialPatternMapper sysSerialPatternMapper;  // 编号模式mapper

    @Autowired
    SysSerialMaxMapper sysSerialMaxMapper; // 最大编号mapper

    /**
     * @Title:getExtraFields
     * @Description:获取当前编号需要的数据库字段
     * @param 
     * tableName - 表格名称
     * fieldName - 字段名称
     * @return 
     * org.jeecg.modules.system.vo.CallResult<java.util.ArrayList<java.lang.String>> -  额外字段列表,编号模式主键（message字段）
     * @exception 
     * @author 冯海夫
     * @date 2019/5/27 15:47 
     * @updatehistory:
     *   2019/5/27 15:47 新增
     */
    public CallResult<ArrayList<String>> getExtraFields(String tableName, String fieldName){
        // 返回值
        CallResult<ArrayList<String>> ret = new CallResult();
        try{
            // 获取编号模式
            SysSerialPattern sysSerialPattern = sysSerialPatternMapper.findByTableField(tableName, fieldName);
            if (sysSerialPattern == null){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_NOT_DEFINE);
                return ret;
            }
            // 获取编号模式中表格字段
            Pattern p = Pattern.compile("\\[\\w+\\]");
            Matcher  m = p.matcher(sysSerialPattern.getSerialPattern());
            ArrayList<String> content = new ArrayList<String>();
            while (m.find()){
                String findContent = m.group();
                content.add(findContent.substring(1, findContent.length()-1));
            }
            ret.setMessage(sysSerialPattern.getId());
            ret.setContent(content);
        }catch(Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.toString());
        }
        return ret;
    }
    /**
     * @Title:isSerialPatternValid
     * @Description:判断编号模式是否有效
     * @param
     * serialPattern - 编号模式字符串
     * @return
     * org.jeecg.modules.system.vo.CallResult<java.util.ArrayList<java.lang.String>> -  编号模式是否有效
     * @exception
     * @author 冯海夫
     * @date 2019/5/27 17:25
     * @updatehistory:
     *   2019/5/27 17:25 新增
     */
    public CallResult<String> isSerialPatternValid(String serialPattern){
        // 返回值
        CallResult<String> ret = new CallResult();
        try{
            if (serialPattern == ""){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_NULL);
                return ret;
            }
            String[] splitPattern = serialPattern.split("\\+");
            int fixedStringSum = 0;
            int tableFieldSum = 0;
            int dateStringSum = 0;
            int serialNumberSum = 0;
            int otherSum = 0;
            for (String subPattern : splitPattern){
                if (Pattern.matches("\\{[\\w#.]+\\}", subPattern)){ // 字符串前缀
                    fixedStringSum++;
                }else if(Pattern.matches("\\[\\w+\\]", subPattern)){ // 表格字段
                    tableFieldSum++;
                }else if(!Pattern.matches("[^yMdHms]", subPattern)){ // yyyyMMddHHmmss
                    dateStringSum++;
                }else if (Pattern.matches("^[1-9]+[0-9]*$", subPattern)){ // 数字
                    serialNumberSum++;
                }else{
                    otherSum++;
                }
            }

            if (fixedStringSum>4){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_INVALID);
                ret.setMessage("固定字符串前缀数量不能超过2个。");
            }
            if (tableFieldSum>4){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_INVALID);
                ret.setMessage(ret.getMessage()+"表格字段数量不能超过4个。");
            }
            if (dateStringSum>1){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_INVALID);
                ret.setMessage(ret.getMessage()+"日期字符串不能超过1个。");
            }
            if (serialNumberSum != 1){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_INVALID);
                ret.setMessage(ret.getMessage()+"有且只能有一个流水号字段。");
            }
            if (otherSum > 0) {
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_INVALID);
                ret.setMessage(ret.getMessage()+"包含不可识别的编号模式。");
            }
        }catch (Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.getMessage());
        }
        return ret;
    }
    /**
     * @Title:getNextSerial
     * @Description:获取下一个编号
     * @param
     * tableName - 表格名称
     * fieldName - 字段名称
     * extraFieldValues - 数据库字段值
     * updateTable - 是否更新最大编号
     * @return
     * org.jeecg.modules.system.vo.CallResult<java.lang.String> - 下一个编号
     * @exception 
     * @author 冯海夫
     * @date 2019/5/27 15:48 
     * @updatehistory:
     *   2019/5/27 15:48 新增
     */
    public CallResult<String> getNextSerial(String tableName, String fieldName, HashMap<String,String> extraFieldValues, boolean updateTable){
        // 返回值
        CallResult<String> ret = new CallResult();

        try{
            // 获取编号模式中额外的数据库字段
            CallResult<ArrayList<String>> extraFields = getExtraFields(tableName, fieldName);
            if (!extraFields.isSucceed()){
                ret.setBaseInfo(extraFields.getCode(), extraFields.getMessage());
                return ret;
            }
            // 从传入的字段参数中，获取定义的字段，如果定义的字段获取不到，返回错误
            HashMap<String,String> usedParams = new HashMap();
            for (String field : extraFields.getContent()){
                if (!extraFieldValues.containsKey(field)){
                    ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_FIELD_NOT_ENOUGH);
                    return ret;
                }
                usedParams.put(field, extraFieldValues.get(field));
            }
            // 获取编号模式
            SysSerialPattern sysSerialPattern = this.getById(extraFields.getMessage());
            // 多个部分用英文加号(+)分隔。前缀支持字符串(用大括号括起来{})和同一表其他字段(用中括号括起来)。年月日时分秒必须连一起，如yyyyMMddHHmmss，可以是其中的一部分
            String pattern = sysSerialPattern.getSerialPattern();
            // 编号模式内容为空
            if (pattern == null || pattern.equals("")){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_NULL);
                return ret;
            }
            // 编号模式无效
            ret = isSerialPatternValid(pattern);
            if (!ret.isSucceed())
                return ret;
            // 拼接sys_serial_max表查询条件,pattern_id（必须),fixed_string1~2（可选),table_field1~4(可选),date_string(可选)。生成新增实体
            SysSerialMax newSysSerialMax = new SysSerialMax();
            String[] splitPattern = pattern.split("\\+");
            QueryWrapper<SysSerialMax> querySerialMax = new QueryWrapper<>();
            querySerialMax.eq("pattern_id", sysSerialPattern.getId());
            newSysSerialMax.setPatternId(sysSerialPattern.getId());
            Class<?> classNewSysSerialMax = newSysSerialMax.getClass();
            String nextSerial = "";
            int serialLength = 0;

            int fixedStringIndex = 1;
            int tableFieldIndex = 1;
            for (String subPattern : splitPattern){
                if (Pattern.matches("\\{[\\w#.]+\\}", subPattern)){ // 字符串前缀
                    String prefix = subPattern.substring(1, subPattern.length()-1);
                    Method method = classNewSysSerialMax.getMethod("setFixedString"+fixedStringIndex, String.class);
                    method.invoke(newSysSerialMax, prefix);
                    String tableField = "fixed_string"+(fixedStringIndex++);
                    querySerialMax.and(query->query.eq(tableField, prefix ));
                    nextSerial += prefix;
                }else if(Pattern.matches("\\[\\w+\\]", subPattern)){ // 表格字段
                    String prefixField = subPattern.substring(1, subPattern.length()-1);
                    String prefixValue = usedParams.get(prefixField);
                    Method method = classNewSysSerialMax.getMethod("setTableField"+tableFieldIndex, String.class);
                    method.invoke(newSysSerialMax, prefixValue);
                    String tableField = "table_field"+(tableFieldIndex++);
                    querySerialMax.and(query->query.eq(tableField, prefixValue ));
                    nextSerial += prefixValue;
                }else if(!Pattern.matches("[^yMdHms]", subPattern)){ // yyyyMMddHHmmss
                    DateFormat dateFormat = new java.text.SimpleDateFormat(subPattern);
                    String dateString = dateFormat.format(new Date());
                    querySerialMax.and(query->query.eq("date_string", dateString ));
                    Method method = classNewSysSerialMax.getMethod("setDateString", String.class);
                    method.invoke(newSysSerialMax, dateString);
                    nextSerial += dateString;
                } else if (Pattern.matches("^[1-9]+[0-9]*$", subPattern)){
                    nextSerial += "{serial}";
                    serialLength = Integer.parseInt(subPattern);
                }
            }
            // 查询
            SysSerialMax sysSerialMax = sysSerialMaxMapper.findMaxSerial(querySerialMax);
            // 生成流水号
            if (sysSerialMax == null){
                sysSerialMax = newSysSerialMax;
                sysSerialMax.setMaxSerial(String.format("%0"+serialLength+"d", 1));
            }else{
                sysSerialMax.setMaxSerial(String.format("%0"+serialLength+"d", Integer.parseInt(sysSerialMax.getMaxSerial())+1));
            }
            nextSerial = nextSerial.replace("{serial}", sysSerialMax.getMaxSerial());
            ret.setContent(nextSerial);
            if (updateTable){
                if (sysSerialMax.getId() == null)
                    sysSerialMaxMapper.insert(sysSerialMax);
                else
                    sysSerialMaxMapper.updateById(sysSerialMax);
            }

        }catch(Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.getMessage());
        }
        return ret;
    }
    /**
     * @Title:getNextSerial
     * @Description:调用内部函数，对外提供接口
     * @param
     *  entity - 待编号的实体类
     *  tableName - 待编号的表格名称
     *  fieldName - 待编号的字段名称
     *  updateTable - 是否更新最大编号表
     * @return
     *  org.jeecg.modules.system.vo.CallResult<java.lang.String> - 下一个编号
     * @exception
     * @author 冯海夫
     * @date 2019/5/28 16:22
     * @updatehistory:
     *   2019/5/28 16:22 新增
     */
    public CallResult<String> getNextSerial(Object entity, String tableName, String fieldName, boolean updateTable) {
        CallResult<String> ret = new CallResult<>();
        try {
            CallResult<ArrayList<String>> extraFields = getExtraFields(tableName, fieldName);
            if (!extraFields.isSucceed()) {
                ret.setBaseInfo(extraFields.getCode(), extraFields.getMessage());
                return ret;
            }
            HashMap<String,String> extraFieldValues = new HashMap<>();
            if (extraFields.getContent().size()>0){
                Class<?> classEntity = entity.getClass();
                for (String extraFieldName : extraFields.getContent()){
                    Method method = classEntity.getMethod("get"+extraFieldName);
                    extraFieldValues.put(extraFieldName, (String)(method.invoke(entity)));
                }
            }
            ret = getNextSerial(tableName, fieldName, extraFieldValues, updateTable);
        }catch (Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.getMessage());
        }
        return ret;
    }

    /**
    * @Author chenchuang
    * @Description //TODO 上一个编号
    * @Date 2020/9/1 14:40
    * @Param [entity, tableName, fieldName, updateTable]
    * @return org.jeecg.modules.system.vo.CallResult<java.lang.String>
    */
    public CallResult<String> getUpperSerial(Object entity, String tableName, String fieldName, boolean updateTable) {
        CallResult<String> ret = new CallResult<>();
        try {
            CallResult<ArrayList<String>> extraFields = getExtraFields(tableName, fieldName);
            if (!extraFields.isSucceed()) {
                ret.setBaseInfo(extraFields.getCode(), extraFields.getMessage());
                return ret;
            }
            HashMap<String,String> extraFieldValues = new HashMap<>();
            if (extraFields.getContent().size()>0){
                Class<?> classEntity = entity.getClass();
                for (String extraFieldName : extraFields.getContent()){
                    Method method = classEntity.getMethod("get"+extraFieldName);
                    extraFieldValues.put(extraFieldName, (String)(method.invoke(entity)));
                }
            }
            ret = getUpperSerial(tableName, fieldName, extraFieldValues, updateTable);
        }catch (Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.getMessage());
        }
        return ret;
    }

    /**
    * @Author chenchuang
    * @Description //TODO 上一个编号
    * @Date 2020/9/1 14:40
    * @Param [tableName, fieldName, extraFieldValues, updateTable]
    * @return org.jeecg.modules.system.vo.CallResult<java.lang.String>
    */
    public CallResult<String> getUpperSerial(String tableName, String fieldName, HashMap<String,String> extraFieldValues, boolean updateTable){
        // 返回值
        CallResult<String> ret = new CallResult();

        try{
            // 获取编号模式中额外的数据库字段
            CallResult<ArrayList<String>> extraFields = getExtraFields(tableName, fieldName);
            if (!extraFields.isSucceed()){
                ret.setBaseInfo(extraFields.getCode(), extraFields.getMessage());
                return ret;
            }
            // 从传入的字段参数中，获取定义的字段，如果定义的字段获取不到，返回错误
            HashMap<String,String> usedParams = new HashMap();
            for (String field : extraFields.getContent()){
                if (!extraFieldValues.containsKey(field)){
                    ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_FIELD_NOT_ENOUGH);
                    return ret;
                }
                usedParams.put(field, extraFieldValues.get(field));
            }
            // 获取编号模式
            SysSerialPattern sysSerialPattern = this.getById(extraFields.getMessage());
            // 多个部分用英文加号(+)分隔。前缀支持字符串(用大括号括起来{})和同一表其他字段(用中括号括起来)。年月日时分秒必须连一起，如yyyyMMddHHmmss，可以是其中的一部分
            String pattern = sysSerialPattern.getSerialPattern();
            // 编号模式内容为空
            if (pattern == null || pattern.equals("")){
                ret.setCode(CallResult.CODE_SYS_SERIAL_PATTERN_DEFINE_NULL);
                return ret;
            }
            // 编号模式无效
            ret = isSerialPatternValid(pattern);
            if (!ret.isSucceed())
                return ret;
            // 拼接sys_serial_max表查询条件,pattern_id（必须),fixed_string1~2（可选),table_field1~4(可选),date_string(可选)。生成新增实体
            SysSerialMax newSysSerialMax = new SysSerialMax();
            String[] splitPattern = pattern.split("\\+");
            QueryWrapper<SysSerialMax> querySerialMax = new QueryWrapper<>();
            querySerialMax.eq("pattern_id", sysSerialPattern.getId());
            newSysSerialMax.setPatternId(sysSerialPattern.getId());
            Class<?> classNewSysSerialMax = newSysSerialMax.getClass();
            String nextSerial = "";
            int serialLength = 0;

            int fixedStringIndex = 1;
            int tableFieldIndex = 1;
            for (String subPattern : splitPattern){
                if (Pattern.matches("\\{[\\w#.]+\\}", subPattern)){ // 字符串前缀
                    String prefix = subPattern.substring(1, subPattern.length()-1);
                    Method method = classNewSysSerialMax.getMethod("setFixedString"+fixedStringIndex, String.class);
                    method.invoke(newSysSerialMax, prefix);
                    String tableField = "fixed_string"+(fixedStringIndex++);
                    querySerialMax.and(query->query.eq(tableField, prefix ));
                    nextSerial += prefix;
                }else if(Pattern.matches("\\[\\w+\\]", subPattern)){ // 表格字段
                    String prefixField = subPattern.substring(1, subPattern.length()-1);
                    String prefixValue = usedParams.get(prefixField);
                    Method method = classNewSysSerialMax.getMethod("setTableField"+tableFieldIndex, String.class);
                    method.invoke(newSysSerialMax, prefixValue);
                    String tableField = "table_field"+(tableFieldIndex++);
                    querySerialMax.and(query->query.eq(tableField, prefixValue ));
                    nextSerial += prefixValue;
                }else if(!Pattern.matches("[^yMdHms]", subPattern)){ // yyyyMMddHHmmss
                    DateFormat dateFormat = new java.text.SimpleDateFormat(subPattern);
                    String dateString = dateFormat.format(new Date());
                    querySerialMax.and(query->query.eq("date_string", dateString ));
                    Method method = classNewSysSerialMax.getMethod("setDateString", String.class);
                    method.invoke(newSysSerialMax, dateString);
                    nextSerial += dateString;
                } else if (Pattern.matches("^[1-9]+[0-9]*$", subPattern)){
                    nextSerial += "{serial}";
                    serialLength = Integer.parseInt(subPattern);
                }
            }
            // 查询
            SysSerialMax sysSerialMax = sysSerialMaxMapper.findMaxSerial(querySerialMax);
            // 生成流水号
            if (sysSerialMax == null){
                sysSerialMax = newSysSerialMax;
//                sysSerialMax.setMaxSerial(String.format("%0"+serialLength+"d", 1));
            }else{
                sysSerialMax.setMaxSerial(String.format("%0"+serialLength+"d", Integer.parseInt(sysSerialMax.getMaxSerial())-1));
            }
            nextSerial = nextSerial.replace("{serial}", sysSerialMax.getMaxSerial());
            ret.setContent(nextSerial);
            if (updateTable){
                if (sysSerialMax.getId() == null)
                    sysSerialMaxMapper.insert(sysSerialMax);
                else
                    sysSerialMaxMapper.updateById(sysSerialMax);
            }

        }catch(Exception ex){
            ex.printStackTrace();
            ret.setCode(CallResult.CODE_EXCEPTION);
            ret.setMessage(ex.getMessage());
        }
        return ret;
    }
}
