package com.brilliance.isc.counter.service.impl;

import com.brilliance.isc.counter.config.CounterConfig;
import com.brilliance.isc.counter.mapper.CounterMapperOfCou;
import com.brilliance.isc.counter.service.CounterService;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("dbCounter")
public class DBCounter implements CounterService {

    Logger logger = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    public CounterMapperOfCou counterMapper;

    @Autowired
    private CounterConfig config;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 事务传播设置为REQUIRES_NEW，减少获取ID锁等待对外部事务影响
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public long idGenForUpdate(String key) {
        int start = config.getStart();
        int step = config.getStep();

        Long curr = counterMapper.selectCurrentForUpdate(key);
        logger.info("{}\t{}\t{}\tforupdate记录锁", Thread.currentThread().getName(), key, curr != null);
        if (curr == null) {
            int cnt = counterMapper.updateCounter(couPrefix, 1);
            logger.trace("{}\t{}\t{}\t获取全局锁", Thread.currentThread().getName(), key, cnt > 0);
            curr = counterMapper.selectCurrentForUpdate(key);
            if(curr == null){
                logger.trace("{}\t{}\t{}\tforupdate记录锁2", Thread.currentThread().getName(), key, curr == null);
                this.counterMapper.insertNewCounter(key, start + step);
                logger.info("{}\t{}\t{}", Thread.currentThread().getName(), key, "insertNewCounter");
                return start;
            }
        }
        this.counterMapper.updateCounter(key, step);
        logger.debug("{}\t{}\t{}\t更新返回COU", Thread.currentThread().getName(), key, curr);
        return curr;

    }

    /**
     * mysql-rc,oracle: true
     * mysql-rr: deadlock (RR级别下为解决幻读问题，在updata未命中数据时会产生gap锁，
     * 多个事务可以对相同区间添加gap锁，但当其中某个事务进行插入数据时，需要获取该行的记录锁，多线程并发时就会产生死锁)
     * @param key
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public long idGenUpdate(String key) {
        int start = config.getStart();
        int step = config.getStep();

        int cnt = this.counterMapper.updateCounter(key, step);
        logger.trace("{}\t{}\t{}\t获取记录锁", Thread.currentThread().getName(), key, cnt > 0);
        if (cnt == 0) {
            cnt = counterMapper.updateCounter(couPrefix, 1);
            logger.trace("{}\t{}\t{}\t获取全局锁", Thread.currentThread().getName(), key, cnt > 0);
            cnt = this.counterMapper.updateCounter(key, step);
            if (cnt == 0) {
                logger.trace("{}\t{}\t{}\t获取记录锁2", Thread.currentThread().getName(), key, cnt > 0);
                this.counterMapper.insertNewCounter(key, start + step);
                logger.trace("{}\t{}\t{}\t插入返回COU", Thread.currentThread().getName(), key, start);
                return start;
            }
        }

        Long val = counterMapper.selectCurrent(key);
        logger.debug("{}\t{}\t{}\t更新返回COU", Thread.currentThread().getName(), key, val-step);
        return val-step;

    }

    /**
     * oracle mysql-rc: true
     * mysql-rr: false
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public long idGenError(String key) {
        int start = config.getStart();
        int step = config.getStep();

        Long curr = counterMapper.selectCurrentForUpdate(key);
        logger.info("{}\t{}\t{}\t获取记录锁", Thread.currentThread().getName(), key, curr != null);
        if (curr == null) {
            try {
                this.counterMapper.insertNewCounter(key, start + step);
                logger.info("{}\t{}\t{}\t插入成功返回", Thread.currentThread().getName(), key, start);
                return start;
            } catch (DuplicateKeyException se) {
                logger.error("{}\t{}\t{}\t插入主键冲突", Thread.currentThread().getName(), key, " ");
                curr = counterMapper.selectCurrentForUpdate(key);
                logger.info("{}\t{}\t{}\t获取记录锁2", Thread.currentThread().getName(), key, curr != null);
            }
        }
        counterMapper.updateCounter(key, step);
        logger.info("{}\t{}\t{}\t更新返回COU", Thread.currentThread().getName(), key, curr);
        return curr;
    }

    public long idGen(String key) {
        return ((DBCounter)applicationContext.getBean("dbCounter")).idGenError(key);
    }
}

