package com.tax.token.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tax.token.constants.TokenConstants;
import com.tax.token.mapper.TkTokenMapper;
import com.tax.token.model.*;
import com.tax.token.service.api.*;
import com.tax.token.service.websocket.NioWebSocketClient;
import com.tax.token.service.websocket.WebSocketClientService;
import com.tax.token.thirdService.TokenThirdServiceFactory;
import com.tax.token.thirdService.impl.SignThirdSJService;
import com.tax.token.utils.CleanCookieUtil;
import com.tax.token.utils.EncryptUtil;
import com.tax.token.utils.NationUtil;
import com.tax.token.utils.TokenUtil;
import com.yxcheng.tax.mq.rabbit.producer.RabbitMQMessageProducer;
import com.yxcheng.token.common.cache.RedisService;
import com.yxcheng.token.common.enumutil.ResultEnum;
import com.yxcheng.token.common.model.JsonResult;
import com.yxcheng.token.common.model.TaxUserInfo;
import com.yxcheng.token.constants.RedisContants;
import com.yxcheng.token.utils.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;

@Transactional
@Component
@ComponentScan(basePackages = {"com.tax.token.dao", "com.tax.token.integration", "com.yxcheng.tax.mq","com.yxcheng.token"})
public class TkTokenServiceImpl implements TkTokenService {

    private static final Logger logger = LoggerFactory.getLogger(TkTokenServiceImpl.class);
    @Autowired
    private TkTokenMapper tkTokenMapper;

    @Autowired
    private TkZoneInfoService tkZoneInfoService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TkTokenLogService tokenLogService;

    @Autowired
    private TkTokenRemarkLogService tkTokenRemarkLogService;

    @Autowired
    private TkRealTokenLogService tkRealTokenLogService;

    @Autowired
    private TkServerService tkServerService;

    @Autowired
    private RabbitMQMessageProducer producer;

    @Autowired
    private SignThirdSJService signThirdSJService;

    @Autowired
    private NioWebSocketClient webSocketClient;

    @Autowired
    private TkUserService tkUserService;

    public List<TkToken> selectByExample(TkTokenExample example) {
        return tkTokenMapper.selectByExample(example);
    }

    public int countToken(TkTokenExample example) {
        return tkTokenMapper.countByExample(example);
    }

