package com.tax.file.service.async.down;

import com.tax.file.constants.FileConstants;
import com.tax.file.context.FileDownContext;
import com.tax.file.model.DownFileModel;
import com.tax.file.model.TkTaxUser;
import com.tax.file.mq.rabbit.producer.RabbitMQMessageProducer;
import com.tax.file.service.api.FileTokenService;
import com.tax.file.service.async.ExecuteMqService;
import com.tax.file.service.async.apply.util.ApplyUtil;
import com.yxcheng.token.common.cache.RedisService;
import com.yxcheng.token.common.model.MqMessage;
import com.yxcheng.token.constants.RedisContants;
import com.yxcheng.token.utils.DateUtils;
import com.yxcheng.token.utils.StringUtil;
import com.yxcheng.token.utils.UUIDGenerator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;

@Component
public class AsyncFileDownService extends AbstractFileDownService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncFileDownService.class);

    @Autowired
    private RedisService redisService;
    @Autowired
    private RabbitMQMessageProducer producer;
    @Autowired
    private FileDownQueryService fileDownQueryService;
    @Autowired
    private FileTokenService fileTokenService;


    @Async("myExecutor")
    public Future<String> doDownFile(String uuidFlag, CountDownLatch latch, TkTaxUser taxUser){
        try{
            DownFileModel model = new DownFileModel();
            model.setTaxUser(taxUser);

            if(FileConstants.isTest()){
                execute(model);
            }else{
                producer.sendDownFileTopic(model, taxUser.getPublishNo());
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.info(uuidFlag + " doDownFile is error!" + e.getMessage());
        } finally {
            latch.countDown();
        }
        return new AsyncResult<>("TkTaxUser="+taxUser.getTaxno()+" accomplished!");
    }


    public void finish(String userName, String taxno) {
        redisService.set(RedisContants.getLimitName(RedisContants.FILE_APPLY_LOCK, userName, taxno),"0",5);
    }

    public boolean isExecuting(String uuidFlag, String userName, String taxno) {
        boolean execFlag = false;
        try {
            if("1".equals(redisService.getKey(RedisContants.getLimitName(RedisContants.FILE_APPLY_LOCK, userName, taxno)))) {
                execFlag = true;
            } else {
                redisService.set(RedisContants.getLimitName(RedisContants.FILE_APPLY_LOCK, userName, taxno),"1",60*60);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(uuidFlag + " taxno is " + taxno + " operation redis is error!" + e.getMessage());
            //redis连接不上认为是运行中
            execFlag = true;
        }
        return execFlag;
    }

    @Async("downExecutor")
    public Future<String> execute(MqMessage mqMessage) {
        DownFileModel model = (DownFileModel)mqMessage;
        String uuidFlag = UUIDGenerator.generate();
        Instant start = Instant.now();

        Date reqDate = DateUtils.getDate();
        String result = "下载成功";
        TkTaxUser taxUser = model.getTaxUser();
        String userName = taxUser.getUserName();
        String taxno = taxUser.getTaxno();
        String threadName = Thread.currentThread().getName();

        logger.info(uuidFlag + " taxno " + taxno + " download file is start.线程名称" + threadName);

        if(isExecuting(uuidFlag, userName, taxno)) {
            result = "运行中";
            logger.info(uuidFlag + " taxno is "+ taxno + " download file is failure.The taxno is executing.");
        } else {
            try{
                //下载失败超次数，不需要下载
                if (!timesLimitStrategy.isAccept(RedisContants.getLimitName(RedisContants.DOWN_FILE_TIMES_LOCK, taxUser.getUserName(), taxUser.getTaxno()))) {
                    result = "下载异常超过最大次数";
                    logger.info(uuidFlag + " taxno " + taxUser.getTaxno() + " download file request times is exceeding,it isn't run.");
                } else if(!timesLimitStrategy.isAccept(RedisContants.getLimitName(RedisContants.FILE_TOKEN_LOCK, taxUser.getUserName(), taxUser.getTaxno()),ApplyUtil.tokenTimes)) {
                    result = "token超过最大次数";
                    logger.info(uuidFlag + " taxno "+taxUser.getTaxno() + " download file is failure.The token over times.");
                } else {
                    FileDownContext context = new FileDownContext();

                    String token = fileTokenService.getTokenCode(taxUser);
                    if(StringUtils.isBlank(token) || !fileTokenService.checkToken(uuidFlag, taxUser, token)){
                        result = "token无效";
                        timesLimitStrategy.addTime(RedisContants.getLimitName(RedisContants.FILE_TOKEN_LOCK, taxUser.getUserName(), taxUser.getTaxno()),30*60, ApplyUtil.tokenTimes);
                        logger.info(uuidFlag + " taxno "+taxUser.getTaxno() + " download file is failure.The taxno isn't get token.");
                    } else {
                        context.setToken(token);
                        model.setContext(context);

                        String applyDateEnd = DateUtils.date2Str(DateUtils.getDate(), new SimpleDateFormat("yyyy-MM-dd"));
                        String applyDateStart = DateUtils.date2Str(DateUtils.addDate(DateUtils.getDate(), -14), new SimpleDateFormat("yyyy-MM-dd"));

                        boolean saveFlag = fileDownQueryService.runDownloadFileList(uuidFlag, model, context, applyDateStart, applyDateEnd, taxUser);
                        if(saveFlag){
                            tkTaxUserService.updateDownloadUserInfo(uuidFlag, taxUser, "1");
                        } else {
                            result = " 下载失败";
                            setDownNum(taxUser.getUserName(), taxUser.getTaxno());
                        }

                    }

                }
            }catch (Exception e){
                result = "运行异常" + StringUtil.getShortLog(e.getMessage(), 200);
                e.printStackTrace();
                timesLimitStrategy.addTime(RedisContants.getLimitName(RedisContants.DOWN_FILE_TIMES_LOCK, taxUser.getUserName(), taxUser.getTaxno()));
                logger.info(uuidFlag + " taxno " + taxno + " download file is failure.The error is " + e.getMessage());
            }finally {
                finish(userName, taxno);
            }
        }
        //统计耗时
        printEndLog(uuidFlag, taxUser.getTaxno(), start);
        fileLogService.saveLog(uuidFlag, taxUser.getUserName(), taxUser.getTaxno(), reqDate, taxUser.getTaxno(), "download file " + result, new Date(), "1");
        return new AsyncResult<>("taxNo="+taxno+" accomplished!");
    }

    public void printEndLog(String uuidFlag, String taxno, Instant start) {
        //统计耗时
        Instant end = Instant.now();
        long duration = end.toEpochMilli() - start.toEpochMilli();
        logger.info(uuidFlag + " taxno "+ taxno + " download file is end.cost is {} ms!",duration);
    }

    public void setDownNum(String userName, String taxno) {
        timesLimitStrategy.addTime(RedisContants.getLimitName(RedisContants.DOWN_FILE_TIMES_LOCK, userName, taxno));
    }

}
