package shop.alien.dining.service.impl;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * SSE推送服务实现类
 *
 * @author system
 * @since 2025-01-XX
 */
@Slf4j
@Service
public class SseServiceImpl implements shop.alien.dining.service.SseService {

    // 存储每个桌号的SSE连接，一个桌号可以有多个连接(多个用户)
    private final ConcurrentHashMap<Integer, ConcurrentHashMap<String, SseEmitter>> connections = new ConcurrentHashMap<>();

    // 定时任务执行器，用于发送心跳
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);

    @Override
    public SseEmitter createConnection(Integer tableId) {
        log.info("创建SSE连接, tableId={}", tableId);

        // 创建SSE连接，设置超时时间为30分钟
        SseEmitter emitter = new SseEmitter(30 * 60 * 1000L);

        // 生成连接ID
        String connectionId = "conn_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId();

        // 存储连接
        connections.computeIfAbsent(tableId, k -> new ConcurrentHashMap<>()).put(connectionId, emitter);

        // 设置完成和超时回调
        emitter.onCompletion(() -> {
            log.info("SSE连接完成, tableId={}, connectionId={}", tableId, connectionId);
            removeConnection(tableId, connectionId);
        });

        emitter.onTimeout(() -> {
            log.info("SSE连接超时, tableId={}, connectionId={}", tableId, connectionId);
            removeConnection(tableId, connectionId);
        });

        emitter.onError((ex) -> {
            if (isClientDisconnect(ex)) {
                log.info("SSE客户端已断开, tableId={}, connectionId={}", tableId, connectionId);
            } else {
                log.error("SSE连接错误, tableId={}, connectionId={}, error={}", tableId, connectionId, ex.getMessage(), ex);
            }
            removeConnection(tableId, connectionId);
        });

        // 发送初始连接成功消息
        try {
            emitter.send(SseEmitter.event()
                    .name("connected")
                    .data("连接成功"));
        } catch (IOException e) {
            log.error("发送初始消息失败, tableId={}, connectionId={}", tableId, connectionId, e);
            removeConnection(tableId, connectionId);
            return null;
        }

        // 启动心跳任务
        startHeartbeat(tableId, connectionId, emitter);

        return emitter;
    }

    @Override
    public void pushCartUpdate(Integer tableId, Object message) {
        log.info("推送购物车更新, tableId={}", tableId);
        ConcurrentHashMap<String, SseEmitter> tableConnections = connections.get(tableId);
        if (tableConnections == null || tableConnections.isEmpty()) {
            log.warn("该桌号没有SSE连接, tableId={}", tableId);
            return;
        }

        String messageJson = JSON.toJSONString(message);
        tableConnections.forEach((connectionId, emitter) -> {
            try {
                emitter.send(SseEmitter.event()
                        .name("cart_update")
                        .data(messageJson));
                log.info("推送购物车更新成功, tableId={}, connectionId={}", tableId, connectionId);
            } catch (IOException e) {
                if (isClientDisconnect(e)) {
                    log.debug("推送时客户端已断开, tableId={}, connectionId={}", tableId, connectionId);
                } else {
                    log.error("推送购物车更新失败, tableId={}, connectionId={}, error={}", tableId, connectionId, e.getMessage(), e);
                }
                removeConnection(tableId, connectionId);
            }
        });
    }

    @Override
    public void closeConnection(Integer tableId) {
        log.info("关闭SSE连接, tableId={}", tableId);
        ConcurrentHashMap<String, SseEmitter> tableConnections = connections.get(tableId);
        if (tableConnections != null) {
            tableConnections.forEach((connectionId, emitter) -> {
                try {
                    emitter.complete();
                } catch (Exception e) {
                    log.error("关闭SSE连接失败, tableId={}, connectionId={}", tableId, connectionId, e);
                }
            });
            connections.remove(tableId);
        }
    }

    /**
     * 判断是否为客户端主动断开（Broken pipe、Connection reset 等），此类情况属正常，无需打 ERROR。
     */
    private boolean isClientDisconnect(Throwable ex) {
        if (ex == null) return false;
        String msg = ex.getMessage();
        if (msg != null) {
            String lower = msg.toLowerCase();
            if (lower.contains("broken pipe") || lower.contains("connection reset")
                    || lower.contains("connection closed") || lower.contains("an established connection was aborted")) {
                return true;
            }
        }
        return isClientDisconnect(ex.getCause());
    }

    /**
     * 移除连接
     */
    private void removeConnection(Integer tableId, String connectionId) {
        ConcurrentHashMap<String, SseEmitter> tableConnections = connections.get(tableId);
        if (tableConnections != null) {
            SseEmitter emitter = tableConnections.remove(connectionId);
            if (emitter != null) {
                try {
                    emitter.complete();
                } catch (Exception e) {
                    log.error("完成SSE连接失败, tableId={}, connectionId={}", tableId, connectionId, e);
                }
            }
            if (tableConnections.isEmpty()) {
                connections.remove(tableId);
            }
        }
    }

    /**
     * 启动心跳任务
     */
    private void startHeartbeat(Integer tableId, String connectionId, SseEmitter emitter) {
        scheduler.scheduleAtFixedRate(() -> {
            try {
                if (emitter != null) {
                    emitter.send(SseEmitter.event()
                            .name("heartbeat")
                            .data("ping"));
                }
            } catch (IOException e) {
                if (isClientDisconnect(e)) {
                    log.debug("心跳时客户端已断开, tableId={}, connectionId={}", tableId, connectionId);
                } else {
                    log.error("发送心跳失败, tableId={}, connectionId={}", tableId, connectionId, e);
                }
                removeConnection(tableId, connectionId);
            }
        }, 30, 30, TimeUnit.SECONDS); // 每30秒发送一次心跳
    }
}
