package com.prolog.rdc.tms.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import com.prolog.common.util.DateUtils;
import com.prolog.common.util.ToolKits;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.ParameterException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.lock.DistributedLock;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.StringUtils;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.product.core.framework.util.PlgFxUsrUtils;
import com.prolog.rdc.tms.constant.BussinessConstant;
import com.prolog.rdc.tms.dao.mapper.SysCodeengineMapper;
import com.prolog.rdc.tms.model.entity.SysCodeengineModel;
import com.prolog.rdc.tms.model.vo.SysCodeengineDeleteVO;
import com.prolog.rdc.tms.model.vo.SysCodeengineQueryVO;
import com.prolog.rdc.tms.model.vo.SysCodeengineSaveVO;
import com.prolog.rdc.tms.model.vo.SysCodeengineUpdateVO;
import com.prolog.rdc.tms.service.SysCodeengineService;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 
 *
 * 号码管理器实现层
 *
 * @className :SysCodeengineServiceImpl
 * @author :fanzw
 * @creationTime :2021/04/21
 * @description :
 *
 * @modifier :fanzw
 * @modifyTime :2021/04/21
 * @reviseRemarks:
 *
 * @version :1.0.0
 *
 */
@Service
//@Transactional
public class SysCodeengineServiceImpl implements SysCodeengineService {

	@Autowired
	private SysCodeengineMapper mapper;

	@Autowired
	private DistributedLock distributedLock;

	/**
	 * 号码管理器分页查询
	 */
	@Override
	public Page<SysCodeengineModel> listSysCodeengineData(SysCodeengineQueryVO sysCodeengineQueryVO) throws Exception {
		ToolKits.isParamNull(sysCodeengineQueryVO, "号码管理器分页查询请求参数不为空");
		ToolKits.isParamNull(sysCodeengineQueryVO.getPageNum(), "当前页不能为空");
		ToolKits.isParamNull(sysCodeengineQueryVO.getPageSize(), "页面大小不能为空");

		// 分页
		PageUtils.startPage(sysCodeengineQueryVO.getPageNum(), sysCodeengineQueryVO.getPageSize());

		// 人员权限
		UserEntity user = PlgFxUsrUtils.getUserEntity();
		ToolKits.isParamNull(user, "当前人员没有操作权限");

		// 系统参数查询
		Criteria criteria = Criteria.forClass(SysCodeengineModel.class);
		Restriction r1 = Restrictions.like("code", "%" + sysCodeengineQueryVO.getSearch() + "%");
		Restriction r2 = Restrictions.like("name", "%" + sysCodeengineQueryVO.getSearch() + "%");
		Restriction r3 = ToolKits.isEmpty(sysCodeengineQueryVO.getSearch()) ? null : Restrictions.or(r1, r2);
		criteria.setRestriction(Restrictions.and(r3));
		criteria.setOrder(Order.newInstance().desc(new String[] { "createtime" }));
		// 执行查询
		List<SysCodeengineModel> sysCodeengineModels = mapper.findByCriteria(criteria);

		return PageUtils.getPage(sysCodeengineModels);
	}

	/**
	 * 号码管理器新增
	 */
	@Override
	public void saveSysCodeengineData(SysCodeengineSaveVO sysCodeengineSaveVO) throws Exception {
		ToolKits.isParamNull(sysCodeengineSaveVO, "号码管理器新增请求参数不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getCode(), "识别代号不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getName(), "名称不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getCodetype(), "号码类型分类不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getCodedatatype(), "值类型不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getLength(), "长度不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getGap(), "间隔不为空");
		ToolKits.isParamNull(sysCodeengineSaveVO.getBillrule(), "号码规则不为空");
		// 人员权限
		UserEntity user = PlgFxUsrUtils.getUserEntity();
		ToolKits.isParamNull(user, "当前人员没有操作权限");

		Criteria criteria = Criteria.forClass(SysCodeengineModel.class);
		Restriction r1 = Restrictions.eq("code", sysCodeengineSaveVO.getCode());
		criteria.setRestriction(Restrictions.and(r1));
		// 执行查询
		List<SysCodeengineModel> sysCodeengineModels = mapper.findByCriteria(criteria);
		if (ToolKits.isEmpty(sysCodeengineModels)) {
			// 获取序号
			String codeid = this.getCodeNumberByCode(BussinessConstant.CODEENGINE_SYS_CODEENGINE_CODEID, "L");

			SysCodeengineModel sysCodeengineModel = new SysCodeengineModel();
			BeanUtils.copyProperties(sysCodeengineSaveVO, sysCodeengineModel);
			sysCodeengineModel.setEnterpriseId(user.getEnterpriseId());
			sysCodeengineModel.setCodeid(codeid);
			sysCodeengineModel.setIssegsource("N");
			sysCodeengineModel.setGroupid("0");
			sysCodeengineModel.setCreateperson(user.getNickname());
			sysCodeengineModel.setCreatetime(new Date());
			mapper.save(sysCodeengineModel);
		} else {
			throw new ParameterException("识别代号[" + sysCodeengineSaveVO.getCode() + "]已存在");
		}
	}

