package com.njcn.gather.plan.service;

import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Component
public class SseClient {
    private static final Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
    private static final int RECONNECT_DELAY = 5000; // 重连延迟时间，5秒

    /**
     * 创建SSE连接
     *
     * @param uid 用户唯一标识
     * @return SseEmitter 实例
     */
    public SseEmitter createSse(String uid) {
        // 创建一个永不超时的SseEmitter
        SseEmitter sseEmitter = new SseEmitter(0L);
        sseEmitterMap.put(uid, sseEmitter);

        // 完成后的回调，记录日志并从map中移除
        sseEmitter.onCompletion(() -> {
            log.info("[{}]结束连接...................", uid);
            sseEmitterMap.remove(uid);
        });

        // 超时回调，记录日志
        sseEmitter.onTimeout(() -> {
            log.info("[{}]连接超时，准备重连...................", uid);
            scheduleReconnect(uid);
        });

        // 异常回调，记录日志并发送错误事件，然后重试
        sseEmitter.onError(throwable -> {
            log.error("[{}]连接异常,{}", uid, throwable.toString(), throwable);
            try {
                sseEmitter.send(SseEmitter.event()
                        .id(uid)
                        .name("发生异常！")
                        .data("发生异常请重试！")
                        .reconnectTime(RECONNECT_DELAY));
            } catch (IOException e) {
                log.error("[{}]发送错误事件失败", uid, e);
            }
            scheduleReconnect(uid);
        });

        try {
            // 发送初始化事件，设置重连时间
            sseEmitter.send(SseEmitter.event().reconnectTime(RECONNECT_DELAY));
        } catch (IOException e) {
            log.error("[{}]发送初始化事件失败", uid, e);
        }

        log.info("[{}]创建sse连接成功！", uid);
        return sseEmitter;
    }

    /**
     * 给指定用户发送消息（可以定时或在事件发生时调用sseEmitter.send()方法来发送事件。）
     *
     * @param uid       用户唯一标识
     * @param messageId 消息ID
     * @param message   消息内容
     * @return 是否发送成功
     */
    public boolean sendMessage(String uid, String messageId, Object message) {
        if (ObjectUtil.isEmpty(message)) {
            log.warn("参数异常，message 为null");
            return false;
        }
        SseEmitter sseEmitter = sseEmitterMap.get(uid);
        if (sseEmitter == null) {
            log.info("消息推送失败uid:[{}],没有创建连接，请重试。", uid);
            return false;
        }
        try {
            sseEmitter.send(SseEmitter.event().id(messageId).reconnectTime(1 * 60 * 1000L).data(message));
            // log.info("用户{},消息id:{},推送成功:{}", uid, messageId, message);
            return true;
        } catch (IOException e) {
            log.error("用户{},消息id:{},推送IO异常:{}", uid, messageId, e.getMessage());
            // 客户端断开连接属于正常情况，不需要重连，直接移除连接
            sseEmitterMap.remove(uid);
            sseEmitter.complete();
            return false;
        } catch (Exception e) {
            log.error("用户{},消息id:{},推送其他异常:{}", uid, messageId, e.getMessage(), e);
            sseEmitter.complete();
            scheduleReconnect(uid);
            return false;
        }
    }

    /**
     * 断开SSE连接
     *
     * @param uid 用户唯一标识
     */
    public void closeSse(String uid) {
        SseEmitter sseEmitter = sseEmitterMap.remove(uid);
        if (sseEmitter != null) {
            sseEmitter.complete();
            log.info("用户{} 连接已关闭", uid);
        } else {
            log.info("用户{} 连接已关闭，或连接不存在", uid);
        }
    }

    /**
     * 计划重连
     *
     * @param uid 用户唯一标识
     */
    private void scheduleReconnect(String uid) {
        // 这里可以添加一个定时任务来尝试重连
        // 例如使用Spring的@Scheduled注解或者ScheduledExecutorService
        log.info("[{}]计划在{}毫秒后重连", uid, RECONNECT_DELAY);
        // 模拟重连操作，实际应用中应根据业务需求实现
        try {
            Thread.sleep(RECONNECT_DELAY);
            createSse(uid);
        } catch (InterruptedException e) {
            log.error("[{}]重连操作被中断", uid, e);
        }
    }
}