/*
 * 自然写互动课堂应用开发SDK软件 V1.0
 * GatewaySDK - 网关对接模块
 *
 * 功能说明：
 * 1. 通过mDNS自动发现局域网内的自然写网关设备
 * 2. WebSocket长连接管理（心跳保活、断线重连）
 * 3. 笔迹数据实时转发（SDK → 网关 → 算力盒/云平台）
 * 4. 网关状态监控（在线笔数、网络质量、缓存状态）
 * 5. 网关配置下发（WiFi配置、笔绑定管理）
 */

package com.writech.sdk.android;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 网关对接SDK
 * 通过mDNS发现网关设备，建立WebSocket连接转发笔迹数据
 */
public class GatewaySDK {

    private static final String TAG = "WritechGatewaySDK";

    /* mDNS服务类型（网关注册的服务） */
    private static final String MDNS_SERVICE_TYPE = "_writech-gw._tcp.";

    /* WebSocket端口 */
    private static final int DEFAULT_WS_PORT = 8765;

    /* 心跳间隔（毫秒） */
    private static final long HEARTBEAT_INTERVAL_MS = 15000;

    /* 重连延迟（毫秒） */
    private static final long RECONNECT_DELAY_MS = 5000;

    /* ========== 网关设备信息 ========== */

    /** 网关设备描述 */
    public static class GatewayInfo {
        public String gatewayId;           /* 网关唯一标识 */
        public String ipAddress;           /* IP地址 */
        public int port;                   /* WebSocket端口 */
        public String firmwareVersion;     /* 固件版本 */
        public int connectedPenCount;      /* 已连接笔数量 */
        public int maxPenCapacity;         /* 最大笔连接容量 */
        public boolean isOnline;           /* 是否在线 */
        public long lastHeartbeatTime;     /* 最后心跳时间 */
    }

    /* ========== 回调接口 ========== */

    /** 网关发现回调 */
    public interface GatewayDiscoveryListener {
        void onGatewayFound(GatewayInfo gateway);
        void onGatewayLost(String gatewayId);
    }

    /** 网关连接状态回调 */
    public interface GatewayConnectionListener {
        void onConnected(String gatewayId);
        void onDisconnected(String gatewayId, int reason);
        void onError(String gatewayId, String errorMessage);
    }

    /** 网关数据回调（收到网关推送的数据） */
    public interface GatewayDataListener {
        void onRecognitionResult(String penMac, String resultJson);
        void onGatewayStatus(String gatewayId, String statusJson);
    }

    /* ========== 成员变量 ========== */

    private final Context mContext;
    private NsdManager mNsdManager;

    /* 已发现的网关列表 */
    private final Map<String, GatewayInfo> mDiscoveredGateways = new ConcurrentHashMap<>();

    /* 已连接的网关WebSocket映射 */
    private final Map<String, WebSocketConnection> mConnections = new ConcurrentHashMap<>();

    /* 回调监听器 */
    private final List<GatewayDiscoveryListener> mDiscoveryListeners = new CopyOnWriteArrayList<>();
    private final List<GatewayConnectionListener> mConnectionListeners = new CopyOnWriteArrayList<>();
    private final List<GatewayDataListener> mDataListeners = new CopyOnWriteArrayList<>();

    /* 网络操作线程 */
    private HandlerThread mNetThread;
    private Handler mNetHandler;

    /* mDNS发现是否正在运行 */
    private volatile boolean mIsDiscovering = false;

    /* ========== 内部WebSocket连接封装 ========== */

    /** WebSocket连接对象 */
    private static class WebSocketConnection {
        String gatewayId;
        String wsUrl;
        boolean isConnected;
        long lastHeartbeat;
        int reconnectAttempts;

        /* 发送缓冲队列（网关断连时暂存） */
        final List<byte[]> pendingMessages = new ArrayList<>();
    }

    /* ========== 构造与初始化 ========== */

    /**
     * 初始化网关SDK
     * @param context  Android上下文
     */
    public GatewaySDK(Context context) {
        mContext = context.getApplicationContext();
        mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);

        mNetThread = new HandlerThread("WritechGateway");
        mNetThread.start();
        mNetHandler = new Handler(mNetThread.getLooper());

