package com.ruoyi.jarvis.service.impl;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.jarvis.config.TencentDocConfig;
import com.ruoyi.jarvis.wecom.WxSendGoofishNotifyClient;
import com.ruoyi.jarvis.service.ITencentDocBatchPushService;
import com.ruoyi.jarvis.service.ITencentDocDelayedPushService;
import com.ruoyi.jarvis.service.ITencentDocTokenService;
import com.ruoyi.jarvis.service.ITencentDocService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 腾讯文档延迟推送服务实现
 * 
 * 功能说明：
 * 1. 录单时触发10分钟倒计时
 * 2. 10分钟内有新录单，重置倒计时
 * 3. 10分钟到期后自动执行推送
 * 4. 推送执行期间有录单，推送完成后重新开始倒计时
 * 5. 使用分布式锁防止并发推送
 * 
 * @author system
 */
@Service
public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushService, ApplicationContextAware {
    
    private static final Logger log = LoggerFactory.getLogger(TencentDocDelayedPushServiceImpl.class);
    
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ITencentDocBatchPushService batchPushService;

    @Autowired
    private WxSendGoofishNotifyClient wxSendGoofishNotifyClient;

    @Autowired
    private TencentDocConfig tencentDocConfig;

    @Autowired
    private ITencentDocTokenService tokenService;

    @Autowired
    private ITencentDocService tencentDocService;

    private ApplicationContext applicationContext;

    private static final int READ_ROWS_WHEN_USE_ROW_TOTAL = 200;
    private static final int MIN_START_ROW_WHEN_USE_ROW_TOTAL = 3;
    
    /**
     * 延迟时间（分钟），可通过配置文件修改
     */
    @Value("${tencent.doc.delayed.push.minutes:10}")
    private int delayMinutes;
    
    /**
     * Redis Key - 存储下次推送的时间戳
     */
    private static final String REDIS_KEY_NEXT_PUSH_TIME = "tendoc:delayed_push:next_time";
    
    /**
     * Redis Key - 推送执行锁
     */
    private static final String REDIS_KEY_PUSH_LOCK = "tendoc:delayed_push:lock";
    
    /**
     * Redis Key - 推送期间有新录单标记
     */
    private static final String REDIS_KEY_NEW_ORDER_FLAG = "tendoc:delayed_push:new_order_flag";
    
    /**
     * 定时任务执行器
     */
    private ScheduledExecutorService scheduler;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 初始化定时任务
     */
    @PostConstruct
    public void init() {
        // 创建单线程的定时任务执行器
        scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread thread = new Thread(r, "TencentDoc-DelayedPush-Thread");
            thread.setDaemon(true);
            return thread;
        });
        
        // 每30秒检查一次是否需要推送
        scheduler.scheduleWithFixedDelay(this::checkAndExecutePush, 30, 30, TimeUnit.SECONDS);
        
        log.info("腾讯文档延迟推送服务已启动，延迟时间: {} 分钟", delayMinutes);
    }
    
    /**
     * 关闭定时任务
     */
    @PreDestroy
    public void destroy() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        log.info("腾讯文档延迟推送服务已关闭");
    }
    
    @Override
    public void triggerDelayedPush() {
        try {
            // 计算下次推送时间 = 当前时间 + 延迟分钟数
            long nextPushTime = System.currentTimeMillis() + (delayMinutes * 60 * 1000L);
            
            // 检查是否正在执行推送
            String lockValue = redisCache.getCacheObject(REDIS_KEY_PUSH_LOCK);
            if (lockValue != null && "locked".equals(lockValue)) {
                // 正在推送中，标记有新订单，推送完成后会重新触发
                redisCache.setCacheObject(REDIS_KEY_NEW_ORDER_FLAG, "true", 1, TimeUnit.HOURS);
                log.info("推送执行中，标记有新订单，推送完成后将重新开始倒计时");
                return;
            }
            
            // 更新下次推送时间
            redisCache.setCacheObject(REDIS_KEY_NEXT_PUSH_TIME, nextPushTime, delayMinutes + 5, TimeUnit.MINUTES);
            
            log.info("触发延迟推送，{}分钟后执行（{}）", delayMinutes, 
                new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date(nextPushTime)));
            
        } catch (Exception e) {
            log.error("触发延迟推送失败", e);
        }
    }
    
    @Override
    public void executePushNow() {
        log.info("手动触发立即推送");
        // 清除待推送标记
        redisCache.deleteObject(REDIS_KEY_NEXT_PUSH_TIME);
        // 执行推送
        doExecutePush();
    }
    
    @Override
    public long getRemainingSeconds() {
        try {
            Long nextPushTime = redisCache.getCacheObject(REDIS_KEY_NEXT_PUSH_TIME);
            if (nextPushTime == null) {
                return -1;
            }
            
            long remaining = (nextPushTime - System.currentTimeMillis()) / 1000;
            return remaining > 0 ? remaining : 0;
        } catch (Exception e) {
            log.error("获取剩余时间失败", e);
            return -1;
        }
    }
    
    @Override
    public void cancelPendingPush() {
        redisCache.deleteObject(REDIS_KEY_NEXT_PUSH_TIME);
        log.info("已取消待推送任务");
    }
    
    /**
     * 定时检查并执行推送
     */
    private void checkAndExecutePush() {
        try {
            // 获取下次推送时间
            Long nextPushTime = redisCache.getCacheObject(REDIS_KEY_NEXT_PUSH_TIME);
            if (nextPushTime == null) {
                // 没有待推送任务
                return;
            }
            
            long now = System.currentTimeMillis();
            if (now < nextPushTime) {
                // 还没到推送时间
                long remainingSeconds = (nextPushTime - now) / 1000;
                log.debug("距离下次推送还有 {} 秒", remainingSeconds);
                return;
            }
            
            // 时间到了，执行推送
            log.info("倒计时结束，开始执行推送");
            doExecutePush();
            
        } catch (Exception e) {
            log.error("检查推送任务失败", e);
        }
    }
    
    /**
     * 执行推送
     */
    private void doExecutePush() {
        String lockValue = null;
        try {
            // 1. 尝试获取分布式锁
            lockValue = redisCache.getCacheObject(REDIS_KEY_PUSH_LOCK);
            if (lockValue != null && "locked".equals(lockValue)) {
                log.warn("推送任务已在执行中，跳过本次推送");
                return;
            }
            
            // 2. 加锁（30分钟超时，防止死锁）
            redisCache.setCacheObject(REDIS_KEY_PUSH_LOCK, "locked", 30, TimeUnit.MINUTES);
            log.info("✓ 获取推送锁成功，开始执行推送");
            
            // 3. 清除待推送标记
            redisCache.deleteObject(REDIS_KEY_NEXT_PUSH_TIME);
            
            // 4. 清除新订单标记
            redisCache.deleteObject(REDIS_KEY_NEW_ORDER_FLAG);
            
            // 5. 调用批量同步接口
            // 注意：这里需要通过HTTP调用Controller的接口，或者注入Controller的方法
            // 为了避免循环依赖，这里使用Spring的ApplicationContext来获取Bean
            executeBatchSync();
            
            log.info("✓ 推送执行完成");
            
        } catch (Exception e) {
            log.error("❌ 推送执行失败", e);
        } finally {
            // 6. 释放锁
            try {
                redisCache.deleteObject(REDIS_KEY_PUSH_LOCK);
                log.info("✓ 释放推送锁");
            } catch (Exception e) {
                log.error("释放推送锁失败", e);
            }
            
            // 7. 检查是否有新订单标记
            String newOrderFlag = redisCache.getCacheObject(REDIS_KEY_NEW_ORDER_FLAG);
            if (newOrderFlag != null && "true".equals(newOrderFlag)) {
                log.info("推送期间有新订单，重新开始倒计时");
                redisCache.deleteObject(REDIS_KEY_NEW_ORDER_FLAG);
                triggerDelayedPush();
            }
        }
    }
    
    /**
     * 执行批量同步
     * 
     * 说明：创建批量推送记录，然后通过HTTP调用本地接口
     */
    private void executeBatchSync() {
        String batchId = null;
        try {
            log.info("开始执行批量同步...");
            
            // 从 Redis 读取 fileId、sheetId（仅文档标识，无行数缓存）
            final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:";
            String fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
            String sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId");

            if (StringUtils.isEmpty(fileId) || StringUtils.isEmpty(sheetId)) {
                log.error("腾讯文档配置不完整，无法执行批量同步。请先在前端配置页面设置文件ID和工作表ID");
                return;
            }

            // 每次从接口获取当前行数，取最后 200 行，无任何缓存
            int batchStartRow;
            int batchEndRow;
            try {
                String accessToken = tokenService.getValidAccessToken();
                if (accessToken == null) {
                    log.warn("无法获取 accessToken，跳过本次批量同步");
                    return;
                }
                int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId);
                if (rowCount <= 0) {
                    log.warn("接口未返回有效行数，跳过本次批量同步");
                    return;
                }
                batchStartRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowCount - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1));
                batchEndRow = rowCount;
            } catch (Exception e) {
                log.warn("获取表格行数失败，跳过本次批量同步: {}", e.getMessage());
                return;
            }

            log.info("本次范围（接口 rowCount 取最后200行）- fileId: {}, sheetId: {}, {} ~ {}", fileId, sheetId, batchStartRow, batchEndRow);
            
            // 创建批量推送记录（使用有效范围）
            batchId = batchPushService.createBatchPushRecord(
                fileId, 
                sheetId, 
                "AUTO", 
                "DELAYED_TIMER", 
                batchStartRow, 
                batchEndRow
            );
            log.info("✓ 创建批量推送记录，批次ID: {}", batchId);
            
            // 直接通过 ApplicationContext 获取 Controller Bean 并调用方法
            // 这样避免了 HTTP 调用，是后端内部方法调用
            try {
                log.info("开始调用批量同步方法（后端内部调用）...");
                
                // 获取 TencentDocController Bean
                Object controller = applicationContext.getBean("tencentDocController");
                
                // 通过反射调用 fillLogisticsByOrderNo 方法
                java.lang.reflect.Method method = controller.getClass().getMethod(
                    "fillLogisticsByOrderNo", 
                    java.util.Map.class
                );
                
                // 构造参数
                java.util.Map<String, Object> params = new java.util.HashMap<>();
                params.put("batchId", batchId);
                params.put("fileId", fileId);
                params.put("sheetId", sheetId);
                
                // 调用方法
                Object result = method.invoke(controller, params);
                
                log.info("✓ 批量同步执行完成，结果: {}", result);
                if (result instanceof AjaxResult) {
                    AjaxResult ar = (AjaxResult) result;
                    if (!ar.isSuccess() && batchId != null) {
                        Object msgObj = ar.get(AjaxResult.MSG_TAG);
                        String msg = msgObj != null ? String.valueOf(msgObj) : "同步接口返回失败";
                        batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
                        wxSendGoofishNotifyClient.pushGoofishAgentText(null, "",
                            "【腾讯文档推送】定时批量同步失败\n批次: " + batchId + "\n" + msg);
                    }
                }
                // 不再将 nextStartRow 写入 Redis，下次定时执行时从接口获取 rowCount 决定范围
                
            } catch (Exception ex) {
                log.error("批量同步调用失败", ex);
                if (batchId != null) {
                    String msg = "批量同步调用失败: " + (ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName());
                    batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
                    wxSendGoofishNotifyClient.pushGoofishAgentText(null, "",
                        "【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg);
                }
            }
            
        } catch (Exception e) {
            log.error("执行批量同步失败", e);
            // 更新批量推送记录为失败状态
            if (batchId != null) {
                try {
                    String msg = "执行批量同步失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName());
                    batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
                    wxSendGoofishNotifyClient.pushGoofishAgentText(null, "",
                        "【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg);
                } catch (Exception ex) {
                    log.error("更新批量推送记录失败", ex);
                }
            }
            throw new RuntimeException("执行批量同步失败", e);
        }
    }
}

