package cd.casic.ci.process.engine.manager.impl;

import cd.casic.ci.process.engine.constant.EngineRuntimeConstant;
import cd.casic.ci.process.engine.manager.LoggerManager;
import cd.casic.ci.process.engine.runContext.TaskRunContext;
import cd.casic.ci.process.process.dao.pipeline.PipTaskLogDao;
import cd.casic.ci.process.process.dataObject.log.PipTaskLog;
import cd.casic.ci.process.process.dataObject.log.PipTaskLogLine;
import cd.casic.ci.process.process.dataObject.task.PipTask;
import cd.casic.ci.process.process.service.task.TaskService;
import cd.casic.ci.process.process.service.taskLog.PipTaskLogLineService;
import cd.casic.framework.commons.util.network.IpUtil;
import cd.casic.ci.process.util.WebFrameworkUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 *  流水线运行时日志管理
 * */
@Service
@Slf4j
public class MemoryLogManager implements LoggerManager {
    /**
     *  第一级 是taskId，可能同一个账号、IP、建立多个连接所以保存为list
     * */
    private final  Map<String, List<SseEmitter>> taskIdSSEMap = new ConcurrentHashMap<>();
    @Resource
    private TaskService taskService;
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    /**
     *  缓存最近一次执行的日志，key是taskId，val是数据库id（已入库的情况下）用于buffer满了增加日志内容
     *  读取日志的时候同时读取数据库和内存中的日志
     * */
    private final static Map<String,String> taskIdDbMap = new ConcurrentHashMap<>();
    @Resource
    private PipTaskLogDao logDao;
    @Resource
    private PipTaskLogLineService logLineService;
    @Override
    public SseEmitter subscribe(String taskId,HttpServletRequest request) {
        if (request!=null) {
            String ipAddr = IpUtil.getIpAddr(request);
            log.info("当前请求ip：{}",ipAddr);
        }
        SseEmitter emitter = new SseEmitter(30000L);
        emitterInit(emitter,taskId);
        Long loginUserId = WebFrameworkUtils.getLoginUserId();
        log.info("SSE连接建立");
        log.info("当前用户id：{}",loginUserId);
        log.info("当前taskId：{}",loginUserId);
        List<SseEmitter> taskIdSSEList = taskIdSSEMap.computeIfAbsent(taskId, k->new ArrayList<>(1));
        taskIdSSEList.add(emitter);

        return emitter;
    }

    @Override
    public void append(String taskId, String logContent) {
        /**
         *  先往内存里写
         *  内存满4mb入库，查询日志和入库操作用同一把锁
         *  然后新内容推sse
         * */
        // 入库或者更新
        if (taskIdDbMap.containsKey(taskId)) {
            // 之前已经入库过
            // 存在则更新
            String id = taskIdDbMap.get(taskId);
            PipTaskLogLine line = new PipTaskLogLine();
            line.setLogId(id);
            line.setLineContent(logContent);
            logLineService.save(line);
        } else {
            // 不存在就新增
            PipTaskLog pipTaskLog = new PipTaskLog();
            pipTaskLog.setTaskId(taskId);
//            pipTaskLog.setContent(logCache.toString());
            logDao.insert(pipTaskLog);
            taskIdDbMap.put(taskId,pipTaskLog.getId());
            PipTaskLogLine line = new PipTaskLogLine();
            line.setLogId(pipTaskLog.getTaskId());
            line.setLineContent(logContent);
            logLineService.save(line);
        }
        List<SseEmitter> sseEmitters = taskIdSSEMap.get(taskId);
        if (!CollectionUtils.isEmpty(sseEmitters)) {
            for (SseEmitter sseEmitter : sseEmitters) {
                try {
                    sseEmitter.send(logContent);
                } catch (IOException e) {
                    log.error("",e);
                }
            }
        }
    }
    @Transactional(rollbackFor = Exception.class)
    public void flushMemory(List<TaskRunContext> taskContextList){
        log.info("流水线日志开始入库");
        for (TaskRunContext taskRunContext : taskContextList) {
            String taskId = taskRunContext.getContextDef().getId();
            if (taskIdDbMap.containsKey(taskId)) {
                String id = taskIdDbMap.get(taskId);
                taskRunContext.getLocalVariables().put(EngineRuntimeConstant.HIS_LOG_KEY,id);
                List<SseEmitter> sseEmitters = taskIdSSEMap.get(taskId);
                if (!CollectionUtils.isEmpty(sseEmitters)) {
                    sseEmitters.forEach(ResponseBodyEmitter::complete);
                }
            }
        }
    }

    @Override
    public String getLogContent(String taskId) {
        String id = taskIdDbMap.get(taskId);
        if (id != null) {
            List<PipTaskLogLine> lineList = logLineService.getLineListByLogId(id,10000);
            StringBuilder stringBuilder = new StringBuilder();
            for (PipTaskLogLine line : lineList) {
                stringBuilder.append(line.getLineContent());
            }
            return stringBuilder.toString();
        }
        return "";
    }
    public void clear(String pipelineId){
        PipTask query = new PipTask();
        query.setPipelineId(pipelineId);
        List<PipTask> taskList = taskService.getTask(query);
        taskList.forEach(it->taskIdDbMap.remove(it.getId()));
    }

    private void emitterInit(SseEmitter emitter,String taskId){
        // 维持心跳
        ScheduledFuture<?> scheduledFuture = scheduler.scheduleWithFixedDelay(() -> {
            try {
                emitter.send(SseEmitter.event().comment("heartbeat"));
            } catch (IOException e) {
                log.error("", e);
            }
        }, 0, 30, TimeUnit.SECONDS);
        emitter.onCompletion(()->{
            taskIdSSEMap.remove(taskId);
            scheduledFuture.cancel(true);
            log.info("===================taskId:{}断开连接===============",taskId);
        });
        emitter.onError((e)->{
            taskIdSSEMap.remove(taskId);
            scheduledFuture.cancel(true);
            log.error("===================错误，taskId:{}断开连接===============",taskId,e);
        });
        emitter.onTimeout(()->{
            taskIdSSEMap.remove(taskId);
            scheduledFuture.cancel(true);
            log.error("===================超时，taskId:{}断开连接===============",taskId);
        });
    }


}
