package com.jeeplus.finance.invoice.service.OMS;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jeeplus.common.redis.RedisUtils;
import com.jeeplus.finance.invoice.domain.CwFinanceInvoice;
import com.jeeplus.finance.invoice.domain.OMS.OMSAccessTokenInfo;
import com.jeeplus.finance.invoice.domain.WorkInvoiceTaxClassificationCode;
import com.jeeplus.finance.invoice.mapper.CwFinanceInvoiceMapper;
import com.jeeplus.finance.invoice.service.CwFinanceInvoiceService;
import com.jeeplus.finance.invoice.service.dto.CwFinanceInvoiceDTO;
import com.jeeplus.finance.invoice.util.OMS.HttpPostJsonUtil;
import com.jeeplus.finance.invoice.util.OMS.OMSNationUtil;
import com.jeeplus.finance.utils.Global;
import com.jeeplus.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.jeeplus.finance.invoice.config.OmsConfig.*;

@Service
@Transactional(readOnly = true)
@Lazy
public class OMSDisposeService {

    /**
     * 给accessToken查询的有效时间设置为1天
     */
    //private final int seconds = 86400;
    //private static final String appId = Global.getConfig("omsAppId");
    //private static final String appKey = Global.getConfig("omsAppKey");
    //private static final String deptCode = Global.getConfig("omsDeptCode");
    //@Value("${omsAppId}")
    //private String appId;
    //@Value("${omsAppKey}")
    //private String appKey;
    //@Value("${omsDeptCode}")
    //private String deptCode;
    //@Value("${omsUrl}")
    //private String omsUrl;
    //如果接口访问不正确，可以循环访问的次数
    private final int remainRetryTimes = 20;


    @Autowired
    private CwFinanceInvoiceMapper workInvoiceDao;

    @Autowired
    private CwFinanceInvoiceService workInvoiceService;

