package com.market.service;

import com.market.model.SpotClientSession;
import com.market.utils.SpotConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 现货 WebSocket 消息发送服务
 * <p>
 * 每个客户端连接后启动独立发送线程（两级队列：req 高优先，broadcast 低优先）
 * 并启动心跳定时任务。
 *
 * @author system
 * @date 2026-05-07
 */
@Service
public class SpotMessageSenderService {

    private static final Logger log = LoggerFactory.getLogger(SpotMessageSenderService.class);

    private final ExecutorService executorService = Executors.newCachedThreadPool();
    private final ScheduledExecutorService scheduledExecutorService =
            Executors.newScheduledThreadPool(SpotConstant.SCHEDULED_THREAD_POOL_SIZE);

    /** clientId → 发送 Future */
    private final Map<String, Future<?>> senderFutures = new ConcurrentHashMap<>();
    /** clientId → 心跳 Future */
    private final Map<String, ScheduledFuture<?>> heartbeatFutures = new ConcurrentHashMap<>();

    /**
     * 启动消息发送器（连接建立后调用）
     *
     * @param clientSession 客户端会话
     */
    public void startMessageSender(SpotClientSession clientSession) {
        Future<?> future = executorService.submit(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    // 优先处理 req 请求（高优先级）
                    String reqMsg = clientSession.getReqQueue().poll(
                            SpotConstant.QUEUE_POLL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
                    if (reqMsg != null) {
                        sendMessage(clientSession.getSession(), reqMsg);
                        continue;
                    }
                    // 处理广播消息（低优先级）
                    String broadcastMsg = clientSession.getBroadcastQueue().poll(
                            SpotConstant.QUEUE_POLL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
                    if (broadcastMsg != null) {
                        sendMessage(clientSession.getSession(), broadcastMsg);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    log.error("消息发送失败: clientId={}", clientSession.getId(), e);
                }
            }
        });
        senderFutures.put(clientSession.getId(), future);
    }

    /**
     * 停止消息发送器和心跳（连接关闭后调用）
     *
     * @param clientSession 客户端会话
     */
    public void stopMessageSender(SpotClientSession clientSession) {
        Future<?> senderFuture = senderFutures.remove(clientSession.getId());
        if (senderFuture != null) {
            senderFuture.cancel(true);
        }
        ScheduledFuture<?> heartbeatFuture = heartbeatFutures.remove(clientSession.getId());
        if (heartbeatFuture != null) {
            heartbeatFuture.cancel(true);
        }
    }

    /**
     * 启动心跳任务（连接建立后调用）
     *
     * @param clientSession 客户端会话
     */
    public void startHeartbeat(SpotClientSession clientSession) {
        ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(() -> {
            try {
                // 1. 检查心跳超时
                LocalDateTime lastPong = clientSession.getLastPongTime();
                long secondsSinceLastPong = ChronoUnit.SECONDS.between(lastPong, LocalDateTime.now());
                if (secondsSinceLastPong > SpotConstant.HEARTBEAT_TIMEOUT_SECONDS) {
                    log.warn("现货客户端心跳超时，断开连接: clientId={}", clientSession.getId());
                    clientSession.getSession().close();
                    return;
                }
                // 2. 发送 Ping
                long timestamp = System.currentTimeMillis();
                clientSession.getReqQueue().offer(String.format(SpotConstant.PING_MESSAGE_TEMPLATE, timestamp));
                clientSession.setLastPingTime(LocalDateTime.now());
            } catch (Exception e) {
                log.error("心跳检测失败: clientId={}", clientSession.getId(), e);
            }
        }, SpotConstant.HEARTBEAT_INTERVAL_SECONDS, SpotConstant.HEARTBEAT_INTERVAL_SECONDS, TimeUnit.SECONDS);
        heartbeatFutures.put(clientSession.getId(), future);
    }

    // ------------------------------------------------------------------ private

    private void sendMessage(WebSocketSession session, String message) {
        try {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(message));
            }
        } catch (Exception e) {
            log.error("WebSocket 发送消息失败: sessionId={}", session.getId(), e);
        }
    }
}