    @Override
    public JsonResult<String> getToken(String taxno, String zoneCode, String userCode, boolean isEncrypt, String functionNo) {

        String uuId = UUIDGenerator.generate();
        JsonResult result = new JsonResult();
        try {
            String times = "0";
            String token = null;
            //获取地区信息
            TkZoneInfoExample zoneInfoExample = new TkZoneInfoExample();
            zoneInfoExample.createCriteria().andZoneCodeEqualTo(zoneCode);

            List<TkZoneInfo> tkZoneInfoList = tkZoneInfoService.selectByExample(zoneInfoExample);
            if(tkZoneInfoList == null || tkZoneInfoList.size() == 0){
                //地区信息不存在
                return result.errorResponse(ResultEnum.zone_not_exist);
            }
            TkZoneInfo tkZoneInfo = tkZoneInfoList.get(0);
            this.synZoneInfo(tkZoneInfo);

            TkTokenExample example = new TkTokenExample();
            example.createCriteria().andTaxnoEqualTo(taxno).andZoneCodeEqualTo(zoneCode).andUserCodeEqualTo(userCode);
            List<TkToken> tkTokenList  = tkTokenMapper.selectByExample(example);

            if(tkTokenList!=null && tkTokenList.size()>0){
                TkToken tkToken = tkTokenList.get(0);

                if(!tkToken.getIsValid().equals("1")) {
                    if(StringUtil.isNotEmpty(tkToken.getErrorCode()) && tkToken.getErrorCode().equals("4007")) {
                        result.errorResponse(ResultEnum.user_taxno_fpy_disable, tkToken.getErrorCode(), tkToken.getErrorMessage(), tkToken.getRetrySecond());
                    } else {
                        result.errorResponse(ResultEnum.user_taxno_temp_disable, tkToken.getErrorCode(), tkToken.getErrorMessage(), tkToken.getRetrySecond());
                    }
                } else if(checkTokenRetrySecond(userCode, taxno)) {
                    result.errorResponse(ResultEnum.user_taxno_waiting, tkToken.getErrorCode(), tkToken.getErrorMessage(), tkToken.getRetrySecond());
                } else {
                    tkToken.setReqDate(DateUtils.getDate());
                    TkUser tkUser = tkUserService.getUserByUserCode(userCode, functionNo);
                    if(TokenConstants.TOKEN_ON_THE_WAY.equals(tkToken.getTokenStatus())){
                        if(tkToken.getTimes() > TokenConstants.TOKEN_RETRY_TIMES){
                            tkToken.setTimes(0);
                            tkToken.setTokenStatus("0");
                            updateReqTokenInfo(uuId, tkToken);
                            tokenLogService.saveTokenLog(uuId, tkToken, "3");
                            result.errorResponse(ResultEnum.token_not_obtained);
                        } else {
                            result.errorResponse(ResultEnum.token_not_obtained, tkToken.getErrorCode(), tkToken.getErrorMessage(), tkToken.getRetrySecond());
                        }
                        return result;
                    }
                    times = redisService.get(RedisContants.TOKEN_REQ_TIMES + taxno + ":" + userCode);
                    if(StringUtils.isBlank(times)){
                        times = "0";
                    }

                    //5分钟内直接返回并且请求次数不大于5次
                    boolean cacheFlag = true;
                    if(!"1".equals(tkUser.getForceGetToken()) && DateUtils.isInTheTime(new Date(),tkToken.getResDate(),
                            TokenConstants.TOKEN_VALID_TIME) && ObjectUtil.isNotBigger(times, TokenConstants.REQ_MAX_TIMES)
                            && StringUtils.isNotEmpty(tkToken.getToken()) && tkToken.getTokenStatus().equals("1")){
                        token = tkToken.getToken();
                        cacheFlag = false;
                        redisService.set(RedisContants.TOKEN_REQ_TIMES + taxno + ":" + userCode,String.valueOf(Integer.valueOf(times) + 1),5*60);
                    } else {
                        TkServerExample serverExample = new TkServerExample();
                        serverExample.createCriteria().andServerNoEqualTo(tkToken.getServerNo());
                        TkServer tkServer = tkServerService.selectByExample(serverExample).get(0);
                        //isSyn等于1时，第一次请求走异步请求
                        if("1".equals(tkUser.getIsSyn())) {
                            tkToken.setTokenStatus(TokenConstants.TOKEN_ON_THE_WAY);
                        }else{
                            tkToken.setZoneFlag(tkUser.getZoneFlag());
                            tkToken.setFunctionNo(functionNo);
                            token = getTokenByThird(uuId, tkToken, tkZoneInfo, tkServer,true, "0");
                        }
                        if(ObjectUtil.isBigger(times, TokenConstants.REQ_MAX_TIMES)){
                            //大于5次后需要重置计数
                            redisService.set(RedisContants.TOKEN_REQ_TIMES + taxno + ":" + userCode,"0",5*60);
                        }
                    }
                    if (StringUtils.isBlank(token)) {
                        result.errorResponse(ResultEnum.token_not_obtained, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
                        if(StringUtil.isNotEmpty(tkToken.getTokenCode())) {
                            if(getStopFlag(tkToken.getUserCode(), tkToken.getTokenCode(), tkToken.getUserType(), tkToken.getTokenStatus())) {
                                if(tkToken.getTokenCode().equals("4007")) {
                                    result.errorResponse(ResultEnum.user_taxno_fpy_disable, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
                                } else {
                                    result.errorResponse(ResultEnum.user_taxno_temp_disable, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
                                }
                            } else if(tkToken.getTokenCode().equals("8000")) {
                                result.errorResponse(ResultEnum.user_taxno_waiting, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
                            }
                        }
                    }

                    if (cacheFlag) {
                        updateTokenInfo(uuId, tkToken);
                    }

                    if(StringUtil.isNotEmpty(token)) {
                        if(null!=tkUser.getJsonFlag() && tkUser.getJsonFlag().equals("1")) {
                            JSONObject json = new JSONObject();
                            json.put("cookie", token);
                            json.put("proxyIp", tkToken.getProxyIp());
                            json.put("session", tkToken.getSecretMd5());
                            json.put("requestId", tkToken.getRequestId());
                            json.put("userGroupId", (StringUtil.isNotEmpty(tkToken.getTaxLoginName())?MD5Util.MD5Encode(tkToken.getTaxLoginName()):""));
                            json.put("userGroupName", (StringUtil.isNotEmpty(tkToken.getTaxLoginName())?tkToken.getTaxLoginName():""));
                            if(isEncrypt && (null!=tkUser.getShowFlag() && tkUser.getShowFlag().equals("1"))) {
                                result.setData(EncryptUtil.encryptAesCk(json.toJSONString()));
                            } else {
                                result.setData(json);
                            }
                        } else {
                            if(isEncrypt && (null!=tkUser.getShowFlag() && tkUser.getShowFlag().equals("1"))) {
                                result.setData(EncryptUtil.encryptAesCk(token));
                            } else {
                                result.setData(token);
                            }
                        }

                    }
                    tokenLogService.saveTokenLog(uuId, tkToken, "0");
                    //记录问题数据日志
                    tkTokenRemarkLogService.saveRemarkLog(uuId, tkToken);
                }
            } else {
                result.errorResponse(ResultEnum.user_taxno_not_exist);
            }
        } catch (Exception e) {
            e.printStackTrace();
            result.errorResponse(ResultEnum.system_error);
            logger.error(uuId + " getToken taxno is " + taxno + " is error:" + e.getMessage());
        }
        return result;
    }

    @Override
    public JsonResult<String> getRealCookie(String uuId, String taxno, String zoneCode, String userCode, boolean showData,
                                            String functionNo, TaxUserInfo taxUserInfo, String checkType, String site, String target) {

        JsonResult result = new JsonResult();
        String token = null;

        //获取地区信息
        TkZoneInfoExample zoneInfoExample = new TkZoneInfoExample();
        zoneInfoExample.createCriteria().andZoneCodeEqualTo(zoneCode);

        List<TkZoneInfo> tkZoneInfoList = tkZoneInfoService.selectByExample(zoneInfoExample);
        if(tkZoneInfoList == null || tkZoneInfoList.size() == 0){
            //地区信息不存在
            return result.errorResponse(ResultEnum.zone_not_exist);
        }
        TkZoneInfo tkZoneInfo = tkZoneInfoList.get(0);
        this.synZoneInfo(tkZoneInfo);

        TkUser tkUser = tkUserService.getUserByUserCode(userCode, functionNo);

        TkToken tkToken = new TkToken();
        tkToken.setZoneFlag(tkUser.getZoneFlag());
        tkToken.setTaxno(taxno);
        tkToken.setServerNo(tkUser.getServerNo());
        tkToken.setReqDate(DateUtils.getDate());
        tkToken.setZoneCode(zoneCode);
        tkToken.setUserCode(userCode);
        if(StringUtil.isNotEmpty(checkType)) {
            tkToken.setUserType(checkType);
        } else {
            tkToken.setUserType("1");
        }
        tkToken.setFunctionNo(functionNo);
        tkToken.setTaxUserInfo(taxUserInfo);
        tkToken.setSite(site);
        tkToken.setTarget(target);

        TkServerExample serverExample = new TkServerExample();
        serverExample.createCriteria().andServerNoEqualTo(tkToken.getServerNo());
        TkServer tkServer = tkServerService.selectByExample(serverExample).get(0);

        token = getTokenByThird(uuId, tkToken,tkZoneInfo,tkServer,true, "2");

        boolean updFlag = false;
        if(StringUtils.isBlank(token)){
            result.errorResponse(ResultEnum.token_not_obtained, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
            if(StringUtil.isNotEmpty(tkToken.getTokenCode())) {
                if(getCountFlag(tkToken.getTokenCode())) {
                    result.errorResponse(ResultEnum.taxno_status_middle, tkToken.getTokenCode(), tkToken.getTokenMessage(), tkToken.getRetrySecond());
                }
                updFlag = true;
            }
        } else {
            updFlag = true;
        }
        if (updFlag) {
            TkToken updToken  = queryToken(taxno, zoneCode, userCode);
            if(null!=updToken) {
                tkToken.setId(updToken.getId());
                tkToken.setTimes(updToken.getTimes());
                updateTokenInfo(uuId, tkToken);
                //记录问题数据日志
                tkTokenRemarkLogService.saveRemarkLog(uuId, tkToken);
            }
        }
        if(showData) {
            if(StringUtil.isNotEmpty(token)) {
                if(null!=tkUser.getJsonFlag() && tkUser.getJsonFlag().equals("1")) {
                    JSONObject json = new JSONObject();
                    json.put("cookie", token);
                    json.put("proxyIp", tkToken.getProxyIp());
                    json.put("session", tkToken.getSecretMd5());
                    json.put("requestId", tkToken.getRequestId());
                    json.put("userGroupId", (StringUtil.isNotEmpty(tkToken.getTaxLoginName())?MD5Util.MD5Encode(tkToken.getTaxLoginName()):""));
                    json.put("userGroupName", (StringUtil.isNotEmpty(tkToken.getTaxLoginName())?tkToken.getTaxLoginName():""));
                    if(null!=tkUser.getShowFlag() && tkUser.getShowFlag().equals("1")) {
                        result.setData(EncryptUtil.encryptAesCk(json.toJSONString()));
                    } else {
                        result.setData(json);
                    }
                } else {
                    if(null!=tkUser.getShowFlag() && tkUser.getShowFlag().equals("1")) {
                        result.setData(EncryptUtil.encryptAesCk(token));
                    } else {
                        result.setData(token);
                    }
                }
            }
        }
        logger.info(uuId + " getRealCookie is " + result.toString());
        tkRealTokenLogService.saveRealTokenLog(uuId, tkToken, "0");
        return result;
    }

    public boolean updateTokenFrom(String uuId, CookieReq cookieReq, JsonResult<String> jsonResult) {
        boolean isEle = false;
        String userCode = cookieReq.getAccessKeyID();
        if(userCode.equals("youqian") && jsonResult.getMessage().contains("4007")) {
            String taxno = cookieReq.getTaxno();
            String zoneCode = cookieReq.getZoneCode();
            TkToken oldToken  = queryToken(taxno, zoneCode, userCode);
            String saveFlag = "0";
            if(null!=oldToken) {
                TkToken tkToken = new TkToken();
                tkToken.setTaxno(taxno);
                tkToken.setSecert(cookieReq.getUserInfo());
                tkToken.setZoneCode(zoneCode);
                tkToken.setUserCode("e" + userCode);
                tkToken.setUserType("2");
                tkToken.setIsValid("1");
                insertToken(oldToken);
                saveFlag = "1";
            }
            isEle = true;
            logger.info(uuId + " getRealCookie updateTokenFrom is success!saveFlag:" + saveFlag);
        }
        return isEle;
    }

    public TkToken queryToken(String taxno, String zoneCode, String userCode) {
        TkToken updToken = null;
        TkTokenExample example = new TkTokenExample();
        example.createCriteria().andTaxnoEqualTo(taxno).andZoneCodeEqualTo(zoneCode).andUserCodeEqualTo(userCode);
        List<TkToken> tkTokenList  = tkTokenMapper.selectByExample(example);
        if(tkTokenList!=null && tkTokenList.size()>0) {
            updToken = tkTokenList.get(0);
        }
        return updToken;
    }

    @Override
    public JsonResult<String> updateErrorInfo(PushTokenReq pushTokenReq) {
        String uuId = UUIDGenerator.generate();
        JsonResult result = new JsonResult();
        TkTokenExample example = new TkTokenExample();
        example.createCriteria().andTaxnoEqualTo(pushTokenReq.getTaxno()).andUserCodeEqualTo(pushTokenReq.getUserCode());
        List<TkToken> tkTokenList  = tkTokenMapper.selectByExample(example);

        if(tkTokenList!=null && tkTokenList.size()>0){
            TkToken tkToken = tkTokenList.get(0);
            if(tkToken.getIsValid().equals("0")) {
                result.errorResponse(ResultEnum.user_taxno_temp_disable);
            } else {
                tkToken.setTokenStatus(TokenConstants.TOKEN_ON_THE_WAY);
                tkToken.setTokenCode(pushTokenReq.getCode());
                tkToken.setTokenMessage(pushTokenReq.getMessage());
                updateTokenErrorInfo(uuId, tkToken);
                tokenLogService.saveTokenLog(uuId, tkToken, "2");
                //记录问题数据日志
                tkTokenRemarkLogService.saveRemarkLog(uuId, tkToken);
            }
        }else{
            result.errorResponse(ResultEnum.user_taxno_not_exist);
        }
        return result;
    }


    //从国税或第三方获取token
    @Override
    public String getTokenByThird(String uuIdFlag, TkToken tkToken, TkZoneInfo tkZoneInfo, TkServer tkServer,boolean isFirst, String requestFrom) {
        String token = null;
        String taxLoginName = null;
        uuIdFlag = uuIdFlag + "_" +  UUIDGenerator.generate();
        Instant start = Instant.now();

        tkToken.setTokenStatus(TokenConstants.TOKEN_ON_THE_WAY);
        tkToken.setRequestFrom(requestFrom);
        if(TokenConstants.SERVER_LOCAL.equals(tkServer.getTokenType())){
            if(StringUtils.isNotBlank(TokenConstants.BRANCH_ZONECODES) && TokenConstants.BRANCH_ZONECODES.contains(tkZoneInfo.getZoneCode())){
                //临时升级解决方案
            }else{
                token = TokenUtil.getToken(tkToken,tkZoneInfo,tkServer);
            }
        } else if(TokenConstants.SERVER_THIRD.equals(tkServer.getTokenType())){
            try {
                String times = redisService.get(getTokenOverTimesName(tkZoneInfo.getZoneCode(), tkToken.getUserCode()));
                if(StringUtils.isNotBlank(times) && Integer.valueOf(times) >=1000){
                    //所在地区请求超时大于300次，暂停操作
                    logger.info(uuIdFlag + " " + tkToken.getUserCode() + " taxno " + tkToken.getTaxno() + " zoneCode " + tkZoneInfo.getZoneCode() + " over 1000 times!");
                } else {
                    logger.info(uuIdFlag + " " + tkToken.getUserCode() + " taxno " + tkToken.getTaxno() + ",requestFrom:" + requestFrom);
                    Map<String, Object> data = TokenThirdServiceFactory.getToken(uuIdFlag, tkToken, tkZoneInfo, tkServer, isFirst);
                    logger.info(uuIdFlag + " " + tkToken.getUserCode() + " taxno " + tkToken.getTaxno() + " get token from remote.result:" + data);
                    if (TokenThirdServiceFactory.isSuccess(data)) {
                        token = TokenThirdServiceFactory.getToken(data);
                    }
                    tkToken.setTokenCode(TokenThirdServiceFactory.getCode(data));
                    tkToken.setTokenMessage(TokenThirdServiceFactory.getMessage(data));
                    tkToken.setRetrySecond(TokenThirdServiceFactory.getTimes(data));
                    tkToken.setRequestId(TokenThirdServiceFactory.getRequestId(data));
                    tkToken.setResContext(TokenThirdServiceFactory.getResContext(data));
                    tkToken.setProxyIp(TokenThirdServiceFactory.getProxyIp(data));
                    tkToken.setSecretMd5(TokenThirdServiceFactory.getSession(data));

                    taxLoginName = TokenThirdServiceFactory.getUserName(data);
                    tkToken.setTaxLoginName(taxLoginName);
                    transErrorCode(tkToken);
                }
            }catch (Exception e){
                e.printStackTrace();
                if(!isFirst && e instanceof SocketTimeoutException){
                    String times = redisService.get(getTokenOverTimesName(tkZoneInfo.getZoneCode(), tkToken.getUserCode()));
                    if(StringUtils.isBlank(times)){
                        times = "1";
                    }else{
                        times = String.valueOf(Integer.valueOf(times)+1);
                    }
                    redisService.set(getTokenOverTimesName(tkZoneInfo.getZoneCode(), tkToken.getUserCode()),times,30*60);
                    logger.error(uuIdFlag + " " + tkToken.getUserCode() + " taxno " + tkToken.getTaxno() + " is SocketTimeoutException");

                }
                tkToken.setTokenCode("9999");
                tkToken.setTokenMessage("请求税局失败");
                logger.error(uuIdFlag + " " + tkToken.getUserCode() + " taxno " + tkToken.getTaxno() + " get token error:" + e.getMessage());
            }
        }else if(TokenConstants.SERVER_WEBSOCKET.equals(tkServer.getTokenType())){
            token = getTokenFromSocket(tkToken,tkZoneInfo,tkServer);
        }
        if(StringUtils.isNotBlank(token)){
            //成功
            tkToken.setResDate(new Date());
            tkToken.setToken(token);
            tkToken.setTokenStatus(TokenConstants.TOKEN_VALID);
            redisService.set(RedisContants.TOKEN_REQ_TIMES + tkToken.getTaxno() + ":" + tkToken.getUserCode(),"0",5*60);
            //用于清理数据转换操作
            if(StringUtil.isNotEmpty(taxLoginName)) {
                redisService.set(RedisContants.USER_GROUP_ID + MD5Util.MD5Encode(taxLoginName),taxLoginName,3600*24);
            }
        }

        Instant end = Instant.now();
        long duration = end.toEpochMilli() - start.toEpochMilli();

        logger.info(uuIdFlag + " " + Thread.currentThread().getName() + " taxno " + tkToken.getTaxno() + " get token result " + tkToken.toString() + " cost time >>>>> " + duration + "ms");
        return token;
    }

    private void transErrorCode(TkToken tkToken) {
        String tokenCode = tkToken.getTokenCode();
        //增加短信异常重试逻辑
        if(StringUtil.isNotEmpty(tokenCode)) {
            if(getCountFlag(tokenCode)) {
                int errorNum = 0;
                if (checkTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno())) {
                    errorNum = getTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno());
                }
                if (errorNum < TokenConstants.ELE_TAX_COUNT_SMALL) {
//                    tkToken.setTokenStatus(TokenConstants.TOKEN_INIT);
                    setTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno());
                } else {
                    tkToken.setTokenCode("4042");
                    tkToken.setTokenMessage("无法收到验证码");
                    delTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno());
                }
            } else if(tokenCode.equals("8000")) {
                String retrySecond = tkToken.getRetrySecond();
                if(StringUtil.isNotEmpty(retrySecond) && !"null".equals(retrySecond) && Integer.parseInt(retrySecond)>0) {
                    tkToken.setTokenStatus(TokenConstants.TOKEN_INIT);
                    tkToken.setRetryTime(DateUtils.addSecond(DateUtils.getDate(), Integer.parseInt(retrySecond)));
                }
                if(StringUtil.isNotEmpty(tkToken.getTokenMessage()) && tkToken.getTokenMessage().contains("短信发送次数超限")) {
                    tkToken.setTokenStatus(TokenConstants.TOKEN_INIT);
                }
            } else if(tokenCode.equals("0")) {
                if (checkTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno())) {
                    delTokenCountNum(tkToken.getUserCode(), tkToken.getTaxno());
                }
            }

        }
    }

