package com.qqchen.deploy.backend.system.service.impl;

import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.notification.dto.WeworkSendNotificationRequest;
import com.qqchen.deploy.backend.notification.entity.NotificationChannel;
import com.qqchen.deploy.backend.notification.enums.WeworkMessageTypeEnum;
import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository;
import com.qqchen.deploy.backend.notification.service.INotificationSendService;
import com.qqchen.deploy.backend.system.dto.SystemReleaseDTO;
import com.qqchen.deploy.backend.system.entity.SystemRelease;
import com.qqchen.deploy.backend.system.query.SystemReleaseQuery;
import com.qqchen.deploy.backend.system.repository.ISystemReleaseRepository;
import com.qqchen.deploy.backend.system.service.ISystemReleaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

/**
 * 系统版本发布记录服务实现
 */
@Slf4j
@Service
@ServiceType(ServiceType.Type.DATABASE)
public class SystemReleaseServiceImpl
        extends BaseServiceImpl<SystemRelease, SystemReleaseDTO, SystemReleaseQuery, Long>
        implements ISystemReleaseService {

    private final ISystemReleaseRepository systemReleaseRepository;

    @Autowired
    private TaskScheduler taskScheduler;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private INotificationChannelRepository notificationChannelRepository;

    @Autowired
    private INotificationSendService notificationSendService;

    @Value("${deploy.notification.release.channel-id}")
    private Long notificationChannelId;

    /**
     * 维护任务映射：releaseId -> ScheduledFuture
     * 用于跟踪和取消已调度的维护任务
     */
    private final Map<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();

    public SystemReleaseServiceImpl(ISystemReleaseRepository systemReleaseRepository) {
        this.systemReleaseRepository = systemReleaseRepository;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SystemReleaseDTO create(SystemReleaseDTO dto) {
        // 调用父类方法保存
        SystemReleaseDTO savedDto = super.create(dto);
        
        // 如果设置了延迟时间，则调度维护任务（无论是否自动停止服务）
        if (dto.getDelayMinutes() != null && dto.getDelayMinutes() > 0) {
            SystemRelease release = converter.toEntity(savedDto);
            scheduleMaintenanceTask(release);
        }
        
        return savedDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SystemReleaseDTO update(Long id, SystemReleaseDTO dto) {
        // 先取消旧的维护任务
        cancelMaintenanceTask(id);
        
        // 调用父类方法更新
        SystemReleaseDTO updatedDto = super.update(id, dto);
        
        // 如果设置了延迟时间，重新调度维护任务
        if (dto.getDelayMinutes() != null && dto.getDelayMinutes() > 0) {
            SystemRelease release = converter.toEntity(updatedDto);
            scheduleMaintenanceTask(release);
        }
        
        return updatedDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        // 取消已调度的维护任务
        cancelMaintenanceTask(id);
        
        // 调用父类方法删除
        super.delete(id);
    }

    /**
     * 取消维护任务
     */
    private void cancelMaintenanceTask(Long releaseId) {
        ScheduledFuture<?> future = scheduledTasks.remove(releaseId);
        if (future != null && !future.isDone()) {
            boolean cancelled = future.cancel(false);
            log.warn("取消维护任务，Release ID: {}, 取消结果: {}", releaseId, cancelled);
        } else {
            log.debug("没有找到待取消的维护任务，Release ID: {}", releaseId);
        }
    }

    /**
     * 调度维护任务（延迟执行）
     */
    private void scheduleMaintenanceTask(SystemRelease release) {
        // 1. 立即发送维护通知（告知用户系统将在X分钟后维护）
        sendMaintenanceNotification(release);
        
        // 2. 计算执行时间
        Date executeTime = Date.from(
            LocalDateTime.now()
                .plusMinutes(release.getDelayMinutes())
                .atZone(ZoneId.systemDefault())
                .toInstant()
        );
        
        log.warn("已调度系统维护任务，版本: {}, 执行时间: {}, 预计耗时: {}分钟", 
            release.getReleaseVersion(), executeTime, release.getEstimatedDuration());
        
        // 3. 延迟执行维护任务（只负责关闭应用，不再发送通知）
        ScheduledFuture<?> future = taskScheduler.schedule(() -> {
            executeMaintenance(release);
            // 执行完成后从Map中移除
            scheduledTasks.remove(release.getId());
        }, executeTime);
        
        // 保存任务引用，用于后续取消
        scheduledTasks.put(release.getId(), future);
        
        log.info("维护任务已保存到调度器，Release ID: {}", release.getId());
    }

    /**
     * 执行维护任务（仅负责关闭应用）
     */
    private void executeMaintenance(SystemRelease release) {
        try {
            log.warn("========================================");
            log.warn("系统维护任务开始执行");
            log.warn("版本: {}", release.getReleaseVersion());
            log.warn("预计耗时: {} 分钟", release.getEstimatedDuration());
            log.warn("自动停止服务: {}", release.getEnableAutoShutdown());
            log.warn("========================================");
            
            // 1. 检查是否需要自动停止服务
            if (!Boolean.TRUE.equals(release.getEnableAutoShutdown())) {
                log.warn("未启用自动停止服务，请手动停止服务进行维护");
                return;
            }
            
            // 2. 优雅关闭应用（使用独立线程，避免调度器死锁）
            log.warn("系统开始优雅关闭...");
            log.info("使用独立线程执行应用关闭，避免调度器死锁");
            
            // 创建独立的关闭线程
            Thread shutdownThread = new Thread(() -> {
                try {
                    // 等待1秒，确保维护任务线程正常退出
                    Thread.sleep(1000);
                    log.warn("执行应用上下文关闭...");
                    ((ConfigurableApplicationContext) applicationContext).close();
                    log.warn("应用上下文关闭完成");
                } catch (Exception e) {
                    log.error("应用关闭失败", e);
                    // 如果优雅关闭失败，强制退出
                    System.exit(1);
                }
            }, "app-shutdown-thread");
            
            shutdownThread.setDaemon(false); // 非守护线程，确保执行完成
            shutdownThread.start();
            
            log.info("关闭指令已发出，维护任务线程即将退出");
            
        } catch (Exception e) {
            log.error("系统维护任务执行失败", e);
        }
    }

    /**
     * 发送维护通知
     */
    private void sendMaintenanceNotification(SystemRelease release) {
        try {
            // 查询通知渠道
            NotificationChannel channel = notificationChannelRepository.findById(notificationChannelId)
                .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND, 
                    new Object[]{"通知渠道ID=" + notificationChannelId + "不存在"}));
            
            // 准备维护通知内容（说明将在X分钟后维护）
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.append(String.format("版本号：%s\n", release.getReleaseVersion()));
            messageBuilder.append(String.format("系统将在 %d 分钟后开始维护升级\n", release.getDelayMinutes()));
            messageBuilder.append(String.format("预计维护时长：%d 分钟\n", 
                release.getEstimatedDuration() != null ? release.getEstimatedDuration() : 10));
            
            // 根据模块类型显示不同的提示信息
            if ("FRONTEND".equals(release.getModule())) {
                // 前端更新：无感升级，无需停止服务
                messageBuilder.append("本次为前端更新，无感升级，无需停止服务\n");
            } else {
                // 后端或全栈更新：根据是否自动停止显示不同提示
                if (Boolean.TRUE.equals(release.getEnableAutoShutdown())) {
                    messageBuilder.append("本次升级程序将自动停止\n");
                } else {
                    messageBuilder.append("请在维护时间前做好准备\n");
                }
            }
            
            messageBuilder.append("\n望周知。");
            String message = messageBuilder.toString();
            
            log.warn("发送维护通知（v{}）：\n{}", release.getReleaseVersion(), message);
            
            // 创建企微通知请求
            WeworkSendNotificationRequest request = new WeworkSendNotificationRequest();
            request.setContent(message);
            request.setTitle("系统维护通知");
            request.setMessageType(WeworkMessageTypeEnum.TEXT);
            
            // 发送通知
            notificationSendService.send(channel, request);
            
            log.info("维护通知发送成功");
            
        } catch (Exception e) {
            log.error("发送维护通知失败", e);
        }
    }

    @Override
    public List<SystemReleaseDTO> getUnnotifiedReleases() {
        log.info("获取未通知的发布记录列表");
        List<SystemRelease> releases = systemReleaseRepository.findByNotifiedFalseAndDeletedFalseOrderByReleaseDateDesc();
        return releases.stream()
                .map(converter::toDto)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void markAsNotified(Long id) {
        log.info("标记发布记录为已通知，ID: {}", id);
        SystemRelease release = systemReleaseRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("发布记录不存在"));
        release.setNotified(true);
        systemReleaseRepository.save(release);
    }

    @Override
    public SystemReleaseDTO getLatestReleaseByModule(String module) {
        log.info("获取模块最新发布记录，模块: {}", module);
        SystemRelease release = systemReleaseRepository.findTopByModuleAndDeletedFalseOrderByReleaseDateDesc(module);
        return release != null ? converter.toDto(release) : null;
    }
}
