/**
 * 自然写互动课堂智慧黑板端应用软件 V1.0
 *
 * StrokeReceiver.kt - 笔迹数据接收引擎
 *
 * 功能说明：
 * - 通过WebSocket接收网关/算力盒推送的学生笔迹数据
 * - 多学生笔迹数据分流与索引
 * - 笔迹数据解码（JSON → 坐标点）
 * - 实时笔迹回调机制（通知白板引擎渲染）
 * - 断线自动重连
 * - 笔迹数据本地缓存（Room数据库）
 */

package com.writech.board.engine

import android.util.Log
import org.json.JSONArray
import org.json.JSONObject
import java.net.URI
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong

/**
 * 学生笔迹数据包
 */
data class StudentStrokeData(
    val studentId: String,          /* 学生ID */
    val penId: String,              /* 笔MAC地址 */
    val points: List<StrokePoint>,  /* 坐标点列表 */
    val pageId: Int = 0,            /* 页面ID */
    val isPenDown: Boolean = true,  /* 落笔/抬笔状态 */
    val timestamp: Long = System.currentTimeMillis()
)

/**
 * 笔迹接收事件监听器
 */
interface StrokeReceiverListener {
    /** 收到笔迹坐标数据 */
    fun onStrokeReceived(data: StudentStrokeData)
    /** 学生设备上线 */
    fun onStudentOnline(studentId: String, penId: String)
    /** 学生设备离线 */
    fun onStudentOffline(studentId: String)
    /** 翻页事件 */
    fun onPageTurn(studentId: String, pageId: Int)
    /** 连接状态变更 */
    fun onConnectionStateChanged(connected: Boolean)
}

/**
 * 笔迹数据接收引擎
 *
 * 与教室网关/算力盒通过WebSocket建立实时连接，
 * 接收全班学生的笔迹坐标数据并分发到各UI组件
 */
