package com.qmth.sop.business.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.cache.CommonCacheService;
import com.qmth.sop.business.entity.Sequence;
import com.qmth.sop.business.entity.SysConfig;
import com.qmth.sop.business.mapper.SequenceMapper;
import com.qmth.sop.business.service.SequenceService;
import com.qmth.sop.business.service.TGErrorService;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.ErrorEnum;
import com.qmth.sop.common.enums.ExceptionResultEnum;
import com.qmth.sop.common.enums.SequenceEnum;
import com.qmth.sop.common.enums.TFCustomTypeEnum;
import com.qmth.sop.common.lock.MemoryLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Optional;

/**
 * <p>
 * 序列表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-08-14
 */
@Service
public class SequenceServiceImpl extends ServiceImpl<SequenceMapper, Sequence> implements SequenceService {

    @Resource
    MemoryLock memoryLock;

    @Resource
    TGErrorService tgErrorService;

    @Resource
    CommonCacheService commonCacheService;

    /**
     * 获取序列号
     *
     * @param name
     * @return
     */
    @Override
    public Long selectNextVal(String name) throws InterruptedException {
        boolean lock = false;
        for (int i = 0; i < SystemConstant.MAX_SEQUENCE_CIRCLE_COUNT; i++) {
            lock = memoryLock.lock(SystemConstant.LOCK_SEQUENCE_PREFIX + name, name, SystemConstant.LOCK_SEQUENCE_TIME_OUT);
            if (lock) {
                try {
                    return this.baseMapper.selectNextVal(name);
                } finally {
                    memoryLock.unlock(SystemConstant.LOCK_SEQUENCE_PREFIX + name);
                }
            } else {
                Thread.sleep(SystemConstant.SEQUENCE_THREAD_SLEEP);
            }
        }
        if (!lock) {
            tgErrorService.saveExamTgError(ErrorEnum.SEQUENCE.name(), ErrorEnum.SEQUENCE.getTitle());
            throw ExceptionResultEnum.ERROR.exception("获取序列失败");
        }
        return null;
    }

    /**
     * 创建流程编号
     *
     * @param tfCustomTypeEnum
     * @return
     */
    @Override
    public String createFlowCode(TFCustomTypeEnum tfCustomTypeEnum) throws InterruptedException {
        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.FLOW_CODE_LENGTH);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("编码长度未设置"));
        String sequence = String.format("%0" + sysConfig.getConfigValue() + "d", this.selectNextVal(tfCustomTypeEnum.getCode()));
        return tfCustomTypeEnum.getMark() + SystemConstant.getNowTime() + sequence;
    }

    /**
     * 创建CRM编号
     *
     * @param crm
     * @return
     * @throws InterruptedException
     */
    @Override
    public String createCrmCode(String crm) throws InterruptedException {
        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.FLOW_CODE_LENGTH);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("编码长度未设置"));
        return SystemConstant.getNowTime() + String.format("%0" + sysConfig.getConfigValue() + "d", this.selectNextVal(crm));
    }

    @Override
    public String createCode(String name) throws InterruptedException {
        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.FLOW_CODE_LENGTH);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("编码长度未设置"));
        return SystemConstant.getNowTime() + String.format("%0" + sysConfig.getConfigValue() + "d", this.selectNextVal(name));
    }

    @Override
    public String createSerialNo(SequenceEnum sequenceEnum) throws InterruptedException {
        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.FLOW_CODE_LENGTH);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("编码长度未设置"));
        return sequenceEnum.getPrefix() + SystemConstant.getNowTime() + String.format("%0" + sysConfig.getConfigValue() + "d", this.selectNextVal(sequenceEnum.getCode()));
    }

    /**
     * 序列重置
     * @return
     */
    @Override
    @Transactional
    public Boolean sequenceReset() {
        return this.baseMapper.sequenceReset();
    }
}