    private boolean getCountFlag(String tokenCode) {
        if(TokenConstants.ELE_TAX_COUNT_CODE_LIST.contains(tokenCode)) {
            return true;
        }
        return false;
    }

    private boolean getCountStopFlag(String tokenCode) {
        if(TokenConstants.ELE_TAX_COUNT_CODE_LIST.contains(tokenCode) || tokenCode.equals("0")) {
            return true;
        }
        return false;
    }

    public static String ELE_TAX_TOKEN_NUM = "ele:tax:token:num:";
    public void setTokenCountNum(String userCode, String taxno) {
        redisService.incr(ELE_TAX_TOKEN_NUM + userCode + ":" + taxno, 1 * 60 * 60);
    }

    public boolean checkTokenCountNum(String userCode, String taxno) {
        return redisService.exists(ELE_TAX_TOKEN_NUM + userCode + ":" + taxno);
    }

    public int getTokenCountNum(String userCode, String taxno) {
        return Integer.parseInt(redisService.get(ELE_TAX_TOKEN_NUM + userCode + ":" + taxno));
    }

    public void delTokenCountNum(String userCode, String taxno) {
        redisService.del(ELE_TAX_TOKEN_NUM + userCode + ":" + taxno);
    }

    public static String ELE_TAX_TOKEN_RETRY_SECOND = "ele:tax:token:retrySecond:";