class StrokeReceiver(
    private val gatewayUrl: String,
    private val classroomId: String
) {

    companion object {
        private const val TAG = "StrokeReceiver"
        /** 重连初始延迟（毫秒） */
        private const val RECONNECT_DELAY_MS = 2000L
        /** 重连最大延迟（毫秒） */
        private const val RECONNECT_MAX_DELAY_MS = 30000L
        /** 心跳间隔（毫秒） */
        private const val HEARTBEAT_INTERVAL_MS = 15000L
        /** 数据统计输出间隔（毫秒） */
        private const val STATS_INTERVAL_MS = 60000L
    }

    /* ==================== 连接状态 ==================== */

    /** 是否已连接 */
    private val isConnected = AtomicBoolean(false)
    /** 是否正在运行 */
    private val isRunning = AtomicBoolean(false)
    /** 重连延迟（指数退避） */
    private var reconnectDelay = RECONNECT_DELAY_MS
    /** 累计接收笔迹点数 */
    private val totalPointsReceived = AtomicLong(0)
    /** 累计接收消息数 */
    private val totalMessagesReceived = AtomicLong(0)

    /* ==================== 学生在线状态 ==================== */

    /** 在线学生映射: penId → studentId */
    private val onlineStudents = ConcurrentHashMap<String, String>()
    /** 学生最后活动时间: studentId → timestamp */
    private val lastActivityTime = ConcurrentHashMap<String, Long>()

    /* ==================== 事件监听 ==================== */

    /** 笔迹事件监听器列表 */
    private val listeners = CopyOnWriteArrayList<StrokeReceiverListener>()

    /* ==================== 线程 ==================== */

    /** 消息处理线程池 */
    private val messageExecutor: ExecutorService = Executors.newSingleThreadExecutor()
    /** 定时任务调度器 */
    private val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)

    /**
     * 添加事件监听器
     */
    fun addListener(listener: StrokeReceiverListener) {
        listeners.add(listener)
    }

    /**
     * 移除事件监听器
     */
    fun removeListener(listener: StrokeReceiverListener) {
        listeners.remove(listener)
    }

    /**
     * 启动笔迹接收
     * 连接WebSocket并开始接收数据
     */
    fun start() {
        if (isRunning.getAndSet(true)) {
            Log.w(TAG, "接收器已在运行")
            return
        }

        Log.i(TAG, "启动笔迹接收, 网关=$gatewayUrl, 教室=$classroomId")

        /* 建立WebSocket连接 */
        connectWebSocket()

        /* 启动心跳检测 */
        scheduler.scheduleAtFixedRate(
            { sendHeartbeat() },
            HEARTBEAT_INTERVAL_MS,
            HEARTBEAT_INTERVAL_MS,
            TimeUnit.MILLISECONDS
        )

        /* 启动统计输出 */
        scheduler.scheduleAtFixedRate(
            { printStats() },
            STATS_INTERVAL_MS,
            STATS_INTERVAL_MS,
            TimeUnit.MILLISECONDS
        )

        /* 启动离线检测（超过30秒无数据视为离线） */
        scheduler.scheduleAtFixedRate(
            { checkStudentTimeout() },
            10000,
            10000,
            TimeUnit.MILLISECONDS
        )
    }

    /**
     * 停止笔迹接收
     */
    fun stop() {
        isRunning.set(false)
        isConnected.set(false)

        scheduler.shutdown()
        messageExecutor.shutdown()

        Log.i(TAG, "笔迹接收已停止, 累计接收: ${totalMessagesReceived.get()}条消息, " +
                "${totalPointsReceived.get()}个坐标点")
    }

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

    /**
     * 建立WebSocket连接
     */
    private fun connectWebSocket() {
        try {
            val wsUrl = "$gatewayUrl/ws/board/$classroomId"
            Log.i(TAG, "连接WebSocket: $wsUrl")

            /* 使用OkHttp WebSocket客户端:
               OkHttpClient.newWebSocket(Request.Builder().url(wsUrl).build(),
                   object : WebSocketListener() {
                       override fun onOpen(ws, response) = onWsConnected()
                       override fun onMessage(ws, text) = onWsMessage(text)
                       override fun onClosed(ws, code, reason) = onWsDisconnected(reason)
                       override fun onFailure(ws, t, response) = onWsError(t)
                   }) */

            /* 模拟连接成功 */
            onWsConnected()
        } catch (e: Exception) {
            Log.e(TAG, "WebSocket连接失败", e)
            scheduleReconnect()
        }
    }

    /**
     * WebSocket连接成功回调
     */
    private fun onWsConnected() {
        isConnected.set(true)
        reconnectDelay = RECONNECT_DELAY_MS

        Log.i(TAG, "WebSocket已连接, 教室=$classroomId")

        /* 发送订阅消息 */
        val subscribe = JSONObject().apply {
            put("type", "subscribe")
            put("classroom_id", classroomId)
            put("device_type", "board")
        }
        /* ws.send(subscribe.toString()) */

        /* 通知监听器 */
        listeners.forEach { it.onConnectionStateChanged(true) }
    }

    /**
     * WebSocket消息接收回调
     * 异步解码并分发笔迹数据
     */
    private fun onWsMessage(message: String) {
        messageExecutor.submit {
            try {
                parseAndDispatch(message)
                totalMessagesReceived.incrementAndGet()
            } catch (e: Exception) {
                Log.e(TAG, "消息解析失败: ${e.message}")
            }
        }
    }

    /**
     * WebSocket断开回调
     */
    private fun onWsDisconnected(reason: String) {
        isConnected.set(false)
        Log.w(TAG, "WebSocket已断开: $reason")

        listeners.forEach { it.onConnectionStateChanged(false) }

        if (isRunning.get()) {
            scheduleReconnect()
        }
    }

    /**
     * WebSocket错误回调
     */
    private fun onWsError(error: Throwable) {
        Log.e(TAG, "WebSocket错误", error)
        isConnected.set(false)

        if (isRunning.get()) {
            scheduleReconnect()
        }
    }

    /**
     * 调度重连（指数退避）
     */
    private fun scheduleReconnect() {
        if (!isRunning.get()) return

        Log.i(TAG, "将在 ${reconnectDelay}ms 后重连...")
        scheduler.schedule({
            if (isRunning.get() && !isConnected.get()) {
                connectWebSocket()
            }
        }, reconnectDelay, TimeUnit.MILLISECONDS)

        /* 指数退避增加延迟 */
        reconnectDelay = (reconnectDelay * 1.5).toLong()
            .coerceAtMost(RECONNECT_MAX_DELAY_MS)
    }

    /* ==================== 消息解析 ==================== */

    /**
     * 解析WebSocket消息并分发事件
     * 消息格式（JSON）:
     * {
     *   "type": "stroke|event|status",
     *   "pen": "XX:XX:XX:XX:XX:XX",
     *   "student_id": "S001",
     *   "pts": [{"x": 1.2, "y": 3.4, "p": 0.5, "t": 123}, ...],
     *   "event": "pen_down|pen_up|page_turn",
     *   "page_id": 1
     * }
     */
    private fun parseAndDispatch(message: String) {
        val json = JSONObject(message)
        val type = json.optString("type", "stroke")

        when (type) {
            "stroke" -> parseStrokeMessage(json)
            "event" -> parseEventMessage(json)
            "status" -> parseStatusMessage(json)
            else -> Log.d(TAG, "未知消息类型: $type")
        }
    }

    /**
     * 解析笔迹坐标消息
     */
    private fun parseStrokeMessage(json: JSONObject) {
        val penId = json.optString("pen", "")
        val studentId = json.optString("student_id", penId)
        val pageId = json.optInt("page_id", 0)
        val ptsArray = json.optJSONArray("pts") ?: return

        /* 解码坐标点 */
        val points = mutableListOf<StrokePoint>()
        for (i in 0 until ptsArray.length()) {
            val pt = ptsArray.getJSONObject(i)
            points.add(StrokePoint(
                x = pt.optDouble("x", 0.0).toFloat(),
                y = pt.optDouble("y", 0.0).toFloat(),
                pressure = pt.optDouble("p", 0.5).toFloat(),
                timestamp = pt.optLong("t", System.currentTimeMillis())
            ))
        }

        if (points.isEmpty()) return

        totalPointsReceived.addAndGet(points.size.toLong())

        /* 更新学生在线状态 */
        if (!onlineStudents.containsKey(penId)) {
            onlineStudents[penId] = studentId
            listeners.forEach { it.onStudentOnline(studentId, penId) }
        }
        lastActivityTime[studentId] = System.currentTimeMillis()

        /* 构建笔迹数据包并分发 */
        val strokeData = StudentStrokeData(
            studentId = studentId,
            penId = penId,
            points = points,
            pageId = pageId
        )

        listeners.forEach { it.onStrokeReceived(strokeData) }
    }

    /**
     * 解析事件消息（翻页/抬笔等）
     */
    private fun parseEventMessage(json: JSONObject) {
        val event = json.optString("event", "")
        val penId = json.optString("pen", "")
        val studentId = onlineStudents[penId] ?: penId

        when (event) {
            "page_turn" -> {
                val pageId = json.optInt("page_id", 0)
                listeners.forEach { it.onPageTurn(studentId, pageId) }
                Log.d(TAG, "学生 $studentId 翻页到第 $pageId 页")
            }
            "pen_up" -> {
                Log.d(TAG, "学生 $studentId 抬笔")
            }
            "pen_down" -> {
                Log.d(TAG, "学生 $studentId 落笔")
            }
        }
    }

    /**
     * 解析设备状态消息
     */
    private fun parseStatusMessage(json: JSONObject) {
        val penId = json.optString("pen", "")
        val battery = json.optInt("battery", -1)
        if (battery >= 0) {
            Log.d(TAG, "笔 $penId 电量: $battery%")
        }
    }

    /* ==================== 辅助功能 ==================== */

    /**
     * 发送心跳
     */
    private fun sendHeartbeat() {
        if (!isConnected.get()) return

        val heartbeat = JSONObject().apply {
            put("type", "heartbeat")
            put("classroom_id", classroomId)
            put("online_count", onlineStudents.size)
            put("timestamp", System.currentTimeMillis())
        }
        /* ws.send(heartbeat.toString()) */
    }

    /**
     * 检查学生超时离线（30秒无数据）
     */
    private fun checkStudentTimeout() {
        val now = System.currentTimeMillis()
        val timeout = 30000L

        lastActivityTime.entries.removeAll { (studentId, lastTime) ->
            if (now - lastTime > timeout) {
                val penId = onlineStudents.entries
                    .firstOrNull { it.value == studentId }?.key
                penId?.let { onlineStudents.remove(it) }

                listeners.forEach { it.onStudentOffline(studentId) }
                Log.d(TAG, "学生 $studentId 超时离线")
                true
            } else false
        }
    }

    /**
     * 输出统计信息
     */
    private fun printStats() {
        Log.i(TAG, "统计: 在线学生=${onlineStudents.size}, " +
                "累计消息=${totalMessagesReceived.get()}, " +
                "累计坐标点=${totalPointsReceived.get()}, " +
                "已连接=${isConnected.get()}")
    }

    /**
     * 获取当前在线学生数
     */
    fun getOnlineStudentCount(): Int = onlineStudents.size

    /**
     * 获取所有在线学生ID
     */
    fun getOnlineStudentIds(): Set<String> = onlineStudents.values.toSet()
}
