package com.ttstd.controlled.signaling;

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。
 *
 * 关闭码语义：
 *  - 4001 令牌失效：清空当前 accessToken，调用方应重新换取令牌后重连；
 *  - 4003 强制下线：停止重连，调用方应回到未激活/未连接状态。
 */
public class WebSocketClient {

    private static final String TAG = "WebSocketClient";

    private final Gson gson = new Gson();
    private final SignalListener listener;
    private final String serverUrl;
    private final String token;
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

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

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

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

    public void connect() {
        manualClose = false;
        if (serverUrl == null || serverUrl.isEmpty()) {
            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();

        ws = client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                Log.d(TAG, "WebSocket 已连接");
                reconnectAttempts = 0;
                // 不再发送 REGISTER，服务端根据 Bearer 令牌自动注册。
                listener.onConnected();
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                SignalMessage msg = parse(text);
                if (msg == null) return;
                if ("REGISTER_SUCCESS".equals(msg.getType()) && msg.getFromDeviceId() != null) {
                    listener.onRegistered(msg.getFromDeviceId());
                }
                listener.onMessage(msg);
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                Log.w(TAG, "WebSocket onClosing code=" + code + " reason=" + reason);
                if (code == 4001) {
                    listener.onTokenExpired();
                    webSocket.close(4001, reason);
                    return;
                }
                if (code == 4003) {
                    manualClose = true;
                    listener.onForceLogout();
                    webSocket.close(4003, reason);
                    return;
                }
                webSocket.close(code, reason);
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                Log.d(TAG, "WebSocket 已关闭 code=" + code);
                if (manualClose) {
                    listener.onDisconnected();
                    return;
                }
                handleReconnect();
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                Log.e(TAG, "WebSocket 连接失败: " + t.getMessage(), t);
                if (manualClose) {
                    listener.onDisconnected();
                    return;
                }
                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 disconnect() {
        manualClose = true;
        if (ws != null) {
            ws.close(1000, "用户断开");
            ws = null;
        }
        if (client != null) {
            client.dispatcher().executorService().shutdown();
        }
        scheduler.shutdownNow();
    }

    private SignalMessage parse(String text) {
        try {
            JsonObject json = gson.fromJson(text, JsonObject.class);
            SignalMessage msg = new SignalMessage();
            if (json.has("type")) msg.setType(json.get("type").getAsString());
            if (json.has("fromDeviceId")) msg.setFromDeviceId(json.get("fromDeviceId").getAsString());
            if (json.has("toDeviceId")) msg.setToDeviceId(json.get("toDeviceId").getAsString());
            if (json.has("deviceType")) msg.setDeviceType(json.get("deviceType").getAsString());
            if (json.has("payload")) msg.setPayload(json.get("payload").getAsString());
            if (json.has("authType")) msg.setAuthType(json.get("authType").getAsString());
            if (json.has("authValue")) msg.setAuthValue(json.get("authValue").getAsString());
            return msg;
        } catch (Exception e) {
            Log.e(TAG, "消息解析失败: " + e.getMessage(), e);
            return null;
        }
    }

    public void sendMessage(SignalMessage message) {
        send(gson.toJson(message));
    }

    public void sendDeviceListRequest() {
        JsonObject msg = new JsonObject();
        msg.addProperty("type", "DEVICE_LIST");
        send(msg.toString());
    }

    public void send(String message) {
        if (ws != null) {
            ws.send(message);
        }
    }

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