    public void setTokenRetrySecond(String userCode, String taxno, int times) {
        redisService.incr(ELE_TAX_TOKEN_RETRY_SECOND + userCode + ":" + taxno, times);
    }

    public boolean checkTokenRetrySecond(String userCode, String taxno) {
        return redisService.exists(ELE_TAX_TOKEN_RETRY_SECOND + userCode + ":" + taxno);
    }

    private String getTokenFromSocket(TkToken tkToken, TkZoneInfo tkZoneInfo, TkServer tkServer) {
        String token = redisService.get(RedisContants.TOKEN_WEB_SOCKET + tkToken.getTaxno());
        if(StringUtils.isBlank(token) || !NationUtil.isValid(tkToken)){
            WebSocketClientService.sendClientHelloForToken(webSocketClient.getChannel(), tkToken.getTaxno(),tkToken.getZoneCode());
//            WebSocketClientService.sendTaxNoForToken(webSocketClient.getChannel(), tkToken.getTaxno());
            token = null;
        }
        return null;
    }

    @Override
    public JsonResult<String> pushToken(PushTokenReq pushTokenReq) {
        TkZoneInfo tkZoneInfo = redisService.get(RedisContants.TOKEN_ZONE_IS_NEW + pushTokenReq.getZoneCode(),TkZoneInfo.class);
        boolean isNew = false;
        if(tkZoneInfo!=null){
            isNew = "1".equals(tkZoneInfo.getIsNew())? true:false;
        }
        if(isNew){
            producer.sendPushTokenNewTopic(pushTokenReq);
        }else {
            producer.sendPushTokenTopic(pushTokenReq);
        }
        return new JsonResult();
    }