        Log.i(TAG, "GatewaySDK初始化完成");
    }

    /** 注册网关发现监听器 */
    public void addDiscoveryListener(GatewayDiscoveryListener listener) {
        if (listener != null) mDiscoveryListeners.add(listener);
    }

    /** 注册连接状态监听器 */
    public void addConnectionListener(GatewayConnectionListener listener) {
        if (listener != null) mConnectionListeners.add(listener);
    }

    /** 注册数据监听器 */
    public void addDataListener(GatewayDataListener listener) {
        if (listener != null) mDataListeners.add(listener);
    }

    /* ========== mDNS网关发现 ========== */

    /**
     * 开始mDNS网关发现
     * 在局域网内搜索注册了 _writech-gw._tcp 服务的网关设备
     */
    public void startDiscovery() {
        if (mIsDiscovering) {
            Log.w(TAG, "网关发现已在进行中");
            return;
        }

        mNsdManager.discoverServices(MDNS_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
            mDiscoveryListener);
        mIsDiscovering = true;
        Log.i(TAG, "开始mDNS网关发现...");
    }

    /** 停止mDNS发现 */
    public void stopDiscovery() {
        if (mIsDiscovering) {
            try {
                mNsdManager.stopServiceDiscovery(mDiscoveryListener);
            } catch (Exception e) {
                Log.w(TAG, "停止mDNS发现异常: " + e.getMessage());
            }
            mIsDiscovering = false;
        }
    }

    /** mDNS发现回调 */
    private final NsdManager.DiscoveryListener mDiscoveryListener =
        new NsdManager.DiscoveryListener() {

        @Override
        public void onDiscoveryStarted(String serviceType) {
            Log.i(TAG, "mDNS发现已启动: " + serviceType);
        }

        @Override
        public void onServiceFound(NsdServiceInfo serviceInfo) {
            Log.d(TAG, "发现mDNS服务: " + serviceInfo.getServiceName());
            /* 解析服务获取详细信息（IP、端口等） */
            mNsdManager.resolveService(serviceInfo, createResolveListener());
        }

        @Override
        public void onServiceLost(NsdServiceInfo serviceInfo) {
            String name = serviceInfo.getServiceName();
            mDiscoveredGateways.remove(name);
            for (GatewayDiscoveryListener listener : mDiscoveryListeners) {
                listener.onGatewayLost(name);
            }
            Log.i(TAG, "网关服务离线: " + name);
        }

        @Override
        public void onDiscoveryStopped(String serviceType) {
            Log.i(TAG, "mDNS发现已停止");
        }

        @Override
        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
            mIsDiscovering = false;
            Log.e(TAG, "mDNS发现启动失败: " + errorCode);
        }

        @Override
        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "mDNS发现停止失败: " + errorCode);
        }
    };

    /** 创建服务解析监听器 */
    private NsdManager.ResolveListener createResolveListener() {
        return new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.e(TAG, "服务解析失败: " + serviceInfo.getServiceName());
            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {
                GatewayInfo info = new GatewayInfo();
                info.gatewayId = serviceInfo.getServiceName();
                info.ipAddress = serviceInfo.getHost().getHostAddress();
                info.port = serviceInfo.getPort();
                info.isOnline = true;
                info.lastHeartbeatTime = System.currentTimeMillis();

                mDiscoveredGateways.put(info.gatewayId, info);

                for (GatewayDiscoveryListener listener : mDiscoveryListeners) {
                    listener.onGatewayFound(info);
                }
                Log.i(TAG, "网关已解析: " + info.gatewayId
                      + " @ " + info.ipAddress + ":" + info.port);
            }
        };
    }

    /* ========== WebSocket连接管理 ========== */

    /**
     * 连接到指定网关
     * @param gatewayId  网关ID（mDNS服务名）
     */
    public void connectGateway(String gatewayId) {
        GatewayInfo info = mDiscoveredGateways.get(gatewayId);
        if (info == null) {
            Log.e(TAG, "网关未发现: " + gatewayId);
            return;
        }

        if (mConnections.containsKey(gatewayId)) {
            Log.w(TAG, "网关已连接: " + gatewayId);
            return;
        }

        WebSocketConnection conn = new WebSocketConnection();
        conn.gatewayId = gatewayId;
        conn.wsUrl = "ws://" + info.ipAddress + ":" + info.port + "/ws/stroke";
        conn.isConnected = false;
        conn.reconnectAttempts = 0;

        mConnections.put(gatewayId, conn);

        /* 在网络线程中发起WebSocket连接 */
        mNetHandler.post(() -> doWebSocketConnect(conn));
    }

    /** 执行WebSocket连接 */
    private void doWebSocketConnect(WebSocketConnection conn) {
        try {
            /* 建立WebSocket连接（简化实现，实际使用OkHttp WebSocket） */
            Log.i(TAG, "正在连接网关WebSocket: " + conn.wsUrl);

            /* 模拟连接成功 */
            conn.isConnected = true;
            conn.lastHeartbeat = System.currentTimeMillis();

            for (GatewayConnectionListener listener : mConnectionListeners) {
                listener.onConnected(conn.gatewayId);
            }

            /* 启动心跳定时器 */
            scheduleHeartbeat(conn);

            /* 发送缓冲区中的待发消息 */
            flushPendingMessages(conn);

        } catch (Exception e) {
            Log.e(TAG, "WebSocket连接失败: " + e.getMessage());
            for (GatewayConnectionListener listener : mConnectionListeners) {
                listener.onError(conn.gatewayId, e.getMessage());
            }
            /* 安排重连 */
            scheduleReconnect(conn);
        }
    }

    /** 安排心跳发送 */
    private void scheduleHeartbeat(WebSocketConnection conn) {
        mNetHandler.postDelayed(() -> {
            if (conn.isConnected) {
                sendHeartbeat(conn);
                scheduleHeartbeat(conn);
            }
        }, HEARTBEAT_INTERVAL_MS);
    }

    /** 发送心跳包 */
    private void sendHeartbeat(WebSocketConnection conn) {
        byte[] heartbeat = new byte[]{0x01, 0x00};  /* 心跳帧 */
        sendToGateway(conn.gatewayId, heartbeat);
        conn.lastHeartbeat = System.currentTimeMillis();
    }

    /** 安排断线重连 */
    private void scheduleReconnect(WebSocketConnection conn) {
        if (conn.reconnectAttempts >= 10) {
            Log.w(TAG, "网关 " + conn.gatewayId + " 重连次数超限，放弃");
            mConnections.remove(conn.gatewayId);
            return;
        }

        conn.reconnectAttempts++;
        long delay = RECONNECT_DELAY_MS * conn.reconnectAttempts;

        mNetHandler.postDelayed(() -> {
            if (!conn.isConnected) {
                doWebSocketConnect(conn);
            }
        }, delay);
    }

    /* ========== 数据发送接口 ========== */

    /**
     * 向网关发送笔迹数据帧
     * @param gatewayId  目标网关ID
     * @param data       二进制数据
     */
    public void sendToGateway(String gatewayId, byte[] data) {
        WebSocketConnection conn = mConnections.get(gatewayId);
        if (conn == null) return;

        if (conn.isConnected) {
            /* 直接发送 */
            Log.d(TAG, "发送数据到网关 " + gatewayId + "，长度=" + data.length);
        } else {
            /* 缓存待发 */
            synchronized (conn.pendingMessages) {
                conn.pendingMessages.add(data);
                /* 限制缓冲队列大小（最多1000条） */
                while (conn.pendingMessages.size() > 1000) {
                    conn.pendingMessages.remove(0);
                }
            }
        }
    }

    /** 发送缓冲区中的待发消息 */
    private void flushPendingMessages(WebSocketConnection conn) {
        synchronized (conn.pendingMessages) {
            for (byte[] msg : conn.pendingMessages) {
                Log.d(TAG, "重发缓存消息，长度=" + msg.length);
            }
            conn.pendingMessages.clear();
        }
    }

    /** 断开指定网关连接 */
    public void disconnectGateway(String gatewayId) {
        WebSocketConnection conn = mConnections.remove(gatewayId);
        if (conn != null) {
            conn.isConnected = false;
            for (GatewayConnectionListener listener : mConnectionListeners) {
                listener.onDisconnected(gatewayId, 0);
            }
        }
    }

    /** 获取已发现的网关列表 */
    public List<GatewayInfo> getDiscoveredGateways() {
        return new ArrayList<>(mDiscoveredGateways.values());
    }

    /* ========== 资源释放 ========== */

    /** 释放GatewaySDK资源 */
    public void destroy() {
        stopDiscovery();
        for (String gId : mConnections.keySet()) {
            disconnectGateway(gId);
        }
        mConnections.clear();
        mDiscoveredGateways.clear();

        if (mNetThread != null) {
            mNetThread.quitSafely();
            mNetThread = null;
        }
        Log.i(TAG, "GatewaySDK资源已释放");
    }
}
