package com.liquidnet.client.admin.zhengzai.adam.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.client.admin.common.utils.ShiroUtils;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamMemberCodeAdminService;
import com.liquidnet.common.cache.redis.util.AbstractRedisUtil;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.admin.AdamMemberCodeGenParam;
import com.liquidnet.service.adam.dto.vo.AdamMemberCodeVo;
import com.liquidnet.service.adam.entity.AdamMember;
import com.liquidnet.service.adam.entity.AdamMemberCode;
import com.liquidnet.service.adam.entity.AdamMemberCodeBatch;
import com.liquidnet.service.adam.entity.AdamMemberPrice;
import com.liquidnet.service.adam.mapper.AdamMemberCodeBatchMapper;
import com.liquidnet.service.adam.mapper.AdamMemberCodeMapper;
import com.liquidnet.service.adam.mapper.AdamMemberMapper;
import com.liquidnet.service.adam.mapper.AdamMemberPriceMapper;
import com.liquidnet.service.adam.util.MemberUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
@Service
public class AdamMemberCodeAdminServiceImpl extends ServiceImpl<AdamMemberCodeMapper, AdamMemberCode> implements IAdamMemberCodeAdminService {
    @Autowired
    AdamMemberCodeMapper adamMemberCodeMapper;
    @Autowired
    RedisDataSourceUtil redisDataSourceUtil;
    @Autowired
    AdamMemberMapper adamMemberMapper;
    @Autowired
    AdamMemberPriceMapper adamMemberPriceMapper;
    @Autowired
    AdamMemberCodeBatchMapper adamMemberCodeBatchMapper;

    @Override
    public boolean createGiftCode(AdamMemberCodeGenParam param) {
        String loginName = ShiroUtils.getLoginName();
        List<AdamMemberCode> memberCodeList = new ArrayList<>();
        ArrayList<String> codeList = CollectionUtil.arrayListString();
        Integer genNum = param.getGenNum();
        LocalDateTime now = LocalDateTime.now();
        String batchNo = IDGenerator.nextMilliId2();
        AbstractRedisUtil redisAdamUtil = redisDataSourceUtil.getRedisAdamUtil();
        for (int i = 0; i < genNum; i++) {
            String mCode;
            boolean exists;
            do {
                mCode = MemberUtil.freeCode();
                String key = AdamRedisConst.INFO_MEMBER_CODE.concat(mCode);
                exists = redisAdamUtil.hasKey(key);
            } while (exists);

            AdamMemberCode initMemberCode = new AdamMemberCode();
            initMemberCode.setCode(mCode);
            initMemberCode.setType(2);
            initMemberCode.setBatchNo(batchNo);
            initMemberCode.setMemberId(param.getMemberId());
            initMemberCode.setMemberPriceId(param.getMemberPriceId());
            // 需要主动生成会员编号，这里暂不生产，待数据记录成功同步RDS时生产
//            initMemberCode.setMemberNo(getNextMemberNo(param.getMemberId()));
            initMemberCode.setState(0);// 0-可用|1-已用
//            initMemberCode.setValidity(param.getValidity());
//            initMemberCode.setEffectAt(now);
//            initMemberCode.setExpireAt(now.plusDays(param.getValidity()).withHour(23).withMinute(59).withSecond(59));
            initMemberCode.setOperator(loginName);
            initMemberCode.setCreatedAt(now);

            memberCodeList.add(initMemberCode);
            codeList.add(mCode);

            //redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_MEMBER_CODE.concat(mCode), AdamMemberCodeVo.getNew().copy(initMemberCode));
        }

        AdamMember member = adamMemberMapper.selectOne(
                Wrappers.lambdaQuery(AdamMember.class).eq(AdamMember::getMemberId, param.getMemberId())
                        .select(AdamMember::getName)
        );
        AdamMemberPrice memberPrice = adamMemberPriceMapper.selectOne(
                Wrappers.lambdaQuery(AdamMemberPrice.class)
                        .eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId())
                        .eq(AdamMemberPrice::getMemberId, param.getMemberId())
                        .eq(AdamMemberPrice::getState, 1)
                        .select(AdamMemberPrice::getName)
        );
        AdamMemberCodeBatch memberCodeBatch = new AdamMemberCodeBatch();
        memberCodeBatch.setBatchNo(batchNo);
        memberCodeBatch.setMemberId(param.getMemberId());
        memberCodeBatch.setMemberPriceId(param.getMemberPriceId());
        memberCodeBatch.setMemberName(member.getName());
        memberCodeBatch.setMemberCombo(memberPrice.getName());
        memberCodeBatch.setGenNum(genNum);
        memberCodeBatch.setUseNum(0);
        memberCodeBatch.setDetail(param.getDetail());
        memberCodeBatch.setState(1);
        memberCodeBatch.setOperator(loginName);
        memberCodeBatch.setCreatedAt(now);