	/**
	 * 号码管理器修改
	 */
	@Override
	public void updateSysCodeengineData(SysCodeengineUpdateVO sysCodeengineUpdateVO) throws Exception {
		ToolKits.isParamNull(sysCodeengineUpdateVO, "号码管理器修改请求参数不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getCodeid(), "号码代号不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getCode(), "识别代号不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getName(), "名称不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getCodetype(), "号码类型分类不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getCodedatatype(), "值类型不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getLength(), "长度不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getGap(), "间隔不为空");
		ToolKits.isParamNull(sysCodeengineUpdateVO.getBillrule(), "号码规则不为空");

		// 人员权限
		UserEntity user = PlgFxUsrUtils.getUserEntity();
		ToolKits.isParamNull(user, "当前人员没有操作权限");

		SysCodeengineModel sysCodeengineModel = mapper.findById(sysCodeengineUpdateVO.getCodeid(), SysCodeengineModel.class);
		if (ToolKits.isNotEmpty(sysCodeengineModel)) {
			// 校验是否修改重复
			Criteria criteria = Criteria.forClass(SysCodeengineModel.class);
			Restriction r1 = Restrictions.eq("code", sysCodeengineUpdateVO.getCode());
			criteria.setRestriction(Restrictions.and(r1));
			// 执行查询
			List<SysCodeengineModel> sysCodeengineModels = mapper.findByCriteria(criteria);
			if (sysCodeengineModels.size() > 1) {
				throw new ParameterException("识别代号[ " + sysCodeengineUpdateVO.getCode() + "]数据重复");
			}

			SysCodeengineModel model = sysCodeengineModels.get(0);
			if (!sysCodeengineUpdateVO.getCodeid().equals(model.getCodeid())) {
				throw new ParameterException("识别代号[ " + sysCodeengineUpdateVO.getCode() + "]已存在");
			}

			BeanUtils.copyProperties(sysCodeengineUpdateVO, sysCodeengineModel);
			sysCodeengineModel.setUpdateperson(user.getNickname());
			sysCodeengineModel.setUpdatetime(new Date());
			mapper.update(sysCodeengineModel);
		} else {
			throw new ParameterException("号码代号[" + sysCodeengineUpdateVO.getCodeid() + "]不存在");
		}
	}

	/**
	 * 号码管理器删除
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteSysCodeengineData(List<SysCodeengineDeleteVO> sysCodeengineDeleteVOs) throws Exception {
		ToolKits.isParamNull(sysCodeengineDeleteVOs, "号码管理器删除请求参数不为空");

		for (SysCodeengineDeleteVO sysCodeengineDeleteVO : sysCodeengineDeleteVOs) {
			ToolKits.isParamNull(sysCodeengineDeleteVO.getCodeid(), "号码代号不为空");

			mapper.deleteById(sysCodeengineDeleteVO.getCodeid(), SysCodeengineModel.class);
		}
	}

	/**
	 * 查询编码表
	 */
	@Override
	public List<SysCodeengineModel> getCodeengine(String condition) {
		return mapper.getCodeengine(condition);
	}

	/**
	 * 生成多个编码
	 */
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	@Override
	public List<String> getCodeNumberByCodes(String code, String codetype, Integer codenum) {
		ToolKits.isParamNull(code, "资源编号不能为空！");
		ToolKits.isParamNull(codetype, "资源类型不能为空！");
		ToolKits.isParamNull(codenum, "资源码个数不能为空!");
		if (codenum < 1) {
			throw new ParameterException("资源码个数不能为空!");
		}
		List<String> list = getCodeNumberByCodeNoTransaction(code, codetype, codenum);

		if (list.size() != codenum) {
			throw new ParameterException("资源码生成了【" + list.size() + "】个与实际生成的个数不符【" + codenum + "】");
		}
		return list;
	}

	/**
	 * 多个code组求出序列号
	 * <p>
	 * Title: getCodeNumberByCodes
	 * </p>
	 * <p>
	 * Description:
	 * </p>
	 * 
	 * @param list
	 * @return
	 */
	@Override
//	@Transactional
	public Map<String, List<String>> getCodeNumberGroupByCodes(List<Map<String, Object>> list) {
		if (list == null || list.size() < 1) {
			throw new RuntimeException("参数不能为空");
		}
		Map<String, List<String>> result = new HashMap<>();
		for (Map<String, Object> map : list) {
			String code = ToolKits.convertToString(map.get("code"));
			String codetype = ToolKits.convertToString(map.get("codetype"));
			int codenum = ToolKits.convertToInteger(map.get("codenum"));
			ToolKits.isParamNull(code, "资源编号不能为空！");
			ToolKits.isParamNull(codetype, "资源类型不能为空！");
			ToolKits.isParamNull(codenum, "资源码个数不能为空!");
			if (codenum < 1) {
				throw new ParameterException("资源码个数不能为空!");
			}
			List<String> listResult = new ArrayList<>();
			for (int i = 0; i < codenum; i++) {
				String maxbillno = getCodeNumberByCodeNoTransaction(code, codetype);
				if (ToolKits.isEmpty(maxbillno)) {
					throw new ParameterException("资源码生成错误！");
				} else if ("".equals(maxbillno) || maxbillno.length() <= 0) {
					throw new ParameterException("资源码生成错误！");
				}
				listResult.add(maxbillno);
			}

			if (listResult.size() != codenum) {
				throw new ParameterException("资源码生成了【" + listResult.size() + "】个与实际生成的个数不符【" + codenum + "】");
			}
			result.put(code, listResult);
		}
		return result;
	}

	/**
	 * 生成单个编码
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public String getCodeNumberByCode(String code, String codetype) {
		return getCodeNumberByCodeNoTransaction(code, codetype);
	}

	public String getCodeNumberByCodeNoTransaction(String code, String codetype) {
		return getCodeNumberByCodeNoTransaction(code, codetype, 1).get(0);
	}

	public List<String> getCodeNumberByCodeNoTransaction(String code, String codetype, int num) {
		//redis key前缀
		String redisKeyPrefix = "REDIS_LOCK_KEY:";
		//锁等待时间
		long waittimeout = 30 * 1000;
		//锁过期时间，可根据业务代码执行时间配置
		long timeout = 30 * 1000;
		RLock lock = distributedLock.lockForLock(redisKeyPrefix + code, waittimeout, timeout);
		if (ObjectUtil.isNotNull(lock)) {
			List<String> resultList;
			try {
				List<SysCodeengineModel> list = mapper.findByMap(MapUtils.put("code", code).getMap(), SysCodeengineModel.class);
				ToolKits.isParamNull(list, "号码资源不存在");
				SysCodeengineModel sce = list.get(0);
				// Integer li_codeid = sce.getCodeid();
				String li_codeid = sce.getCodeid();
				/** DB sequence 生成 **/
				List<String> seqList = Lists.newArrayList();
				if ("SEED".equals(sce.getCodetype())) {
					seqList.addAll(geneSeedNo(sce.getSegcodesql(), num));
				} else {
					// 有parent,优先使用
					if (sce.getGroupid() != null && sce.getGroupid() != null) {
						Integer li_count = mapper.countGroup(sce.getGroupid());
						li_codeid = li_count > 0 ? sce.getGroupid() : li_codeid;
					}
					sce = mapper.getSysCodeengineById(li_codeid);
					if ("SEED".equals(sce.getCodetype())) {
						seqList.addAll(geneSeedNo(sce.getSegcodesql(), num));
					}
				}
				final String rule = sce.getBillrule();
				final String codeFlag = sce.getCodeflag();
				// 生成最大号码数 sequence 步长和数据表字段gap值必须一致
				seqList = geneMaxNos(sce, num);
				if (seqList.isEmpty()) {
					throw new ParameterException("序列生成失败！");
				}
				String maxflowno = seqList.get(seqList.size() - 1);
				// 构造需要返回的结果
				resultList = seqList.stream().map(s -> geneRetValue(rule, codeFlag, s)).collect(Collectors.toList());
				int index = mapper.updateMaxNo(maxflowno, DateUtils.now(DateUtils.PATTERN_NORMAL), sce.getCodeid());
				if (index > 1) {
					throw new ParameterException("最大号码更新失败！");
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			} finally {
				if (lock.isLocked() && lock.isHeldByCurrentThread()) {
					lock.unlock();
				}
			}
			return resultList;
		} else {
			throw new BizException("取号失败，获取redis锁失败！");
		}
	}

	/**
	 * @Description 构造返回值
	 * @param ruleStr   :
	 * @param codeflag  :
	 * @param ls_seedno :
	 * @return : java.lang.String
	 */
	private String geneRetValue(final String ruleStr, String codeflag, String ls_seedno) {
		ToolKits.isParamNull(ruleStr, "请配置号码规则");

		StringBuilder maxno = new StringBuilder();
		String[] rules = ruleStr.split("\\|\\|");
		for (int j = 0; j < rules.length; j++) {
			String rule = rules[j].replaceAll("'", "");
			if (rule.length() >= 4) {
				String flg = rule.substring(0, 4);
				if ("Date".equals(flg)) {
					int k = rule.indexOf("(");
					int t = rule.indexOf(")");
					if (k == -1 || t == -1) {
						throw new ParameterException("号码规则(日期格式错误)");
					}

					String datetime = DateUtils.now("yyyyMMddHHmmss");
					String flgs = rule.substring(k + 1, t);
					flgs = flgs.replaceAll("yyyy", datetime.substring(0, 4));
					flgs = flgs.replaceAll("mm", datetime.substring(4, 6));
					flgs = flgs.replaceAll("dd", datetime.substring(6, 8));
					flgs = flgs.replaceAll("24hh", datetime.substring(8, 10));
					flgs = flgs.replaceAll("hh", datetime.substring(8, 10));
					flgs = flgs.replaceAll("mi", datetime.substring(10, 12));
					flgs = flgs.replaceAll("ss", datetime.substring(12, 14));
					maxno.append(flgs);
				} else if ("List".equals(rule) || "Seed".equals(rule)) {
					maxno.append(ls_seedno);
				} else if ("CodeFlag".equals(rule)) {
					maxno.append(codeflag);
				} else if ("InsId".equals(rule)) {
				} else {
					maxno.append(rule);
				}
			} else {
				maxno.append(rule);
			}

		}
		return maxno.toString();
	}

	/**
	 * @Description 生成最大号码值
	 * @param :
	 * @param :
	 * @return : java.lang.String
	 */
	private String geneMaxNo(String maxNo, String codeType, Date lastDate, Integer gap, Integer noLen) {
		String ls_maxflowno = ToolKits.isEmpty(maxNo) ? "0" : maxNo;
		// 如果等于0,说明最后修改日期和当前是同一天，就不用初始化
		if ("D".equals(codeType) && !Long.valueOf("0").equals(DateUtils.subtract(DateUtils.nowDate(), lastDate, ChronoUnit.DAYS))) {
			ls_maxflowno = StringUtils.padLeft("0", noLen, "0");
		}

		ls_maxflowno = ToolKits.convertToString(ToolKits.convertToLong(ls_maxflowno) + (gap == null ? 1 : gap));
		if (ToolKits.isEmpty(noLen)) {
			ls_maxflowno = StringUtils.padLeft(ls_maxflowno, 10, "0");
		} else {
			if (noLen - ls_maxflowno.length() > 0) {
				ls_maxflowno = StringUtils.padLeft(ls_maxflowno, noLen, "0");
			}
		}
		return ls_maxflowno;
	}

	private List<String> geneMaxNos(SysCodeengineModel sce, int num) {
		List<String> result = Lists.newArrayList();
		if (num == 1) {
			result.add(geneMaxNo(sce.getMaxbillno(), sce.getCodetype(), sce.getLastdate(), sce.getGap(), sce.getLength()));
		} else if (num > 1) {
			String maxNo = sce.getMaxbillno();
			for (int i = 0; i < num; i++) {
				Date geneDate = i == 0 ? sce.getUpdatetime() : DateUtils.nowDate();
				maxNo = geneMaxNo(maxNo, sce.getCodetype(), geneDate, sce.getGap(), sce.getLength());
				result.add(maxNo);
			}
		}
		return result;
	}

	/**
	 * @Description 批量序列值获取
	 * @param sequeueName :
	 * @param num         :
	 * @return : java.util.List<java.lang.String>
	 */
	private List<String> geneSeedNo(String sequeueName, int num) {
		ToolKits.isParamNull(sequeueName, "请定义段代码(数据库序列)");
		String nocache = UUID.randomUUID().toString().replace("-", "");
		String sql = "select to_char(" + sequeueName + ".nextval) nextvalue from dual where '" + nocache + "'='" + nocache + "' connect by rownum <= " + num;
		List<HashMap<String, Object>> data = mapper.findWithSql(sql, null);
		ToolKits.isParamNull(data, "段代码获取失败(数据库序列)");
		return data.stream().map(m -> String.valueOf(m.get("NEXTVALUE"))).collect(Collectors.toList());
	}

}