    @Override
    public JsonResult<String> signData(SignDataReq signDataReq) {
        JsonResult<String> jsonResult = new JsonResult<>();
        TkTokenExample example = new TkTokenExample();
        example.createCriteria().andTaxnoEqualTo(signDataReq.getTaxNo());
        TkToken tkToken = tkTokenMapper.selectByExample(example).get(0);

        TkServerExample serverExample = new TkServerExample();
        serverExample.createCriteria().andServerNoEqualTo(tkToken.getServerNo());
        TkServer tkServer = tkServerService.selectByExample(serverExample).get(0);

        try {
            Map<String, Object> data = signThirdSJService.signData(tkToken, signDataReq, tkServer, false);
            jsonResult.setData(ObjectUtil.fixNull(data.get("sign")));
            jsonResult.setErrorCode(ObjectUtil.fixNull(data.get("code")));
            jsonResult.setMessage(ObjectUtil.fixNull(data.get("message")));
        } catch (Exception e) {
            e.printStackTrace();
            jsonResult.setSuccess(false);
        }
        return jsonResult;
    }

    @Override
    public JsonResult<TkTokenRemarkLog> queryTokenErrorInfo(TkToken tkToken) {
        JsonResult<TkTokenRemarkLog> jsonResult = new JsonResult<>();

        TkTokenRemarkLogExample example = new TkTokenRemarkLogExample();
        example.createCriteria().andTaxnoEqualTo(tkToken.getTaxno()).andUserCodeEqualTo(tkToken.getUserCode());
        TkTokenRemarkLog tkTokenRemarkLog = tkTokenRemarkLogService.getOne(example);

        //检测是否在token表存在
        String tokenFlag = "0";
        TkTokenExample tokenExample = new TkTokenExample();
        tokenExample.createCriteria().andTaxnoEqualTo(tkToken.getTaxno()).andUserCodeEqualTo(tkToken.getUserCode());
        int tokenCount = tkTokenMapper.countByExample(tokenExample);
        if(tokenCount>0) {
            tokenFlag = "1";
        }
        if(null != tkTokenRemarkLog && getStopFlag(tkTokenRemarkLog.getUserCode(), tkTokenRemarkLog.getTokenCode(), tkToken.getUserType(), tkToken.getTokenStatus())){
            tkTokenRemarkLog.setTokenDate(tkTokenRemarkLog.getUpdateDate() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tkTokenRemarkLog.getUpdateDate()) : "");
        } else {
            tkTokenRemarkLog = new TkTokenRemarkLog();
        }
        tkTokenRemarkLog.setTokenFlag(tokenFlag);
        jsonResult.setData(tkTokenRemarkLog);
        return jsonResult;
    }

    @Override
    public JsonResult<String> insertToken(TkToken tkToken) {
        JsonResult<String> jsonResult = new JsonResult<>();

        String uuId = UUIDGenerator.generate();
        String userCode = tkToken.getUserCode();
        String taxno = tkToken.getTaxno();
        try {
            TkUser tkUser = tkUserService.getUserByUserCode(userCode, TokenConstants.FUNCTION_NO_FPY);
            if(tkUser == null){
                return jsonResult.errorResponse(ResultEnum.user_mismatch_rules);
            }

            TkTokenExample example = new TkTokenExample();
            example.createCriteria().andTaxnoEqualTo(tkToken.getTaxno()).andUserCodeEqualTo(tkToken.getUserCode());
            List<TkToken> queryList = tkTokenMapper.selectByExample(example);

            Date curDate = DateUtils.getDate();
            String secert = null;
            boolean jsonFlag = false;
            if(tkUser.getIsCheckToken().equals("0")) {
                if(null != tkToken.getTaxUserInfo()) {
                    secert = JSON.toJSONString(tkToken.getTaxUserInfo());
                    jsonFlag = true;
                } else {
                    if(StringUtils.isNotEmpty(tkToken.getSecert())) {
                        try {
                            secert = new String(Base64Util.decode(tkToken.getSecert()), "UTF-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            if(queryList!=null && queryList.size()>0){
                boolean updateFlag = false;
                TkToken updateEntity = queryList.get(0);
                //地区有变化修改
                if(!tkToken.getZoneCode().equals(updateEntity.getZoneCode())){
                    updateFlag = true;
                }

                //用户信息有变化修改
                if(jsonFlag) {
                    if(null==updateEntity.parseTaxUserInfo() || !tkToken.getTaxUserInfo().toString().equals(updateEntity.parseTaxUserInfo().toString())) {
                        updateFlag = true;
                    }
                } else {
                    String oldSecert = StringUtils.isEmpty(updateEntity.getSecert())?"":updateEntity.getSecert();
                    String newSecert = StringUtils.isEmpty(secert)?"":secert;
                    if(!oldSecert.equals(newSecert)) {
                        updateFlag = true;
                    }
                }
                //检测token信息
                if(!updateEntity.getIsValid().equals(tkToken.getIsValid()) || !updateEntity.getUserType().equals(tkToken.getUserType())) {
                    updateFlag = true;
                }
                if(updateFlag) {
                    if(!tkToken.getUserType().equals("9")) {
                        updateEntity.setJsonFlag(jsonFlag);
                        updateEntity.setSecert(secert);
                        updateEntity.setZoneCode(tkToken.getZoneCode());
                        updateEntity.setUserType(tkToken.getUserType());
                    }
                    updateEntity.setIsValid(tkToken.getIsValid());
                    updateEntity.setTokenStatus("0");
                    updateEntity.setTimes(0);
                    updateEntity.setUpdateDate(curDate);
                    tkTokenMapper.updateTokenInfo(updateEntity);
                }
                logger.info(uuId + " insertToken taxno is " + taxno + " is update success!" + updateEntity.printSaveInfo() + ",updateFlag:" + updateFlag);
                //开启因短信超次数关闭的税号
                if(StringUtil.isNotEmpty(updateEntity.getErrorCode()) && updateEntity.getErrorCode().equals("4042")) {
                    CookieReq cookieReq = new CookieReq();
                    cookieReq.setUserCode(userCode);
                    cookieReq.setZoneCode(updateEntity.getZoneCode());
                    cookieReq.setTaxno(taxno);
                    Map<String, Object> cleanMap = CleanCookieUtil.cleanCookie(uuId, cookieReq, "10");
                    logger.info(uuId + " insertToken taxno is " + taxno + " cleanCookie result is " + JSON.toJSONString(cleanMap));
                }
            } else {
                TkToken entity = new TkToken();

                entity.setTaxno(tkToken.getTaxno());
                entity.setZoneCode(tkToken.getZoneCode());
                entity.setUserCode(userCode);
                entity.setServerNo(tkUser.getServerNo());
                entity.setSecert(secert);
                entity.setReqDate(curDate);
                entity.setCreateDate(curDate);
                entity.setUpdateDate(curDate);
                entity.setUserType(tkToken.getUserType());
                if(tkToken.getUserType().equals(TokenConstants.USER_TYPE_HAND)) {
                    entity.setIsValid("0");
                } else {
                    entity.setIsValid("1");
                }
                tkTokenMapper.insertSelective(entity);
                logger.info(uuId + " insertToken taxno is " + taxno + " is save success!" + entity.printSaveInfo());
            }
        } catch (Exception e) {
            e.printStackTrace();
            jsonResult.errorResponse(ResultEnum.system_error);
            logger.error(uuId + " insertToken taxno is " + taxno + " is error:" + e.getMessage());
        }
        return jsonResult;
    }

    @Override
    public void saveToken(String taxno, String token) {
        TkTokenExample example = new TkTokenExample();
        example.createCriteria().andTaxnoEqualTo(taxno).andIsValidEqualTo(TokenConstants.TOKEN_VALID);
        List<TkToken> tkTokenList  = tkTokenMapper.selectByExample(example);
        if(tkTokenList!=null && tkTokenList.size()>0){
            TkToken tkToken = tkTokenList.get(0);
            tkToken.setToken(token);
            tkToken.setResDate(new Date());
            tkToken.setTokenStatus(TokenConstants.TOKEN_VALID);
            tkTokenMapper.updateByPrimaryKey(tkToken);
            redisService.set(RedisContants.TOKEN_REQ_TIMES + tkToken.getTaxno() + ":" + tkToken.getUserCode(),"0",5*60);
        }
    }

    @Override
    public JsonResult<String> getSignFromTaxDisk(SignDataReq signDataReq) {
        JsonResult<String> jsonResult = new JsonResult<>();
        String sign = redisService.get(RedisContants.TOKEN_WEB_SOCKET_GETSIGN + signDataReq.getTaxNo());
        if(StringUtils.isBlank(sign)){
            WebSocketClientService.sendDataForSign(webSocketClient.getChannel(),signDataReq.getTaxNo(), signDataReq.getStrData());
            jsonResult.setMessage("签名获取中");
        }
        jsonResult.setData(sign);
        return jsonResult;
    }

    @Override
    public void synZoneInfo(TkZoneInfo tkZoneInfo){
        if(redisService.exists(RedisContants.ZONE_CODE + tkZoneInfo.getZoneCode())) {
            FinZoneInfo finZoneInfo =JSON.parseObject(redisService.get(RedisContants.ZONE_CODE + tkZoneInfo.getZoneCode()),  FinZoneInfo.class);
            tkZoneInfo.setYmbb(finZoneInfo.getYmbb());
        }
    }

    public void updateReqTokenInfo(String uuId, TkToken tkToken) {
        TkToken updateToken = new TkToken();
        updateToken.setReqDate(tkToken.getReqDate());
        updateToken.setTimes(tkToken.getTimes());
        updateToken.setId(tkToken.getId());
        updateToken.setTokenStatus(tkToken.getTokenStatus());
        updateToken.setUpdateErrorFlag("1");
        tkTokenMapper.updateByPrimaryKeySelective(updateToken);
        logger.info(uuId + " getToken --> updateReqTokenInfo set times and token status! " +  tkToken.getUserCode() + "=" + tkToken.getTaxno());
    }

    public void updateTokenTimes(TkToken tkToken) {
        TkToken updateToken = new TkToken();
        updateToken.setTimes(tkToken.getTimes());
        updateToken.setTokenStatus(tkToken.getTokenStatus());
        updateToken.setReqDate(tkToken.getReqDate());
        updateToken.setId(tkToken.getId());
        tkTokenMapper.updateByPrimaryKeySelective(updateToken);
    }

    @Override
    public void updateTokenInfo(String uuId, TkToken tkToken) {
        TkToken updateToken = new TkToken();
        updateToken.setReqDate(tkToken.getReqDate());
        updateToken.setResDate(tkToken.getResDate());
        updateToken.setToken(tkToken.getToken());
        updateToken.setTokenStatus(tkToken.getTokenStatus());
        updateToken.setProxyIp(tkToken.getProxyIp());
        updateToken.setTaxLoginName(tkToken.getTaxLoginName());
        updateToken.setSecretMd5(tkToken.getSecretMd5());

        if(tkToken.getTokenStatus().equals(TokenConstants.TOKEN_VALID)) {
            updateToken.setIsValid("1");
            updateToken.setTimes(0);
            updateToken.setRetrySecond("0");
        } else{
            updateToken.setTimes(tkToken.getTimes()+1);
        }
        updateToken.setId(tkToken.getId());
        //设置无效开关
        if(StringUtils.isNotBlank(tkToken.getTokenCode())){
            updateToken.setErrorCode(tkToken.getTokenCode());
            updateToken.setErrorMessage(StringUtil.getShortLog(tkToken.getTokenMessage(), 5000));
            updateToken.setRetrySecond(tkToken.getRetrySecond());
            updateToken.setRetryTime(tkToken.getRetryTime());
            if(getStopFlag(tkToken.getUserCode(), tkToken.getTokenCode(), tkToken.getUserType(), tkToken.getTokenStatus())) {
                updateToken.setIsValid("0");
                logger.info(uuId + " updateTokenInfo set disable valid! " +  tkToken.getUserCode() + "=" + tkToken.getTaxno() + "=" + updateToken.getIsValid());
            }
        }
        //电子税局的cookie只记录md5，不记录cookie，通过md5换取发票云cookie
        if(tkToken.getTokenStatus().equals("1")) {
            if(StringUtil.isNotEmpty(tkToken.getFunctionNo()) && tkToken.getFunctionNo().equals("01")) {
                updateToken.setToken(null);
                updateToken.setTokenStatus("0");
            }
        }
        updateToken.setUpdateErrorFlag("1");
        tkTokenMapper.updateByPrimaryKeySelective(updateToken);
    }

    public void updateTokenErrorInfo(String uuId, TkToken tkToken) {
        TkToken updateToken = new TkToken();
        updateToken.setTokenStatus(tkToken.getTokenStatus());
        updateToken.setId(tkToken.getId());
        //设置无效开关
        if(StringUtils.isNotBlank(tkToken.getTokenCode())){
            if(getStopFlag(tkToken.getUserCode(), tkToken.getTokenCode(), tkToken.getUserType(), tkToken.getTokenStatus())) {
                updateToken.setIsValid("0");
                logger.info(uuId + " updateTokenInfo set disable valid! " +  tkToken.getUserCode() + "=" + tkToken.getTaxno());
            }
        }
        tkTokenMapper.updateByPrimaryKeySelective(updateToken);
    }

    private boolean getStopFlag(String userCode, String tokenCode, String userType, String tokenStatus) {
        if((tokenCode.length()==4 && tokenCode.startsWith("4"))) {
            return true;
        }
        if((tokenCode.equals("8080") && null!=userType && userType.equals(TokenConstants.USER_TYPE_HAND))) {
            return true;
        }
        //一次性用户的，取不到直接关闭
        if(null!=userType && userType.equals(TokenConstants.USER_TYPE_ONE) && !tokenStatus.equals("1")) {
            return true;
        }
        return false;
    }

    public JsonResult<String> selectErrorTokenList(String userCode, Integer pageId) {
        Integer pageNum = 1000;
        JsonResult result = new JsonResult();
        List<TkToken> errorTokenList = tkTokenMapper.selectErrorTokenList(userCode, pageId , pageNum);
        String taskNo = UUIDGenerator.generate();
        Map invListMap = createResponseJson(taskNo, errorTokenList, pageNum);
        result.setData(invListMap);
        logger.info(taskNo + " selectErrorTokenList is " + StringUtil.getShortLog(JSON.toJSONString(result), 500));
        return result;
    }

    public Map createResponseJson(String taskno, List<TkToken> errorTokenList, Integer pageNum) {
        Map result = new HashMap();
        List<TokenErrorRes> resErrorList = new ArrayList<TokenErrorRes>();
        Integer pageId = 0;
        for(TkToken tkToken:errorTokenList) {
            TokenErrorRes errorRes = new TokenErrorRes();
            errorRes.setTaxno(tkToken.getTaxno());
            errorRes.setTokenErrorCode(tkToken.getTokenErrorCode());
            errorRes.setTokenErrorDate(tkToken.getTokenErrorDate() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tkToken.getTokenErrorDate()) : "");
            errorRes.setTokenErrorMessage(tkToken.getTokenErrorMessage());
            errorRes.setUserStatus(tkToken.getIsValid());
            errorRes.setUserType(tkToken.getUserType());
            errorRes.setLastResDate(tkToken.getResDate() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tkToken.getResDate()) : "");
            errorRes.setZoneCode(tkToken.getZoneCode());
            resErrorList.add(errorRes);
            pageId = tkToken.getId();
        }
        int quantity = resErrorList.size();
        result.put("taskno", taskno);
        result.put("quantity", quantity);
        result.put("errorTaxnoList", resErrorList);
        result.put("pageId", pageId);
        result.put("followTask", quantity < pageNum ? false : true);

        return result;
    }

    private String getTokenOverTimesName(String zoneCode, String userCode) {
        return RedisContants.TOKEN_TIMEOUT_TIMES + zoneCode + userCode;
    }


    public JsonResult<String> cleanCookie(String uuId, CookieReq cookieReq) {

        JsonResult result = new JsonResult();
        String taxno = cookieReq.getTaxno();
        String userCode = cookieReq.getUserCode();

        TkUser tkUser = tkUserService.getUserByUserCode(userCode, TokenConstants.FUNCTION_NO_FPY);
        if(tkUser == null){
            result.errorResponse(ResultEnum.forbidden);
        } else {
            try {
                String newUserInfo = "";
                if(null != cookieReq.getUserInfoJson()) {
                    newUserInfo = cookieReq.getUserInfoJson().toString();
                } else {
                    String oldUserInfo = cookieReq.getUserInfo();
                    if (StringUtils.isNotEmpty(oldUserInfo)) {
                        try {
                            newUserInfo = new String(Base64Util.decode(oldUserInfo), "UTF-8");
                            logger.info(uuId + " cleanCookie taxno is " + taxno + ", newUserInfo is " + newUserInfo);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                            logger.error(uuId + " cleanCookie taxno is " + taxno + " parse userInfo is error!" + e.getMessage());
                        }
                    }
                }
                if(StringUtil.isEmpty(newUserInfo)) {
                    result.errorResponse(ResultEnum.param_error);
                } else {
                    cookieReq.setUserInfo(MD5Util.MD5Encode(newUserInfo));
                    if(!tkUser.getDefaultCleanType().equals("9")) {
                        Map<String, Object> resMap = CleanCookieUtil.cleanCookie(uuId, cookieReq, tkUser.getDefaultCleanType());
                        if (!TokenThirdServiceFactory.isSuccess(resMap)) {
                            String resMessage = (String)resMap.get("message");
                            if(StringUtil.isNotEmpty(resMessage)) {
                                result.setMessage(resMessage);
                            } else {
                                result.errorResponse(ResultEnum.flush_error);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                result.errorResponse(ResultEnum.flush_error);
                logger.error(uuId + " cleanCookie taxno is " + taxno + " ,send is error!" + e.getMessage());
            }
        }
        logger.info(uuId + " cleanCookie taxno is " + taxno + ", result is " + result);
        return result;
    }

    public JsonResult<String> cleanGroupCookie(String uuId, CookieReq cookieReq) {
        JsonResult result = new JsonResult();
        String userInfo = cookieReq.getUserInfo();
        try {
            String userName = redisService.get(RedisContants.USER_GROUP_ID + userInfo);
            logger.info(uuId + " cleanGroupCookie userInfo is " + userInfo + " to " + userName);
            if(StringUtil.isNotEmpty(userName)){
                cookieReq.setUserInfo(userName);
                Map<String, Object> resMap = CleanCookieUtil.cleanCookie(uuId, cookieReq, "3");
                if (!TokenThirdServiceFactory.isSuccess(resMap)) {
                    String resMessage = (String)resMap.get("message");
                    if(StringUtil.isNotEmpty(resMessage)) {
                        result.setMessage(resMessage);
                    } else {
                        result.errorResponse(ResultEnum.flush_error);
                    }
                }
            } else {
                result.errorResponse(ResultEnum.notfound);
                logger.info(uuId + " cleanGroupCookie userInfo is " + userInfo + " not find userName in cache! ");
            }
        } catch (Exception e) {
            e.printStackTrace();
            result.errorResponse(ResultEnum.flush_error);
            logger.error(uuId + " cleanGroupCookie taxno is " + userInfo + " ,send is error!" + e.getMessage());
        }
        logger.info(uuId + " cleanGroupCookie taxno is " + userInfo + ", result is " + result);
        return result;
    }

    public JsonResult<String> queryTokenInfo(String optType, String userCode, String taxno) {
        JsonResult result = new JsonResult();

        TkTokenExample example = new TkTokenExample();
        example.createCriteria().andTaxnoEqualTo(taxno).andUserCodeEqualTo(userCode);
        List<TkToken> tkTokenList  = tkTokenMapper.selectByExample(example);
        if(tkTokenList!=null && tkTokenList.size()>0){
            TkToken tkToken = tkTokenList.get(0);
            if(optType.equals("0")) {
                result.setData(tkToken.toString());
            } else {
                tkToken.setIsValid("1");
                tkToken.setTokenStatus("0");
                tkToken.setTimes(0);
                tkTokenMapper.updateByPrimaryKeySelective(tkToken);
                result.setData(tkToken.toString());
            }
        } else {
            result.errorResponse(ResultEnum.user_taxno_not_exist);
        }
        return result;
    }

    public boolean checkTokenRealRequestCount(String userCode, String taxno) {
        return redisService.exists(RedisContants.TOKEN_REALREQUEST_COUNT + userCode + RedisContants.DELIMITER + taxno);
    }

    public int getTokenRealRequestCount(String userCode, String taxno) {
        return Integer.parseInt(redisService.get(RedisContants.TOKEN_REALREQUEST_COUNT + userCode + RedisContants.DELIMITER + taxno));
    }

    public void setTokenRealRequestCount(String userCode, String taxno) {
        String times = redisService.get(RedisContants.TOKEN_REALREQUEST_COUNT + userCode + RedisContants.DELIMITER + taxno);
        if(StringUtils.isBlank(times)){
            times = "1";
        } else {
            times = String.valueOf(Integer.valueOf(times)+1);
        }
        redisService.set(RedisContants.TOKEN_REALREQUEST_COUNT + userCode + RedisContants.DELIMITER + taxno, times, 20 * 60);
    }

}