        if (adamMemberCodeBatchMapper.insert(memberCodeBatch) > 0 && this.saveBatch(memberCodeList, memberCodeList.size())) {
            List<AdamMemberCode> list = this.list(
                    Wrappers.lambdaQuery(AdamMemberCode.class)
                            .in(AdamMemberCode::getCode, codeList)
                            .eq(AdamMemberCode::getState, 0)
                            .eq(AdamMemberCode::getBatchNo, batchNo)
//                            .select(AdamMemberCode::getMid, AdamMemberCode::getCode)
            );
            for (AdamMemberCode memberCode : list) {
                memberCode.setMemberNo(getNextMemberNo(param.getMemberId()));

                redisAdamUtil.set(AdamRedisConst.INFO_MEMBER_CODE.concat(memberCode.getCode()), AdamMemberCodeVo.getNew().copy(memberCode));
            }
            this.updateBatchById(list, list.size());
            return true;
        }
        return false;
    }

    public String getNextMemberNo(String memberId) {
        long s = System.currentTimeMillis();
        int memberMaxNo = this.getMaxMemberNo();
        if (-1 == memberMaxNo) {
            // TODO: 2021/7/29 降级DB
            throw new LiquidnetServiceException();
//            if (RedisLockUtil.tryLock(LOCK_KEY_UMEMBER_NO, 1, 3)) {
//                memberMaxNo = adamRdmService.getMaxMemberNo();
//                if (-1 == memberMaxNo) {
////                    Query query = Query.query(Criteria.where("memberId").is(memberId)).with(Sort.by(Sort.Direction.DESC, "memberNo")).limit(1);
////
////                    AdamUserMemberVo latestMaxMemberNoVo = mongoTemplate.findOne(query, AdamUserMemberVo.class, AdamUserMemberVo.class.getSimpleName());
////
////                    if (null == latestMaxMemberNoVo) {
//                        AdamMemberVo memberVo = adamRdmService.getMemberVoByMemberId(memberId);
//
//                        adamRdmService.setMaxMemberNo(memberVo.getStartNo());
////                    } else {
////                        adamRdmService.setMaxMemberNo(Integer.parseInt(latestMaxMemberNoVo.getMemberNo()));
////                    }
//                }
//                RedisLockUtil.unlock(LOCK_KEY_UMEMBER_NO);
//            } else {
//                return null;
//            }
        }
        String nextMemberNoStr = String.valueOf(this.incrMemberNo());
        while (Pattern.matches("([\\d])\\1{" + (nextMemberNoStr.length() - 1) + "}", nextMemberNoStr)) {
            // 不能是完全相同的数字
            nextMemberNoStr = String.valueOf(this.incrMemberNo());
        }
        log.debug("#MNO耗时:{}ms", System.currentTimeMillis() - s);
        return nextMemberNoStr;
    }

    private int getMaxMemberNo() {
        Object o = redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.INCR_MEMBER_NO);
        return null == o ? -1 : (int) o;
    }

    public boolean setMaxMemberNo(int val) {
        return redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INCR_MEMBER_NO, val);
    }

    public int incrMemberNo() {
        return (int) redisDataSourceUtil.getRedisAdamUtil().incr(AdamRedisConst.INCR_MEMBER_NO, 1);
    }
}
