package cn.nianxx.thhotel.integrations.messaging.agentbus.adapter;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.time.Duration;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import jakarta.annotation.PreDestroy;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

/**
 * AgentBus WebSocket 长连接客户端。默认关闭，开启后只接收入站 frame 并交给 SourceMessage 捕获链路。
 */
@Component
public class AgentBusWebSocketClient implements SmartLifecycle {

    private static final String MISSING_TOKEN = "MISSING_TOKEN";
    private static final String INVALID_WS_URL = "INVALID_WS_URL";
    private static final String CONNECT_FAILED = "CONNECT_FAILED";
    private static final String WEBSOCKET_ERROR = "WEBSOCKET_ERROR";

    private final AgentBusProperties properties;
    private final AgentBusFrameProcessor frameProcessor;
    private final AgentBusConnectionStatus status;
    private final ScheduledExecutorService reconnectExecutor;
    private final AtomicBoolean running = new AtomicBoolean(false);

    private volatile WebSocket webSocket;

    /**
     * 注入 AgentBus 连接配置、frame 处理器和安全状态组件。
     */
    public AgentBusWebSocketClient(
            AgentBusProperties properties,
            AgentBusFrameProcessor frameProcessor,
            AgentBusConnectionStatus status) {
        this.properties = properties;
        this.frameProcessor = frameProcessor;
        this.status = status;
        this.reconnectExecutor = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(runnable, "agentbus-websocket-reconnect");
            thread.setDaemon(true);
            return thread;
        });
    }

    /**
     * Spring 容器启动后按配置决定是否连接 AgentBus WebSocket。
     */
    @Override
    public void start() {
        if (!properties.getProbe().isEnabled()) {
            return;
        }
        if (running.compareAndSet(false, true)) {
            connect();
        }
    }

    /**
     * 停止 WebSocket 长连接并关闭重连循环。
     */
    @Override
    public void stop() {
        running.set(false);
        WebSocket current = webSocket;
        if (current != null) {
            current.sendClose(WebSocket.NORMAL_CLOSURE, "application stopping");
        }
        status.markDisconnected();
    }

    /**
     * 返回 WebSocket 客户端是否处于运行状态。
     */
    @Override
    public boolean isRunning() {
        return running.get();
    }

    /**
     * 当前客户端默认随应用自动启动，但只有配置开启时才真正建立连接。
     */
    @Override
    public boolean isAutoStartup() {
        return true;
    }

    /**
     * Spring 容器销毁时关闭重连线程，避免本地测试或重启时残留后台任务。
     */
    @PreDestroy
    public void shutdown() {
        stop();
        reconnectExecutor.shutdownNow();
    }

    /**
     * 建立 AgentBus WebSocket 连接。Token 缺失时只记录错误代码，不尝试连接。
     */
    private void connect() {
        if (!running.get()) {
            return;
        }
        String token = trimToNull(properties.getWs().getToken());
        if (token == null) {
            status.recordError(MISSING_TOKEN, "AgentBus WebSocket token is not configured.");
            return;
        }
        URI uri = buildReadyUri(properties.getWs().getUrl());
        if (uri == null) {
            status.recordError(INVALID_WS_URL, "AgentBus WebSocket URL is invalid.");
            return;
        }
        HttpClient.newBuilder()
                .connectTimeout(normalizeDuration(properties.getWs().getConnectTimeout(), Duration.ofSeconds(15)))
                .build()
                .newWebSocketBuilder()
                .header("Authorization", "Bearer " + token)
                .buildAsync(uri, new Listener())
                .whenComplete((socket, exception) -> {
                    if (exception != null) {
                        status.recordError(CONNECT_FAILED, "AgentBus WebSocket connection failed.");
                        scheduleReconnect();
                    } else {
                        webSocket = socket;
                    }
                });
    }

    /**
     * 安排断线后重连。停止状态下不会继续重连。
     */
    private void scheduleReconnect() {
        if (!running.get()) {
            return;
        }
        long delayMillis = normalizeDuration(properties.getWs().getReconnectDelay(), Duration.ofSeconds(5)).toMillis();
        reconnectExecutor.schedule(this::connect, delayMillis, TimeUnit.MILLISECONDS);
    }

    /**
     * 将 WebSocket URL 补上 ready=1 查询参数，用于请求 AgentBus session.ready。
     */
    private URI buildReadyUri(String rawUrl) {
        String url = trimToNull(rawUrl);
        if (url == null) {
            return null;
        }
        try {
            String separator = url.contains("?") ? "&" : "?";
            return URI.create(url + separator + "ready=1");
        } catch (IllegalArgumentException exception) {
            return null;
        }
    }

    /**
     * 标准化 Duration 配置，缺失或非正数时使用默认值。
     */
    private Duration normalizeDuration(Duration value, Duration defaultValue) {
        if (value == null || value.isZero() || value.isNegative()) {
            return defaultValue;
        }
        return value;
    }

    /**
     * 将空白字符串统一视为未配置。
     */
    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    /**
     * JDK WebSocket Listener。只接收文本 frame，不发送 ACK、task.result 或客户回复。
     */
    private class Listener implements WebSocket.Listener {

        private final StringBuilder textBuffer = new StringBuilder();

        /**
         * 标记 WebSocket 已连接，并请求第一条消息。
         */
        @Override
        public void onOpen(WebSocket webSocket) {
            status.markConnected();
            WebSocket.Listener.super.onOpen(webSocket);
        }

        /**
         * 收到完整文本 frame 后交给 AgentBusFrameProcessor 处理。
         */
        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
            textBuffer.append(data);
            if (last) {
                frameProcessor.process(textBuffer.toString());
                textBuffer.setLength(0);
            }
            webSocket.request(1);
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }

        /**
         * 连接关闭时记录断开并按配置重连。
         */
        @Override
        public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
            status.markDisconnected();
            scheduleReconnect();
            return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
        }

        /**
         * WebSocket 异常只记录安全错误代码，不输出 Token 或原始 frame。
         */
        @Override
        public void onError(WebSocket webSocket, Throwable error) {
            status.markDisconnected();
            status.recordError(WEBSOCKET_ERROR, "AgentBus WebSocket error.");
            scheduleReconnect();
            WebSocket.Listener.super.onError(webSocket, error);
        }
    }
}
