package com.ssyf.task.websocket;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 单个直播间的 WS 客户端引擎
 * 封装了底层协议、心跳保活、Token验证逻辑
 */
@Slf4j
public class LiveRoomWsClient extends WebSocketClient {

    private final Integer roomId;
    private final String userToken;
    private final Consumer<String> messageHandler; // 消息回调
    private final Consumer<Integer> onCloseCallback; // 关闭回调
    private final Runnable heartbeatCallback; // 心跳成功回调（用于刷新Redis状态）

    // 保活调度器
    private ScheduledExecutorService scheduler;
    private ScheduledFuture<?> pingTask;
    private ScheduledFuture<?> authTimeoutTask;

    private boolean isAuthenticated = false;
    private String clientId;

    public LiveRoomWsClient(URI serverUri, Integer roomId, String token, 
                            Consumer<String> messageHandler, 
                            Consumer<Integer> onCloseCallback,
                            Runnable heartbeatCallback) {
        super(serverUri);
        this.roomId = roomId;
        this.userToken = token;
        this.messageHandler = messageHandler;
        this.onCloseCallback = onCloseCallback;
        this.heartbeatCallback = heartbeatCallback;
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        log.info("[Room {}] WS Connected. Sending Token...", roomId);
        
        // 开启10秒认证超时检测
        scheduler = Executors.newSingleThreadScheduledExecutor();
        authTimeoutTask = scheduler.schedule(() -> {
            if (!isAuthenticated) {
                log.warn("[Room {}] Auth Timeout. Closing...", roomId);
                this.close();
            }
        }, 10, TimeUnit.SECONDS);

    }



    @Override
    public void onMessage(String message) {
        log.debug("[Room {}] Received: {}", roomId, message);
        try {
            // 基础协议解析
            WsPayload payload = JSONUtil.toBean(message, WsPayload.class);

            // 1. 处理认证响应
            if (payload.isAuthSuccess()) {
                handleAuthSuccess();
                return;
            } else if (payload.isAuthFail()) {
                log.error("[Room {}] Token Invalid. Stopping.", roomId);
                this.close();
                return;
            }

            messageHandler.accept(message);

        } catch (Exception e) {
            log.error("[Room {}] Parse error: {}", roomId, e.getMessage());
        }
    }

    private void handleAuthSuccess() {
        log.info("[Room {}] Auth Success.", roomId);
        this.isAuthenticated = true;
        if (authTimeoutTask != null) {
            authTimeoutTask.cancel(false);
        }

        // 启动 40秒 Ping
        pingTask = scheduler.scheduleAtFixedRate(this::sendPingReq, 40, 40, TimeUnit.SECONDS);
    }

    private void sendPingReq() {
        if (this.isOpen()) {
            log.debug("[Room {}] Sending Ping...", roomId);
            this.sendPing(); // 发送底层 Ping Frame
            // 调用回调刷新 Redis 状态，证明我还活着
            if (heartbeatCallback != null) {
                heartbeatCallback.run();
            }
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        log.warn("[Room {}] Closed. Code: {}, Reason: {}, Remote: {}", roomId, code, reason, remote);
        cleanup();
        if (onCloseCallback != null) {
            onCloseCallback.accept(roomId);
        }
    }

    @Override
    public void onError(Exception ex) {
        log.error("[Room {}] Error: ", roomId, ex);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        // 处理二进制消息（如果有）
    }

    private void cleanup() {
        if (pingTask != null) {
            pingTask.cancel(true);
        }
        if (authTimeoutTask != null) {
            authTimeoutTask.cancel(true);
        }
        if (scheduler != null) {
            scheduler.shutdownNow();
        }
        isAuthenticated = false;
    }
}