package com.yeejoin.amos.boot.module.jg.biz.service.impl;

import com.yeejoin.amos.boot.module.jg.api.dto.FlowingEquipRedisKeyDTO;
import com.yeejoin.amos.boot.module.jg.api.mapper.CommonMapper;
import com.yeejoin.amos.boot.module.jg.api.service.IEquipUsedCheck;
import com.yeejoin.amos.boot.module.jg.biz.context.FlowingEquipRedisContext;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Slf4j
public abstract class BaseEquipUsedCheckService implements IEquipUsedCheck {

    @Autowired
    CommonMapper commonMapper;

    /**
     * 并发校验（页面同时提交或者页面同时打开多个时，某些设备都是为使用状态）校验设备流程在用状态及更新不在用为再使用状态
     *
     * @param record      设备唯一标识
     * @param companyCode 登录人的公司代码
     */
    @Override
    public void equipRepeatUsedCheck(String record, String companyCode) {
        RLock lock = getRedisClient().getLock(this.getRepeatUsedCheckLockKey(companyCode, getApplyBizType(), record));
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            if (!isLocked) {
                log.error("设备：{}在被其他业务使用，加锁失败", record);
                throw new BadRequest("设备已被其他流程使用，请刷新后重试！");
            }
            RBucket<Set<String>> RBucket = getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()));
            Set<String> equipListOfUsed = RBucket.get();
            // 查询当前设备在流程中的申请单号列表
            List<String> equipInUseList = commonMapper.findEquipInUseService(record);
            if (equipListOfUsed != null && equipListOfUsed.contains(record)) {
                String joinedEquipInUse = String.join(",", equipInUseList);
                log.error("设备已被其他流程使用，不允许重复提交，设备ID:{}，在流程中申请单：{}", record, joinedEquipInUse);
                throw new BadRequest("设备已被其他流程使用，不允许重复提交，请联系管理员!");
            }
            if (equipListOfUsed == null || equipListOfUsed.isEmpty()) {
                equipListOfUsed = new TreeSet<>();
            }
            equipListOfUsed.add(record);
            getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType())).set(equipListOfUsed);
            FlowingEquipRedisContext.setRedisKeyInfo(new FlowingEquipRedisKeyDTO(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()), Collections.singletonList(record)));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    @Override
    public void registrationRepeatUsedCheck(String record, String companyCode) {
        RLock lock = getRedisClient().getLock(this.getRepeatUsedCheckLockKey(companyCode, getApplyBizType(), record));
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            if (!isLocked) {
                log.error("使用登记证：{}在被其他业务使用，加锁失败", record);
                throw new BadRequest("使用登记证已被其他流程使用，请刷新后重试！");
            }
            RBucket<Set<String>> RBucket = getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()));
            Set<String> equipListOfUsed = RBucket.get();
            // 查询当前设备在用的申请单号列表
            List<String> equipInUseList = commonMapper.findEquipInUseService(record);
            if (equipListOfUsed != null && equipListOfUsed.contains(record)) {
                String joinedEquipInUse = String.join(",", equipInUseList);
                log.error("使用登记证已被其他流程使用，不允许重复提交，设备ID:{}，在流程中申请单：{}", record, joinedEquipInUse);
                throw new BadRequest("使用登记证已被其他流程使用，不允许重复提交，请联系管理员!");
            }
            if (equipListOfUsed == null || equipListOfUsed.isEmpty()) {
                equipListOfUsed = new TreeSet<>();
            }
            equipListOfUsed.add(record);
            getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType())).set(equipListOfUsed);
            FlowingEquipRedisContext.setRedisKeyInfo(new FlowingEquipRedisKeyDTO(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()), Collections.singletonList(record)));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private String getRepeatUsedCheckLockKey(String companyCode, String bizType, String record) {
        return String.format("%s-%s-%s-%s", "JG_REPEAT_CHECK", companyCode, bizType, record);
    }

    String getFlowingEquipRedisKey(String companyCode, String type) {
        return String.format("%s:%s:%s", "EQUIP_IN_FLOWING", companyCode, type);
    }

    /**
     * 删除流程中的数据，释放redis空间（异常处理及驳回到发起节点、撤回到发起节点、完成时时进行）
     *
     * @param records  设备唯一标识
     * @param redisKey key
     */
    @Override
    public void delDataForCheckWithKey(List<String> records, String redisKey) {
        delRedisData(records, redisKey, getRedisClient());
    }

    @Override
    public Set<String> getEquipInFlow(String companyCode) {
        RBucket<Set<String>> rBucket = getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()));
        return rBucket.get();
    }

    /**
     * 删除流程中的数据，释放redis空间（异常处理及驳回到发起节点、撤回到发起节点、完成时时进行）
     *
     * @param records     设备唯一标识
     * @param companyCode 登录人的公司代码
     */
    @Override
    public void delDataForCheckEquipRepeatUsed(List<String> records, String companyCode) {
        RBucket<Set<String>> rBucket = getRedisClient().getBucket(this.getFlowingEquipRedisKey(companyCode, getApplyBizType()));
        Set<String> equipListOfUsed = rBucket.get();
        if(equipListOfUsed != null){
            equipListOfUsed = equipListOfUsed.stream().filter(record -> !records.contains(record)).collect(Collectors.toSet());
            rBucket.set(equipListOfUsed);
        }
    }


    private void delRedisData(List<String> records, String redisKey, RedissonClient redissonClient) {
        RBucket<Set<String>> rBucket = redissonClient.getBucket(redisKey);
        Set<String> equipListOfUsed = rBucket.get();
        if(equipListOfUsed != null){
            equipListOfUsed = equipListOfUsed.stream().filter(record -> !records.contains(record)).collect(Collectors.toSet());
            rBucket.set(equipListOfUsed);
        }
    }

    public abstract RedissonClient getRedisClient();

    public abstract String getApplyBizType();

    @Override
    public String applyBizType() {
        return getApplyBizType();
    }

}
