package com.tax.token.job;

import com.tax.token.constants.TokenConstants;
import com.tax.token.job.dto.TokenAdjustPriorityDto;
import com.tax.token.model.TkToken;
import com.tax.token.model.TkTokenExample;
import com.tax.token.service.api.TkTokenService;
import com.yxcheng.token.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @ProjectName: tax-sys
 * @Package: com.tax.token.job
 * @ClassName: SynGetTokenTask
 * @Author: ddx
 * @Description: ${description}
 * @Date: 2022-05-16 18:53
 * @Version: 1.0
 */
@Slf4j
@Component
public class SynGetTokenTask {
    @Autowired
    private TkTokenService tokenService;
    @Autowired
    private SynTokenService synTokenService;

    /**
     * get token list to process.
     * @param totalCount
     * @param item
     * @return
     */
    public List<TkToken> getTokenList(int totalCount, int item, int limit) {
        TkTokenExample tokenExample = new TkTokenExample();
        tokenExample.createCriteria()
                .andTokenStatusEqualTo(TokenConstants.TOKEN_ON_THE_WAY)
                .andTimesLessThanOrEqualTo(TokenConstants.TOKEN_RETRY_TIMES)
                .andIsValidEqualTo(TokenConstants.TOKEN_VALID)
                .andUserTypeEqualTo(TokenConstants.USER_TYPE_NORMAL)
                .andIdModEqual(totalCount, item);
        tokenExample.setOrderByClause("times asc");
        tokenExample.setLimitClause(String.valueOf(limit));
        List<TkToken> tokenList = tokenService.selectByExample(tokenExample);
        return tokenList;
    }

    public List<TkToken> getBehindTokenList(TokenAdjustPriorityDto tokenAdjustPriorityDto) {
        log.info("delayHours:{}", tokenAdjustPriorityDto.getDelayHours());
        log.info("validHours:{}", tokenAdjustPriorityDto.getValidHours());
        log.info("limit:{}", tokenAdjustPriorityDto.getLimit());
        TkTokenExample tokenExample = new TkTokenExample();
        tokenExample.createCriteria()
                .andTokenStatusEqualTo(TokenConstants.TOKEN_ON_THE_WAY)
                .andTimesGreaterThan(TokenConstants.TOKEN_RETRY_TIMES_START)
                .andTimesLessThanOrEqualTo(TokenConstants.TOKEN_RETRY_TIMES)
                .andIsValidEqualTo(TokenConstants.TOKEN_VALID)
                .andReqDateGreaterThanOrEqualTo(DateUtils.getDateMoveHour(tokenAdjustPriorityDto.getValidHours()))
                .andReqDateLessThanOrEqualTo(DateUtils.getDateMoveHour(tokenAdjustPriorityDto.getDelayHours()));
        tokenExample.setLimitClause(String.valueOf(tokenAdjustPriorityDto.getLimit()));
        List<TkToken> tokenList = tokenService.selectByExample(tokenExample);
        return tokenList;
    }

    public void adjustPriorityToken(List<TkToken> tokenList) {
        for (TkToken tkToken : tokenList) {
            log.info("adjust priority token for taxNo:{}", tkToken.getTaxno());
            // times with 0 is the highest priority
            tkToken.setTimes(TokenConstants.TOKEN_RETRY_TIMES_START);
            // return to HTTP request process
            tkToken.setTokenStatus(TokenConstants.TOKEN_INIT);
            // refresh lively
            tkToken.setReqDate(new Date());
            tokenService.updateTokenTimes(tkToken);
        }
    }

    public int countToken() {
        TkTokenExample tokenExample = new TkTokenExample();
        tokenExample.createCriteria()
                .andTokenStatusEqualTo(TokenConstants.TOKEN_ON_THE_WAY)
                .andTimesLessThanOrEqualTo(TokenConstants.TOKEN_RETRY_TIMES)
                .andUserTypeEqualTo(TokenConstants.USER_TYPE_NORMAL)
                .andIsValidEqualTo(TokenConstants.TOKEN_VALID);
        int count = tokenService.countToken(tokenExample);
        log.info("token count:{}", count);
        return count;
    }

    /**
     * process list
     * @param uuidFlag
     * @param tokenList
     */
    public void asyncRunToken(String uuidFlag, List<TkToken> tokenList) {
        Instant start = Instant.now();
        log.info("{} async run token start", uuidFlag);
        try {
            if (CollectionUtils.isNotEmpty(tokenList)) {
                int total = tokenList.size();
                CountDownLatch latch = new CountDownLatch(total);
                for (TkToken tkToken : tokenList) {
                    synTokenService.asyncTask(latch, uuidFlag, tkToken);
                }
                log.info("{} async run token exec num: {}", uuidFlag, latch.getCount());
                latch.await();
            }
        } catch (InterruptedException e) {
            log.error("{} async run token is error:", uuidFlag, e);
        }
        Instant end = Instant.now();
        long duration = end.toEpochMilli() - start.toEpochMilli();
        log.info("{} async run token finished, elapsed: {} ms", uuidFlag, duration);
    }

    public void syncRunToken(String uuidFlag, List<TkToken> tokenList) {
        Instant start = Instant.now();
        log.info("{} sync run token start", uuidFlag);
        try {
            if (CollectionUtils.isNotEmpty(tokenList)) {
                for (TkToken tkToken : tokenList) {
                    synTokenService.handleToken(uuidFlag, tkToken);
                }
            }
        } catch (Exception e) {
            log.error("{} sync run token is error:", uuidFlag, e);
        }
        Instant end = Instant.now();
        long duration = end.toEpochMilli() - start.toEpochMilli();
        log.info("{} sync run token finished, elapsed: {} ms", uuidFlag, duration);
    }
}
