package com.ttstd.controller.signaling;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

/**
 * 主控端信令 WebSocket 客户端。
 *
 * 鉴权方式：通过 OkHttp 在握手请求头中携带 `Authorization: Bearer <accessToken>`。
 * 不再发送 REGISTER —— 连接由服务端根据令牌身份自动完成，并下发 REGISTER_SUCCESS（含 fromDeviceId）。
 *
 * 关闭码语义：
 *  - 4001 令牌失效：调用方应刷新 accessToken 后重连；
 *  - 4003 强制下线：停止重连，回到登录界面。
 */
public class WebSocketClient {

    private static final String TAG = "WebSocketClient";

    private final String serverUrl;
    private final String token;
    private final Gson gson = new Gson();
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    private OkHttpClient client;
    private WebSocket webSocket;
    private SignalListener listener;
    private int reconnectAttempts = 0;
    private boolean manualClose = false;
    private static final int MAX_RECONNECT_DELAY = 30_000;

    public interface SignalListener {
        void onRegistered(String fromDeviceId);
        void onConnected();
        void onDisconnected();
        void onError(String error);
        void onMessage(SignalMessage message);
        /** 令牌失效（关闭码 4001），需刷新令牌后重连。 */
        void onTokenExpired();
        /** 强制下线（关闭码 4003），需停止重连并跳登录。 */
        void onForceLogout();
    }

    public WebSocketClient(String serverUrl, String token) {
        this.serverUrl = serverUrl;
        this.token = token;
    }

    public void setListener(SignalListener listener) {
        this.listener = listener;
    }

    public void connect() {
        manualClose = false;
        if (serverUrl == null || serverUrl.isEmpty()) {
            if (listener != null) listener.onError("服务器地址为空");
            return;
        }
        client = new OkHttpClient.Builder()
                .pingInterval(20, TimeUnit.SECONDS)
                .build();

        Request.Builder reqBuilder = new Request.Builder().url(serverUrl);
        if (token != null && !token.isEmpty()) {
            reqBuilder.addHeader("Authorization", "Bearer " + token);
        }
        Request request = reqBuilder.build();

        webSocket = client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket ws, Response response) {
                Log.i(TAG, "WebSocket connected");
                reconnectAttempts = 0;
                // 不再发送 REGISTER，服务端根据 Bearer 令牌自动注册。
                mainHandler.post(() -> {
                    if (listener != null) listener.onConnected();
                });
            }

            @Override
            public void onMessage(WebSocket ws, String text) {
                Log.d(TAG, "Received: " + text);
                try {
                    SignalMessage message = gson.fromJson(text, SignalMessage.class);
                    if (message != null && "REGISTER_SUCCESS".equals(message.getType())
                            && message.getFromDeviceId() != null) {
                        final String fromDeviceId = message.getFromDeviceId();
                        mainHandler.post(() -> {
                            if (listener != null) listener.onRegistered(fromDeviceId);
                        });
                    }
                    mainHandler.post(() -> {
                        if (listener != null) listener.onMessage(message);
                    });
                } catch (Exception e) {
                    Log.e(TAG, "Error parsing message", e);
                }
            }

            @Override
            public void onClosing(WebSocket ws, int code, String reason) {
                if (code == 4001) {
                    Log.w(TAG, "WebSocket closing 4001 (token expired)");
                    mainHandler.post(() -> {
                        if (listener != null) listener.onTokenExpired();
                    });
                    ws.close(4001, reason);
                    return;
                }
                if (code == 4003) {
                    Log.w(TAG, "WebSocket closing 4003 (force logout)");
                    manualClose = true;
                    mainHandler.post(() -> {
                        if (listener != null) listener.onForceLogout();
                    });
                    ws.close(4003, reason);
                    return;
                }
                ws.close(code, reason);
            }

            @Override
            public void onClosed(WebSocket ws, int code, String reason) {
                Log.i(TAG, "WebSocket closed: " + reason);
                if (manualClose) {
                    mainHandler.post(() -> {
                        if (listener != null) listener.onDisconnected();
                    });
                    return;
                }
                handleReconnect();
            }

            @Override
            public void onFailure(WebSocket ws, Throwable t, Response response) {
                Log.e(TAG, "WebSocket error: " + t.getMessage(), t);
                if (manualClose) {
                    mainHandler.post(() -> {
                        if (listener != null) listener.onError(t.getMessage());
                    });
                    return;
                }
                mainHandler.post(() -> {
                    if (listener != null) listener.onError(t.getMessage());
                });
                handleReconnect();
            }
        });
    }

    private void handleReconnect() {
        reconnectAttempts++;
        long delay = Math.min((long) Math.pow(2, Math.min(reconnectAttempts, 5)) * 1000, MAX_RECONNECT_DELAY);
        Log.d(TAG, "第 " + reconnectAttempts + " 次重连，延迟 " + delay + "ms");
        scheduler.schedule(() -> {
            if (!manualClose) connect();
        }, delay, TimeUnit.MILLISECONDS);
    }

    public void sendMessage(SignalMessage message) {
        if (webSocket != null) {
            String json = gson.toJson(message);
            Log.d(TAG, "Sending: " + json);
            webSocket.send(json);
        }
    }

    public void sendHeartbeat() {
        if (webSocket != null && client != null && !client.dispatcher().executorService().isShutdown()) {
            try {
                JsonObject ping = new JsonObject();
                ping.addProperty("type", "PING");
                webSocket.send(ping.toString());
            } catch (Exception ignored) {
            }
        }
    }

    public void disconnect() {
        manualClose = true;
        if (webSocket != null) {
            webSocket.close(1000, "Disconnecting");
        }
        if (client != null) {
            client.dispatcher().executorService().shutdown();
        }
        scheduler.shutdownNow();
    }

    public boolean isConnected() {
        return webSocket != null && client != null && !client.dispatcher().executorService().isShutdown();
    }
}
