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

import com.yeejoin.amos.boot.module.jg.api.dto.CompanyEquipCountDto;
import com.yeejoin.amos.boot.module.jg.api.mapper.JgUseRegistrationMapper;
import com.yeejoin.amos.boot.module.jg.api.service.IEquipUsedCheck;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Component
@Slf4j
public class UseRegisterEquipUsedCheckImpl implements IEquipUsedCheck {

    private RedissonClient redissonClient;

    private String bizType = "useRegister";

    private JgUseRegistrationMapper useRegistrationMapper;

    public UseRegisterEquipUsedCheckImpl(RedissonClient redissonClient, JgUseRegistrationMapper useRegistrationMapper) {
        this.redissonClient = redissonClient;
        this.useRegistrationMapper = useRegistrationMapper;
    }

    /**
     * 并发校验（页面同时提交或者页面同时打开多个时，某些设备都是为使用状态）校验设备流程在用状态及更新不在用为再使用状态
     *
     * @param record      设备唯一标识
     * @param companyCode 登录人的公司代码
     */
    @Override
    public void equipRepeatUsedCheck(String record, String companyCode) {
        RLock lock = redissonClient.getLock(this.getRepeatUsedCheckLockKey(companyCode, bizType, record));
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            if (!isLocked) {
                log.error("设备：{}在被其他业务使用，加锁失败", record);
                throw new BadRequest("存在其他业务在使用设备，请刷新后重试！");
            }
            RBucket<Set<String>> RBucket = redissonClient.getBucket(this.getFlowingEquipRedisKey(companyCode, bizType));
            Set<String> equipListOfUsed = RBucket.get();
            if (equipListOfUsed != null && equipListOfUsed.contains(record)) {
                throw new BadRequest("存在设备正在同时被其他流程使用，请刷新后重试！");
            }
            if (equipListOfUsed == null || equipListOfUsed.isEmpty()) {
                equipListOfUsed = new TreeSet<>();
            }
            equipListOfUsed.add(record);
            redissonClient.getBucket(this.getFlowingEquipRedisKey(companyCode, bizType)).set(equipListOfUsed);
        } 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);
    }

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

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

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

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

    @Override
    public void init() {
        List<CompanyEquipCountDto> companyEquipCountDtos = useRegistrationMapper.queryForFlowingEquipList();
        companyEquipCountDtos.forEach(c -> {
            RBucket<Set<String>> rBucket = redissonClient.getBucket(this.getFlowingEquipRedisKey(c.getCompanyCode(), bizType));
            rBucket.set(Arrays.stream(c.getRecords().split(",")).collect(Collectors.toSet()));
        });
    }

    @Override
    public void delDataForCheckWithKey(List<String> records, String redisKey) {
        delRedisData(records, redisKey, redissonClient);
    }

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