    // 注入SpringBoot内置的JSON序列化工具（无额外依赖）
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 用于生成开蓝票信息
     * @param map
     * @param workInvoiceId 需要开票的开票信息id
     */
    @Transactional(readOnly = false)
    public void doInvoiceBusiness(Map<String,Object> map, String workInvoiceId, String informType) {
        RedisUtils jedis = null;
        String accessToken = null;
        try {
            jedis = RedisUtils.getInstance();
            accessToken = (String) jedis.get("OMSAccessToken");
            if(StringUtils.isBlank(accessToken)){
                // 获取AccessToken 9998重试5次
                accessToken = getOmsAccessTokenWithRetry(10, "accessToken", workInvoiceId, informType);
                if(StringUtils.isNotBlank(accessToken)){
                    jedis.setEx("OMSAccessToken", accessToken,86400, TimeUnit.SECONDS);
                    map.put("token状态", "重新获取token成功，存入Redis");
                } else {
                    accessToken = "";
                    map.put("token状态", "获取token失败");
                    return;
                }
            } else {
                map.put("token状态", "从Redis获取token成功");
            }



            OMSNationUtil util = new OMSNationUtil();
            //获取需要开票的发票信息
            CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);

            if(null != workInvoice){
                if( StringUtils.isBlank(workInvoice.getBillingPeopleReal())){
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                    return;
                }

                if(StringUtils.isBlank(workInvoice.getBankAccount())){
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                    return;
                }


                //判定开票内容是否关联税收编码
                if(StringUtils.isNotBlank(workInvoice.getBillingContent())){
                    WorkInvoiceTaxClassificationCode billingContentDetail = workInvoiceDao.getBillingContentDetail(workInvoice);
                    if(null != billingContentDetail){
                        workInvoice.setBillingContent(billingContentDetail.getGoodName());
                        workInvoice.setGoodsTaxno(billingContentDetail.getGoodsTaxno());
                    }else{
                        handleInvoiceRetryAllFail("",workInvoiceId, "未找到对应税收编码，请联系信息部确认后重新发起", informType); // 解析失败直接兜底
                    }
                    //生成开票基础信息
                    String string = util.neatenData(workInvoiceId,workInvoice,billingContentDetail);

                    OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
                    InvoiceTokenInfo.setAppId(appId);
                    InvoiceTokenInfo.setAppKey(appKey);
                    InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
                    InvoiceTokenInfo.setAccessToken(accessToken);
                    InvoiceTokenInfo.setData(string);
                    String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);

                    String jsonInvoicResultStr = HttpPostJsonUtil.doPost(omsUrl +"/prod-api/output/server/order/upload", jsonInvoiceStr);
                    System.out.println("✅ 订单提交接口返回值：" + jsonInvoicResultStr);
                    map.put("订单接口信息", jsonInvoicResultStr);

                    // 调用订单上传重试方法（包含所有码值规则）
                    if(StringUtils.isNotBlank(jsonInvoicResultStr)){
                        //将上个节点获取的值存到数据库，留作redis崩溃后进行重新获取处理
                        workInvoice.setOrderForGoodsResultStr(jsonInvoicResultStr);
                        workInvoiceDao.updateOrderForGoodsResultStrById(workInvoice);


                        String finalAccessToken = accessToken;
                        String finalJsonInvoiceStr = jsonInvoiceStr;
                        executeOrderUploadRetry(workInvoiceId, remainRetryTimes, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, map,"accessToken", "blueTicket", informType);
                    }else{
                        handleInvoiceRetryAllFail(jsonInvoiceStr,workInvoiceId, "蓝票发送订单生成接口返回值为空，开票失败。节点访问参数为：" + jsonInvoicResultStr, informType); // 解析失败直接兜底

                    }
                }else{
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                }



            }else{
                handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
            }



        } catch (Exception e) {
            e.printStackTrace();
            map.put("errorMsg", "系统异常：" + e.getMessage());
        } finally {
            if(jedis != null){
                //jedis.close();
            }
        }
    }

    /**
     * 用于调用accessToken
     * @param remainRetryTimes
     * @param getKey
     * @return
     */
    @Transactional(readOnly = false)
    public String getOmsAccessTokenWithRetry(int remainRetryTimes, String getKey, String workInvoiceId,  String informType) {
        try {
            OMSAccessTokenInfo tokenInfo = new OMSAccessTokenInfo();
            tokenInfo.setAppId(appId);
            tokenInfo.setAppKey(appKey);
            tokenInfo.setExchangeId(UUID.randomUUID().toString());
            String jsonStr = JSON.toJSONString(tokenInfo);
            String accessTokenStr = HttpPostJsonUtil.doPost(omsUrl +"/prod-api/server/accessToken", jsonStr);

            if(StringUtils.isBlank(accessTokenStr)){
                System.err.println("获取AccessToken失败：接口返回空，剩余重试次数："+remainRetryTimes);
                if (remainRetryTimes > 1) {
                    int nextRetry = remainRetryTimes - 1;
                    System.err.println("⚠️ 获取AccessToken失败：接口返回空，剩余重试次数："+nextRetry);
                    Thread.sleep(30 * 1000);
                    return getOmsAccessTokenWithRetry(nextRetry, getKey, workInvoiceId, informType);
                } else {
                    System.err.println("❌ 获取AccessToken失败：接口返回空，重试次数耗尽！");
                    //需要将错误信息保存到对应开票信息表中
                    CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);
                    if(null != workInvoice){
                        workInvoice.setOmsAccessTokenError("获取AccessToken失败：接口返回空");
                        //修改结果
                        workInvoiceDao.updateAccessTokenErrorById(workInvoice);
                    }
                    //如果需要 可以将执行失败信息通过短信通知业务发起人
                    handleInvoiceRetryAllFail("", workInvoiceId, "获取AccessToken失败：接口返回空", informType); // 解析失败也执行兜底方法

                    return "";
                }
            }

            OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(accessTokenStr, OMSAccessTokenInfo.class);
            if(null == resultTokenInfo || null == resultTokenInfo.getResult()){
                System.err.println("获取AccessToken失败：返回结果解析异常，剩余重试次数："+remainRetryTimes);
                if (remainRetryTimes > 1) {
                    int nextRetry = remainRetryTimes - 1;
                    System.err.println("⚠️ 获取AccessToken失败：返回结果解析异常，剩余重试次数："+nextRetry);
                    Thread.sleep(30 * 1000);
                    return getOmsAccessTokenWithRetry(nextRetry, getKey, workInvoiceId, informType);
                } else {
                    System.err.println("❌ 获取AccessToken失败：返回结果解析异常，重试次数耗尽！");
                    //需要将错误信息保存到对应开票信息表中
                    CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);
                    if(null != workInvoice){
                        workInvoice.setOmsAccessTokenError("获取AccessToken失败：返回结果解析异常");
                        //修改结果
                        workInvoiceDao.updateAccessTokenErrorById(workInvoice);
                    }
                    //如果需要 可以将执行失败信息通过短信通知业务发起人
                    handleInvoiceRetryAllFail("", workInvoiceId, "获取AccessToken失败：返回结果解析异常", informType); // 解析失败也执行兜底方法

                    return "";
                }
            }
            String code = resultTokenInfo.getResult().getCode();
            String message = resultTokenInfo.getResult().getMessage();
            if ("0000".equals(code)) {
                String token = OMSNationUtil.extractAccessTokenFromBase64(getKey, resultTokenInfo.getData().toString());
                System.out.println("✅ 获取AccessToken成功，重试次数剩余："+remainRetryTimes);
                return token;
            } else if ("9998".equals(code)) {
                if (remainRetryTimes > 1) {
                    int nextRetry = remainRetryTimes - 1;
                    System.err.println("⚠️ 获取AccessToken返回9998接口波动，30秒后重试，剩余次数："+nextRetry);
                    Thread.sleep(30 * 1000);
                    return getOmsAccessTokenWithRetry(nextRetry, getKey, workInvoiceId, informType);
                } else {
                    System.err.println("❌ 获取AccessToken失败：连续5次返回9998，重试次数耗尽！");
                    //需要将错误信息保存到对应开票信息表中
                    CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);
                    if(null != workInvoice){
                        workInvoice.setOmsAccessTokenError("获取AccessToken失败,需要业务人员重新发起");
                        //修改结果
                        workInvoiceDao.updateAccessTokenErrorById(workInvoice);
                    }
                    //如果需要 可以将执行失败信息通过短信通知业务发起人
                    handleInvoiceRetryAllFail("", workInvoiceId, "获取AccessToken失败,需要业务人员重新发起", informType); // 解析失败也执行兜底方法

                    return "";
                }
            } else {
                System.err.println("❌ 获取AccessToken失败：返回业务错误码，code："+code + "。 错误信息为："+message);
                //需要将错误信息保存到对应开票信息表中
                CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);
                if(null != workInvoice){
                    workInvoice.setOmsAccessTokenError("获取AccessToken失败：返回业务错误码，code："+code + "； 错误信息为："+message + "。需要业务人员重新发起");
                    //修改结果
                    workInvoiceDao.updateAccessTokenErrorById(workInvoice);
                }
                return "";
            }
        } catch (InterruptedException e) {
            System.err.println("❌ 获取AccessToken失败：重试延迟被中断");
            Thread.currentThread().interrupt();
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("❌ 获取AccessToken失败：调用接口异常，剩余重试次数："+remainRetryTimes);
            return "";
        }
    }


    /**
     * 调用订单上传方法，用于执行开票处理
     * @param workInvoiceId     发票id
     * @param remainRetryTimes  允许接口重调次数
     * @param jsonInvoicResultStr      上一个接口返回值
     * @param jsonInvoiceStr    上一个接口参数值
     * @param accessToken   接口访问accessToken
     * @param map   作用不大，仅作为保留
     * @param getKey    用来判定的参数值
     * @param initiationType    用来判定是什么类型的，比如蓝票、全类型红票、还是快捷红票
     * @param informType    流程用来通知的类型
     */

    @Transactional(readOnly = false)
    public void executeOrderUploadRetry(String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String jsonInvoiceStr, String accessToken, Map<String,Object> map, String getKey, String initiationType, String informType) {
        String jsonInvoicResult = "";
        try {
            OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
            if(null == resultTokenInfo || null == resultTokenInfo.getResult()){
                System.err.println("❌ 订单上传解析失败，剩余重试次数："+remainRetryTimes);
                handleInvoiceRetryAllFail(accessToken, workInvoiceId, "", informType); // 解析失败也执行兜底方法
                return;
            }
            String code = resultTokenInfo.getResult().getCode();
            String message = resultTokenInfo.getResult().getMessage();

            // ======================== 所有码值规则 全部在这里【最终定稿】=========================
            if ("0000".equals(code)) {
                // ✅ 0000 成功：解析数据+存入map+触发30秒后异步下载发票
                jsonInvoicResult = OMSNationUtil.extractAccessTokenFromBase64(getKey, resultTokenInfo.getData().toString());
                map.put("订单接口返回值",jsonInvoicResult);
                System.out.println("✅ 订单上传返回0000成功，触发发票下载接口");
                if(StringUtils.isNotBlank(jsonInvoicResult)){
                    if(initiationType.equals("fastRed")){
                        workInvoiceId = jsonInvoicResult;
                    }
                }
                String finalWorkInvoiceId = workInvoiceId;
                // 存入Redis，由InvoiceDownloadTask接管
                saveInvoiceDownloadTaskToRedis(accessToken, finalWorkInvoiceId, informType);
                System.out.println("✅ 解析开票数据任务["+finalWorkInvoiceId+"]已存入Redis，由InvoiceDownloadTask接管重试");

            } else if ("9998".equals(code)) {
                // 通过redis调用 方式系统崩溃导致的数据丢失
                // ✅ 9998 接口波动，放入redis中 用于定时任务进行处理
                saveInvoiceRetryScheduledTaskToRedis(jsonInvoiceStr, accessToken, workInvoiceId, getKey, initiationType);

            } else if ("0003".equals(code)) {
                // ✅ 0003 token失效：清除旧token → 重新获取token → 从头完整执行所有流程
                System.err.println("⚠️ 订单上传返回0003（token失效），开始重新获取token并从头执行流程");
                RedisUtils jedis = null;
                try {
                    jedis = RedisUtils.getInstance();
                    jedis.delete("OMSAccessToken");
                    map.put("0003处理", "已清除旧token，准备重新获取");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(jedis != null) {
                        //jedis.close();
                    }
                }
                doInvoiceBusiness(map, workInvoiceId, informType);
            } else {
                // ✅ ✅ ✅ 核心修正：0001/0002/其他任意错误码 → 直接调用handleInvoiceRetryAllFail执行修改系统信息逻辑 ✅ ✅ ✅
                System.err.println("❌ 订单上传返回业务错误码："+code+"，立即执行失败兜底逻辑修改系统信息！");
                System.err.println("❌ 订单上传返回业务错误原因："+message);
                map.put("订单状态", "失败，错误码："+code);
                handleInvoiceRetryAllFail(accessToken, workInvoiceId, message, informType); // 解析失败也执行兜底方法
                remainRetryTimes = 0;
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("❌ 订单上传重试异常，剩余次数："+remainRetryTimes);
            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "订单上传重试异常，请重新发起", informType); // 解析失败也执行兜底方法
        }
    }

    /**
     * 用于访问接口返回9998时使用，存入Redis供定时任务处理
     * 硬编码配置：初始重试次数100次，Redis过期时间1天（86400秒）
     * @param jsonInvoiceStr 订单上传的JSON参数
     * @param accessToken    AccessToken
     * @param workInvoiceId        订单号
     * @param getKey         获取token的key
     * @param initiationType 开票类型（蓝票/红票）
     */
    @Transactional(readOnly = false)
    public void saveInvoiceRetryScheduledTaskToRedis(String jsonInvoiceStr ,String accessToken, String workInvoiceId, String getKey, String initiationType) {
        RedisUtils jedis = null;
        // 硬编码：初始重试次数100次
        final int INIT_RETRY_COUNT = 100;
        // 硬编码：Redis任务过期时间1天（86400秒）
        final int REDIS_EXPIRE_SECONDS = 86400;

        try {
            jedis = RedisUtils.getInstance();
            String redisKey = "OMS_invoice_retry_scheduled:" + workInvoiceId;

            // 先检查是否已存在，存在则复用原有重试次数，不存在则初始化20次
            String retryTimes = (String) jedis.hGet(redisKey, "retryTimes");
            if (StringUtils.isBlank(retryTimes)) {
                retryTimes = String.valueOf(INIT_RETRY_COUNT); // 初始20次
            }

            // 存储所有需要的字段
            jedis.hPut(redisKey, "jsonInvoiceStr", jsonInvoiceStr);
            jedis.hPut(redisKey, "accessToken", accessToken);
            jedis.hPut(redisKey, "workInvoiceId", workInvoiceId);
            jedis.hPut(redisKey, "getKey", getKey);
            jedis.hPut(redisKey, "initiationType", initiationType);
            jedis.hPut(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
            jedis.hPut(redisKey, "retryTimes", retryTimes); // 重试次数

            jedis.expire(redisKey, REDIS_EXPIRE_SECONDS); // 1天过期
            System.out.println("✅ 9998重试任务已存入Redis，订单号：" + workInvoiceId + "，剩余重试次数：" + retryTimes);


            // ========== 追加：Redis数据转JSON，持久化到发票表（兜底逻辑） ==========
            long redisStoreTime = System.currentTimeMillis() / 1000; // 秒级存储时间，用于后期回滚
            // 封装Map：包含Redis所有哈希字段 + redisKey/过期时间/存储时间，保留redisKey（按你要求）
            Map<String, Object> retryTaskMap = new HashMap<>(12);
            // Redis哈希中所有原字段（和Redis完全一致）
            retryTaskMap.put("jsonInvoiceStr", jsonInvoiceStr);
            retryTaskMap.put("accessToken", accessToken);
            retryTaskMap.put("workInvoiceId", workInvoiceId);
            retryTaskMap.put("getKey", getKey);
            retryTaskMap.put("initiationType", initiationType);
            retryTaskMap.put("firstExecTime", String.valueOf(System.currentTimeMillis()));
            retryTaskMap.put("retryTimes", retryTimes);
            // 额外兜底字段（保留redisKey）
            retryTaskMap.put("redisKey", redisKey);
            retryTaskMap.put("redisExpireSeconds", REDIS_EXPIRE_SECONDS);
            retryTaskMap.put("redisStoreTime", redisStoreTime);
            retryTaskMap.put("initRetryCount", INIT_RETRY_COUNT); // 追加初始重试次数，后期回滚可参考

            // Map转JSON字符串（单独捕获序列化异常，不影响Redis核心逻辑）
            String retryTaskJson = objectMapper.writeValueAsString(retryTaskMap);

            // 更新发票表（和之前红冲/下载任务用相同的DAO和实体，风格统一）
            CwFinanceInvoiceDTO workInvoice = new CwFinanceInvoiceDTO();
            workInvoice.setId(workInvoiceId);
            workInvoice.setRetryInvoiceJson(retryTaskJson);
            // 处理DAO更新结果，按你的风格打印控制台信息
            workInvoiceDao.updateRedInvoiceJsonByWorkInvoiceId(workInvoice);

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("❌ 存入9998重试任务到Redis失败，订单号：" + workInvoiceId + "，原因：" + e.getMessage());
        } finally {
            if (jedis != null) {
                //jedis.close();
            }
        }
    }


    /**
     * 新增开蓝票生成的下载用的redis
     * @param accessToken
     * @param workInvoiceId 开票的id
     */
    public void saveInvoiceDownloadTaskToRedis(String accessToken, String workInvoiceId, String informType) {
        RedisUtils jedis = null;
        try {
            jedis = RedisUtils.getInstance();
            String redisKey = "OMS_invoice_download:" + workInvoiceId;
            jedis.hPut(redisKey, "accessToken", accessToken);
            jedis.hPut(redisKey, "workInvoiceId", workInvoiceId);
            jedis.hPut(redisKey, "informType", informType);
            jedis.hPut(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
            jedis.expire(redisKey, 14400); // 1天过期

            // ========== 追加：封装Redis数据→转JSON→更新发票表（和红冲方法逻辑一致） ==========
            long redisStoreTime = System.currentTimeMillis() / 1000; // 秒级存储时间，用于后期回滚
            // 封装Map：和Redis中存储的键值完全一致，方便后期回滚解析
            Map<String, Object> downloadTaskMap = new HashMap<>(10);
            downloadTaskMap.put("accessToken", accessToken);
            downloadTaskMap.put("workInvoiceId", workInvoiceId);
            downloadTaskMap.put("informType", informType);
            downloadTaskMap.put("firstExecTime", String.valueOf(System.currentTimeMillis())); // 和Redis保持一致的毫秒数字符串
            downloadTaskMap.put("redisKey", redisKey);
            downloadTaskMap.put("redisExpireSeconds", 14400);
            downloadTaskMap.put("redisStoreTime", redisStoreTime); // 新增秒级存储时间

            // Map转JSON字符串（单独捕获序列化异常，不影响核心逻辑）
            String downloadTaskJson = objectMapper.writeValueAsString(downloadTaskMap);

            // 更新发票表（和红冲方法用相同的DAO/实体，保持风格统一）
            CwFinanceInvoiceDTO workInvoice = new CwFinanceInvoiceDTO();
            workInvoice.setId(workInvoiceId);
            workInvoice.setBlueDownloadInvoiceJson(downloadTaskJson);
            workInvoiceDao.updateRedInvoiceJsonByWorkInvoiceId(workInvoice);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                //jedis.close();
            }
        }
    }

    /**
     * 兜底方法
     * @param accessToken
     */
    @Transactional(readOnly = false)
    public void handleInvoiceRetryAllFail(String accessToken, String workInvoiceId, String errorMessage, String informType) {
        // ============ 你的所有【修改系统信息/更新订单状态/写失败日志】逻辑 全部写在这里！！！ ============
        try {
            System.err.println("============ 开始执行【失败兜底-系统信息修改逻辑】 ============");
            CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);
            if(null != workInvoice) {

                if (errorMessage.contains("订单已存在")) {
                    //如果开过票
                    workInvoice.setStatus("12");
                    //同时需要将下载的redis进行恢复，以及保存到数据库中
                    RedisUtils jedis = null;
                    try {
                        jedis = RedisUtils.getInstance();
                        String redisKey = "OMS_invoice_download:" + workInvoiceId;
                        jedis.hPut(redisKey, "accessToken", accessToken);
                        jedis.hPut(redisKey, "workInvoiceId", workInvoiceId);
                        jedis.hPut(redisKey, "informType", informType);
                        jedis.hPut(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
                        jedis.expire(redisKey, 14400); // 2小时过期

                        // ========== 追加：封装Redis数据→转JSON→更新发票表（和红冲方法逻辑一致） ==========
                        long redisStoreTime = System.currentTimeMillis() / 1000; // 秒级存储时间，用于后期回滚
                        // 封装Map：和Redis中存储的键值完全一致，方便后期回滚解析
                        Map<String, Object> downloadTaskMap = new HashMap<>(10);
                        downloadTaskMap.put("accessToken", accessToken);
                        downloadTaskMap.put("workInvoiceId", workInvoiceId);
                        downloadTaskMap.put("informType", informType);
                        downloadTaskMap.put("firstExecTime", String.valueOf(System.currentTimeMillis())); // 和Redis保持一致的毫秒数字符串
                        downloadTaskMap.put("redisKey", redisKey);
                        downloadTaskMap.put("redisExpireSeconds", 14400);
                        downloadTaskMap.put("redisStoreTime", redisStoreTime); // 新增秒级存储时间

                        // Map转JSON字符串（单独捕获序列化异常，不影响核心逻辑）
                        String downloadTaskJson = objectMapper.writeValueAsString(downloadTaskMap);

                        // 更新发票表（和红冲方法用相同的DAO/实体，保持风格统一）
                        workInvoice.setBlueDownloadInvoiceJson(downloadTaskJson);
                        workInvoiceDao.updateRedInvoiceJsonByWorkInvoiceId(workInvoice);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else if (errorMessage.contains("该发票已经开过票")) {
                    workInvoice.setStatus("5");
                } else {
                    workInvoice.setStatus("3");
                }
                //修改结果
                workInvoiceDao.updateAccessTokenErrorById(workInvoice);
                if (!"12".equals(workInvoice.getStatus())) {
                    //通知发起人或者开票管理员
                    workInvoiceService.handleInvoiceRetryAllFail(workInvoice, errorMessage, informType);
                }
            }
            System.err.println("============ 【失败兜底-系统信息修改逻辑】执行完成 ============");

        } catch (Exception e) {
            // 必加：捕获这个方法的异常，避免兜底方法报错导致线程异常
            e.printStackTrace();
            System.err.println("❌ 执行失败兜底方法时抛出异常：" + e.getMessage());
        }
    }


    /**
     * 快速开红票的调用方法
     * @param map
     * @param allEinvno
     */
    @Transactional(readOnly = false)
    public void doFastRedInvoiceBusiness(Map<String,Object> map, String allEinvno, String workInvoiceId, String informType) {
        RedisUtils jedis = null;
        String accessToken = null;
        try {
            jedis = RedisUtils.getInstance();
            accessToken = (String) jedis.get("OMSAccessToken");
            if(StringUtils.isBlank(accessToken)){
                // 获取AccessToken 9998重试5次
                accessToken = getOmsAccessTokenWithRetry(10, "accessToken", workInvoiceId, informType);
                if(StringUtils.isNotBlank(accessToken)){
                    jedis.setEx("OMSAccessToken", accessToken,86400,TimeUnit.SECONDS);
                    map.put("token状态", "重新获取token成功，存入Redis");
                } else {
                    accessToken = "";
                    map.put("token状态", "获取token失败");
                    return;
                }
            } else {
                map.put("token状态", "从Redis获取token成功");
            }

            OMSNationUtil util = new OMSNationUtil();
            String string = util.neatenFastRedInvoiceData(allEinvno);

            OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
            InvoiceTokenInfo.setAppId(appId);
            InvoiceTokenInfo.setAppKey(appKey);
            InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
            InvoiceTokenInfo.setAccessToken(accessToken);
            InvoiceTokenInfo.setData(string);
            String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);

            String jsonInvoicResultStr = HttpPostJsonUtil.doPost(omsUrl +"/prod-api/output/server/invoice/makeredinv", jsonInvoiceStr);
            System.out.println("✅ 快速红冲订单提交接口返回值：" + jsonInvoicResultStr);
            map.put("快速红冲订单接口信息", jsonInvoicResultStr);

            // 调用订单上传重试方法（包含所有码值规则）
            if(StringUtils.isNotBlank(jsonInvoicResultStr)){
                String finalAccessToken = accessToken;
                String finalJsonInvoiceStr = jsonInvoiceStr;
                executeOrderUploadRetry(workInvoiceId, 5, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, map, "orderno", "fastRed", informType);
            }

        } catch (Exception e) {
            e.printStackTrace();
            map.put("errorMsg", "系统异常：" + e.getMessage());
        } finally {
            if(jedis != null){
                //jedis.close();
            }
        }
    }


    /**
     * 调用生成红字确认申请单
     * @param map
     * @param workInvoiceId 需要开红字票的开票id信息
     * @param redInvoiceRelevancyId 开红票对应蓝票的invoiceid
     * @param originalInvno 原蓝票发票号码（数电票号码）
     * @param informType 用于流程通知的判定条件
     */
    @Transactional(readOnly = false)
    public void doAllScenarioRedInvoiceBusiness(Map<String,Object> map, String workInvoiceId, String redInvoiceRelevancyId, String originalInvno, String informType) {
        RedisUtils jedis = null;
        String accessToken = null;
        try {
            jedis = RedisUtils.getInstance();
            accessToken = (String)jedis.get("OMSAccessToken");
            if(StringUtils.isBlank(accessToken)){
                // 获取AccessToken 9998重试5次
                accessToken = getOmsAccessTokenWithRetry(10, "accessToken", workInvoiceId, informType);
                if(StringUtils.isNotBlank(accessToken)){
                    jedis.setEx("OMSAccessToken", accessToken,86400,TimeUnit.SECONDS);
                    map.put("token状态", "重新获取token成功，存入Redis");
                } else {
                    accessToken = "";
                    map.put("token状态", "获取token失败");
                    return;
                }
            } else {
                map.put("token状态", "从Redis获取token成功");
            }

            OMSNationUtil util = new OMSNationUtil();
            //获取需要开票的发票信息
            CwFinanceInvoiceDTO workInvoice = workInvoiceService.queryById(workInvoiceId);

            if(null != workInvoice){

                if(StringUtils.isBlank(workInvoice.getBillingPeopleReal()) ){
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                    return;
                }
                //获取开票银行信息
                if(StringUtils.isBlank(workInvoice.getBankAccount())){
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                    return;
                }

                //获取被红冲发票的基本信息


                if(StringUtils.isNotBlank(workInvoice.getBillingContent())){
                    WorkInvoiceTaxClassificationCode billingContentDetail = workInvoiceDao.getBillingContentDetail(workInvoice);
                    if(null != billingContentDetail){
                        workInvoice.setBillingContent(billingContentDetail.getGoodName());
                        workInvoice.setGoodsTaxno(billingContentDetail.getGoodsTaxno());
                    }else{
                        handleInvoiceRetryAllFail("",workInvoiceId, "未找到对应税收编码，请联系信息部确认后重新发起", informType); // 解析失败直接兜底
                        return;
                    }

                    //获取本次红冲对应蓝票的基本信息
                    CwFinanceInvoiceDTO workInvoiceRelevancy = workInvoiceService.queryById(redInvoiceRelevancyId);
                    if(null == workInvoiceRelevancy || null == workInvoiceRelevancy.getBillingPeopleReal()){
                        handleInvoiceRetryAllFail("",workInvoiceId, "未找到需要红冲票的信息，请确认后重新发起", informType); // 解析失败直接兜底
                        return;
                    }
                    //获取被红冲蓝票的税率信息
                    WorkInvoiceTaxClassificationCode relevancyBillingContentDetail = workInvoiceDao.getBillingContentDetail(workInvoiceRelevancy);
                    if(null == relevancyBillingContentDetail){
                        handleInvoiceRetryAllFail("",workInvoiceId, "未找到被红冲蓝票的对应税收编码，请联系信息部确认后重新发起", informType); // 解析失败直接兜底
                        return;
                    }

                    //生成红冲的数据
                    String string = util.neatenAllScenarioRedInvoiceData(workInvoiceId, originalInvno,workInvoice, workInvoiceRelevancy, billingContentDetail,relevancyBillingContentDetail);

                    OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
                    InvoiceTokenInfo.setAppId(appId);
                    InvoiceTokenInfo.setAppKey(appKey);
                    InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
                    InvoiceTokenInfo.setAccessToken(accessToken);
                    InvoiceTokenInfo.setData(string);
                    String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);

                    String jsonInvoicResultStr = HttpPostJsonUtil.doPost(omsUrl +"/prod-api/output/server/redApply/apply", jsonInvoiceStr);
                    System.out.println("✅ 全场景红冲订单提交接口返回值：" + jsonInvoicResultStr);
                    map.put("全场景红冲订单接口信息", jsonInvoicResultStr);

                    // 调用订单上传重试方法（包含所有码值规则）
                    if(StringUtils.isNotBlank(jsonInvoicResultStr)){

                        //将上个节点获取的值存到数据库，留作redis崩溃后进行重新获取处理
                        workInvoice.setOrderForGoodsResultStr(jsonInvoicResultStr);
                        workInvoiceDao.updateOrderForGoodsResultStrById(workInvoice);


                        String finalAccessToken = accessToken;
                        queryRedInvoiceConfirm (workInvoiceId, 5, jsonInvoicResultStr, finalAccessToken,informType);
                    } else {
                        handleInvoiceRetryAllFail(accessToken,workInvoiceId, "全场景红冲订单接口返回值为空，开票失败。节点申请参数为："+ jsonInvoiceStr, informType); // 解析失败直接兜底
                        return;
                    }
                }else{
                    handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                    return;
                }

            }else{
                handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败", informType); // 解析失败直接兜底
                return;
            }


        } catch (Exception e) {
            e.printStackTrace();
            map.put("errorMsg", "系统异常：" + e.getMessage());
        } finally {
            if(jedis != null){
                //jedis.close();
            }
        }
    }

    /**
     * 全类型红冲--红字确认单查询接口（仅存Redis，不执行业务）
     * @param remainRetryTimes  剩余重试次数
     * @param jsonInvoicResultStr   需要解析的返回值密文
     * @param accessToken   accessToken
     * @param workInvoiceId 开票的id
     */
    @Transactional(readOnly = false)
    public void queryRedInvoiceConfirm (String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String accessToken, String informType) {
        try {
            OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
            if(null == resultTokenInfo || null == resultTokenInfo.getResult()){
                System.err.println("❌ 全场景红冲订单提交接口返回值解析失败，剩余重试次数："+remainRetryTimes);
                handleInvoiceRetryAllFail(accessToken, workInvoiceId, "", informType); // 解析失败也执行兜底方法
                return;
            }
            String code = resultTokenInfo.getResult().getCode();

            if ("0000".equals(code)) {
                // 仅存入Redis，删除所有业务执行逻辑
                saveRedInvoiceTaskToRedis(workInvoiceId, remainRetryTimes, jsonInvoicResultStr, accessToken, workInvoiceId, System.currentTimeMillis(),informType);
                System.out.println("✅ 红冲任务["+workInvoiceId+"]已存入Redis，由定时任务接管执行");
            } else {
                String message = resultTokenInfo.getResult().getMessage();
                System.err.println("❌ 全类型红冲--红字确认单查询接口返回业务错误码："+code+"，错误原因为："+message);
                handleInvoiceRetryAllFail(accessToken, workInvoiceId, message, informType); // 解析失败也执行兜底方法
            }
        } catch (Exception e) {
            e.printStackTrace();
            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "全类型红冲--红字确认单查询接口发起失败，请重新发起", informType); // 解析失败也执行兜底方法
        }
    }

    /**
     * 存储红冲任务到Redis（供原有业务代码调用，参数完整）
     * @param workInvoiceId 发票id
     * @param remainRetryTimes  允许回调的次数，这里暂时未用到，仅作为保留参数
     * @param jsonInvoicResultStr   全场景红冲订单提交接口返回值
     * @param accessToken
     * @param applyNo   发票id
     * @param startTime 这个任务开始时间。限制：开始3天内如果双方没有全部确认，则本次红冲失败
     */
    @Transactional(readOnly = false)
    public void saveRedInvoiceTaskToRedis(String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String accessToken, String applyNo, long startTime, String informType) {
        RedisUtils jedis = null;
        try {
            jedis = RedisUtils.getInstance();
            String redisKey = "OMS_red_invoice_task:" + workInvoiceId;
            // 存储所有执行所需的参数（确保定时任务能独立执行）
            jedis.hPut(redisKey, "remainRetryTimes", String.valueOf(remainRetryTimes));
            jedis.hPut(redisKey, "jsonInvoicResultStr", jsonInvoicResultStr);
            jedis.hPut(redisKey, "accessToken", accessToken);
            jedis.hPut(redisKey, "workInvoiceId", workInvoiceId);
            jedis.hPut(redisKey, "informType", informType);
            jedis.hPut(redisKey, "startTime", String.valueOf(startTime));
            jedis.expire(redisKey, 259200 + 3600); // 3天+1小时过期



            //将对应的redis中数据进行存储到数据库中，放置redis数据崩溃丢失
            // 1. 封装Redis中所有存储的参数到Map（和Redis键值完全一致，方便后期回滚解析）
            long redisStoreTime = System.currentTimeMillis() / 1000; // 核心：精确到秒，long类型
            Map<String, Object> redInvoiceMap = new HashMap<>(); // 容量从8改为10，适配新字段
            redInvoiceMap.put("remainRetryTimes", remainRetryTimes);
            redInvoiceMap.put("jsonInvoicResultStr", jsonInvoicResultStr);
            redInvoiceMap.put("accessToken", accessToken);
            redInvoiceMap.put("workInvoiceId", workInvoiceId);
            redInvoiceMap.put("informType", informType);
            redInvoiceMap.put("startTime", startTime);
            redInvoiceMap.put("redisKey", redisKey);
            redInvoiceMap.put("redisExpireSeconds", 259200 + 3600);
            redInvoiceMap.put("redisStoreTime", redisStoreTime); // 新增：Redis存储时间（秒级时间戳）

            // 2. Map转JSON字符串（Spring内置Jackson，无额外依赖）
            String redInvoiceJson = objectMapper.writeValueAsString(redInvoiceMap);

            CwFinanceInvoiceDTO workInvoice = new CwFinanceInvoiceDTO();
            workInvoice.setId(workInvoiceId);
            workInvoice.setRedInvoiceJson(redInvoiceJson);
            // 3. 更新现有发票表的JSON字段（根据发票ID精准更新）
            workInvoiceDao.updateRedInvoiceJsonByWorkInvoiceId(workInvoice);

        } catch (Exception e) {
            e.printStackTrace();
            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "全类型红冲--红字确认单查询接口发起失败，请重新发起", informType); // 解析失败也执行兜底方法
        } finally {
            if (jedis != null) {
                //jedis.close();
            }
        }
    }


    // ========== 辅助方法：删除Redis解析任务 ==========
    private void deleteInvoiceDownloadTaskFromRedis(String orderno) {
        RedisUtils jedis = null;
        try {
            jedis = RedisUtils.getInstance();
            jedis.delete("OMS_invoice_download:" + orderno);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                //jedis.close();
            }
        }
    }

}
