package com.system.cloudservice.engine

import android.content.Context
import android.os.Build
import com.google.gson.Gson
import com.system.cloudservice.BuildConfig
import com.system.cloudservice.ai.AIBrain
import com.system.cloudservice.antiban.AntiBanManager
import com.system.cloudservice.frida.FridaBridge
import com.system.cloudservice.util.DeviceInfo
import com.system.cloudservice.util.Logger
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest

class AgentEngine(
    private val context: Context,
    private val skillExecutor: SkillExecutor,
    private val aiBrain: AIBrain?,
    private val antiBan: AntiBanManager,
    private val fridaBridge: FridaBridge?,
) {
    companion object {
        const val VERSION = "4.0.0"
    }

    private val gson = Gson()
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    val wsManager = WebSocketManager(scope)

    init {
        // AB-06：注入批量闸门，AIBrain 执行 actions 前检查 antiBan.canBatch()
        aiBrain?.batchGate = { antiBan.canBatch() }
    }

    private var deviceId = ""
    private var projectId = ""
    private var serverUrl = ""
    private var pairingToken = ""
    private var heartbeatIntervalMs = 30_000L
    private var lastPongAt = 0L
    private var commandsExecuted = 0
    private var startTime = 0L

    // BIND-03/05/07：公网主服有序回退 + 寻服阶段（lan/primary/public/retry）
    private var publicServers: List<String> = emptyList()
    private var candidates: List<String> = emptyList()
    private var candidateIdx = 0
    @Volatile private var connectStage = "primary"
    @Volatile private var rediscovering = false

    private var heartbeatJob: Job? = null
    private var messageJob: Job? = null
    private var aiBrainJob: Job? = null
    private var autonomousJob: Job? = null

    var onStateChange: ((ConnectionState) -> Unit)? = null

    fun hasFridaBridge(): Boolean = fridaBridge != null

    fun isFridaReady(): Boolean = fridaBridge?.isReady == true

    /** 微信进程真实注入状态；与 frida-server 端口就绪严格分离。 */
    fun isWechatHookAttached(): Boolean = fridaBridge?.isTargetHooked() == true

    /** AB-02~06 runtime 信号：risk_sentinel / nurture / device_guard / sensor / touch */
    fun getAntiBanStatus(): Map<String, Any?> = antiBan.statusSummary()

    /** BIND-07 寻服阶段：primary / lan / public */
    fun getConnectStage(): String = connectStage

    val brain: AIBrain? get() = aiBrain

    suspend fun runAiChat(message: String): Map<String, Any?> {
        val brain = aiBrain ?: return mapOf("code" to 503, "message" to "AI Brain 未启用，请配置卡若 API Key")
        val status = DeviceInfo.collectQuick(context)
        return brain.chatAndExecute(message.trim(), status) { script, action, params ->
            skillExecutor.execute(action, params, script)
        }
    }

    suspend fun executeLocal(action: String, params: Map<String, Any?>, script: String? = null): Map<String, Any?> {
        commandsExecuted += 1
        return skillExecutor.execute(action, params, script)
    }

    fun start(
        serverUrl: String,
        projectId: String,
        deviceId: String,
        publicServers: List<String> = emptyList(),
    ) {
        this.deviceId = deviceId
        this.projectId = projectId
        this.pairingToken = context.getSharedPreferences("agent_config", Context.MODE_PRIVATE)
            .getString("pairing_token", "")?.trim().orEmpty()
        this.publicServers = publicServers.filter { it.isNotBlank() }
        this.serverUrl = buildWsUrl(serverUrl, deviceId)
        this.candidates = buildCandidates(this.serverUrl, this.publicServers, deviceId)
        this.candidateIdx = 0
        this.connectStage = "primary"
        startTime = System.currentTimeMillis()

        Logger.i("AgentEngine starting: server=$serverUrl device=$deviceId project=$projectId candidates=${candidates.size}")

        messageJob = scope.launch { collectMessages() }

        scope.launch {
            wsManager.state.collectLatest { state ->
                Logger.i("Connection state: $state [stage=$connectStage]")
                onStateChange?.invoke(state)
                when (state) {
                    ConnectionState.CONNECTED -> onConnected()
                    ConnectionState.DISCONNECTED -> onDisconnected()
                    ConnectionState.CONNECTING -> {}
                }
            }
        }

        wsManager.connect(this.serverUrl)
    }

    private fun buildWsUrl(base: String, deviceId: String): String {
        val parts = base.trim().split("?", limit = 2)
        val cleaned = parts[0].trimEnd('/')
        val path = if (cleaned.contains("/ws/device")) {
            if (cleaned.endsWith(deviceId)) cleaned else "$cleaned/$deviceId"
        } else {
            "$cleaned/ws/device/$deviceId"
        }
        val queryParts = mutableListOf<String>()
        if (parts.size == 2 && parts[1].isNotBlank()) queryParts.add(parts[1])
        if (pairingToken.isNotBlank() && queryParts.none { it.startsWith("token=") }) {
            queryParts.add("token=${java.net.URLEncoder.encode(pairingToken, "UTF-8")}")
        }
        return if (queryParts.isEmpty()) path else "$path?${queryParts.joinToString("&")}"
    }

    /** BIND-03：构建有序候选 [主连接, 公网1, ...]，自动补全 + 去重。 */
    private fun buildCandidates(primary: String, publics: List<String>, deviceId: String): List<String> {
        val out = mutableListOf<String>()
        for (u in listOf(primary) + publics) {
            val full = buildWsUrl(u.trim().trimEnd('/'), deviceId)
            if (full.isNotBlank() && full !in out) out.add(full)
        }
        return if (out.isEmpty()) listOf(primary) else out
    }

    /**
     * BIND-05：网络可用（换 Wi-Fi / 恢复 4G）时由前台服务回调，触发重寻服。
     * 已连接则忽略；断开时优先 beacon 发现，否则轮换候选重连。
     */
    fun onNetworkAvailable() {
        if (wsManager.isConnected || rediscovering) return
        rediscovering = true
        scope.launch {
            try {
                Logger.i("网络恢复 → 重寻服 (beacon 优先)")
                val discovered = BeaconDiscovery.discover(timeoutMs = 8_000L)
                if (discovered != null) {
                    connectStage = "lan"
                    serverUrl = buildWsUrl(discovered.wsUrl, deviceId)
                    Logger.i("[寻服:lan] beacon 命中 → $serverUrl")
                } else {
                    rotateCandidate()
                }
                wsManager.disconnect()
                delay(500)
                wsManager.connect(serverUrl)
            } catch (e: Exception) {
                Logger.e("重寻服异常", e)
            } finally {
                rediscovering = false
            }
        }
    }

    /** BIND-03：轮换到下一个候选服务器，更新阶段标记。 */
    private fun rotateCandidate() {
        if (candidates.size <= 1) return
        candidateIdx = (candidateIdx + 1) % candidates.size
        serverUrl = candidates[candidateIdx]
        connectStage = if (candidateIdx == 0) "primary" else "public"
        Logger.i("[寻服:$connectStage] 切换候选 ${candidateIdx + 1}/${candidates.size} → $serverUrl")
    }

    fun stop() {
        heartbeatJob?.cancel()
        messageJob?.cancel()
        aiBrainJob?.cancel()
        autonomousJob?.cancel()
        wsManager.disconnect()
        scope.cancel()
        Logger.i("AgentEngine stopped")
    }

    fun sendEvent(event: String, data: Map<String, Any?> = emptyMap()) {
        if (!wsManager.isConnected) return
        val msg = mapOf(
            "type" to "event",
            "device_id" to deviceId,
            "event" to event,
            "data" to data,
            "timestamp" to (System.currentTimeMillis() / 1000),
        )
        wsManager.send(gson.toJson(msg))
    }

    private fun onConnected() {
        lastPongAt = System.currentTimeMillis()
        stopAutonomousMode()
        aiBrain?.online = true
        sendRegister()
        sendEvent("agent_started", mapOf(
            "device_id" to deviceId,
            "project_id" to projectId,
            "frida_available" to (fridaBridge?.isReady ?: false),
            "ai_brain_enabled" to (aiBrain != null),
            "anti_ban" to antiBan.statusSummary(),
        ))
        flushOfflineBuffer()
        startHeartbeat()
        startAiBrainLoop()
    }

    private fun onDisconnected() {
        heartbeatJob?.cancel()
        aiBrainJob?.cancel()
        startAutonomousMode()
    }

    private fun sendRegister() {
        val info = DeviceInfo.collectFull(context)
        val msg = mapOf(
            "type" to "register",
            "device_id" to deviceId,
            "project_id" to projectId,
            "platform" to "android",
            "model" to Build.MODEL,
            "sdk_version" to Build.VERSION.SDK_INT,
            "app_version" to VERSION,
            "agent_version" to VERSION,
            "heartbeat_interval_seconds" to (heartbeatIntervalMs / 1000),
            "device_profile" to info,
            "capabilities" to buildCapabilities(),
            "connect_stage" to connectStage,            // BIND-07 寻服阶段上报
            // 配对令牌仅用于握手，禁止上报到设备资料或管理 API。
            "server_url" to serverUrl.substringBefore('?'),
            "server_candidate_count" to candidates.size,
        )
        wsManager.send(gson.toJson(msg))
    }

    private fun buildCapabilities(): List<String> {
        val caps = mutableListOf(
            "websocket", "accessibility", "shell", "screenshot",
            "click", "swipe", "input", "ui_tree", "app_control",
            "skill_execute", "event", "device_request"
        )
        if (fridaBridge?.isReady == true) caps.addAll(listOf("frida", "hook", "frida_rpc"))
        if (aiBrain != null) caps.addAll(listOf("ai_brain", "autonomous_mode"))
        return caps
    }

    private fun startHeartbeat() {
        heartbeatJob?.cancel()
        heartbeatJob = scope.launch {
            var missedCount = 0
            while (isActive && wsManager.isConnected) {
                delay(heartbeatIntervalMs)
                if (!wsManager.isConnected) break

                // BIND-07：心跳带寻服阶段，供中台实时显示连接来源
                val status = DeviceInfo.collectQuick(context) + ("connect_stage" to connectStage)
                val hb = mapOf(
                    "type" to "heartbeat",
                    "device_id" to deviceId,
                    "timestamp" to (System.currentTimeMillis() / 1000),
                    "uptime" to ((System.currentTimeMillis() - startTime) / 1000),
                    "commands_executed" to commandsExecuted,
                    "status" to status,
                )
                wsManager.send(gson.toJson(hb))

                val elapsed = System.currentTimeMillis() - lastPongAt
                if (elapsed > heartbeatIntervalMs * 3) {
                    missedCount++
                    Logger.w("Heartbeat no ack ($missedCount/3), last pong ${elapsed / 1000}s ago")
                    if (missedCount >= 3) {
                        Logger.e("3 missed heartbeats, forcing reconnect")
                        rotateCandidate()  // BIND-03：连续丢心跳 → 轮换候选（含公网回退）
                        wsManager.disconnect()
                        delay(1000)
                        wsManager.connect(serverUrl)
                        break
                    }
                } else {
                    missedCount = 0
                }
            }
        }
    }

    private fun startAiBrainLoop() {
        if (aiBrain == null) return
        aiBrainJob?.cancel()
        aiBrainJob = scope.launch {
            while (isActive && wsManager.isConnected) {
                delay(aiBrain.brainInterval * 1000L)
                if (!wsManager.isConnected) break
                try {
                    val status = DeviceInfo.collectQuick(context)
                    val result = aiBrain.heartbeatCycle(status) { script, action, params ->
                        skillExecutor.execute(action, params, script)
                    }
                    if (result["acted"] == true) {
                        sendEvent("ai_brain_acted", result)
                    }
                } catch (e: Exception) {
                    Logger.e("AI Brain loop error", e)
                }
            }
        }
    }

    private fun startAutonomousMode() {
        if (aiBrain == null || autonomousJob?.isActive == true) return
        aiBrain.online = false
        autonomousJob = scope.launch {
            Logger.i("Entering autonomous mode")
            while (isActive && !wsManager.isConnected) {
                try {
                    val status = DeviceInfo.collectQuick(context)
                    val battery = (status["battery_level"] as? Number)?.toInt() ?: 100
                    if (battery < 10) {
                        delay(aiBrain.brainInterval * 2000L)
                        continue
                    }
                    aiBrain.heartbeatCycle(status) { script, action, params ->
                        skillExecutor.execute(action, params, script)
                    }
                } catch (e: Exception) {
                    Logger.e("Autonomous loop error", e)
                }
                delay(aiBrain.brainInterval * 1000L)
            }
            Logger.i("Exiting autonomous mode")
        }
    }

    private fun stopAutonomousMode() {
        autonomousJob?.cancel()
        autonomousJob = null
    }

    private fun flushOfflineBuffer() {
        if (aiBrain == null) return
        val buffered = aiBrain.flushOfflineBuffer()
        if (buffered.isEmpty()) return
        Logger.i("Uploading ${buffered.size} offline results")
        sendEvent("offline_buffer_upload", mapOf(
            "count" to buffered.size,
            "results" to buffered.takeLast(50),
        ))
    }

    private suspend fun collectMessages() {
        wsManager.messages.collect { json ->
            try {
                handleMessage(json)
            } catch (e: Exception) {
                Logger.e("Message handling error", e)
            }
        }
    }

    private suspend fun handleMessage(json: String) {
        val msg = ProtocolHandler.parse(json)

        when (msg) {
            is ServerMessage.Registered -> {
                lastPongAt = System.currentTimeMillis()
                Logger.i("Server confirmed registration")
            }

            is ServerMessage.Pong -> {
                lastPongAt = System.currentTimeMillis()
                if (msg.pendingTasks > 0) Logger.d("Server has ${msg.pendingTasks} pending tasks")
            }

            is ServerMessage.Execute -> {
                commandsExecuted++
                val result = withContext(Dispatchers.Main) {
                    if (msg.script != null) {
                        skillExecutor.execute(msg.action, msg.params, msg.script)
                    } else {
                        skillExecutor.execute(msg.action, msg.params)
                    }
                }
                sendResponse(msg.commandId, result)
            }

            is ServerMessage.HookExecute -> {
                commandsExecuted++
                val result = skillExecutor.execute(msg.action, msg.params, msg.script)
                sendResponse(msg.commandId, result)
            }

            is ServerMessage.AiTask -> {
                if (aiBrain != null) {
                    aiBrain.addTask(msg.instruction, "server", msg.priority)
                    sendResponse(msg.commandId, mapOf("code" to 200, "message" to "AI任务已入队"))
                } else {
                    sendResponse(msg.commandId, mapOf("code" to 503, "message" to "AI Brain未启用"))
                }
            }

            is ServerMessage.StandingOrder -> {
                aiBrain?.addStandingOrder(msg.order)
                sendResponse(msg.commandId, mapOf("code" to 200, "message" to "常驻指令已添加"))
            }

            is ServerMessage.ConfigUpdate -> {
                msg.heartbeatInterval?.let { sec ->
                    if (sec in 5..120) {
                        heartbeatIntervalMs = sec * 1000L
                        startHeartbeat()
                        Logger.i("Heartbeat updated to ${sec}s")
                    }
                }
            }

            is ServerMessage.AgentExecute -> {
                commandsExecuted++
                val result = skillExecutor.executeAgentTask(msg.task)
                sendResponse(msg.commandId, result)
            }

            is ServerMessage.DeviceRequestAck -> {
                val hb = (msg.data["heartbeat_interval"] as? Number)?.toInt()
                if (hb != null && hb in 5..120) {
                    heartbeatIntervalMs = hb * 1000L
                    startHeartbeat()
                }
            }

            is ServerMessage.UiUpdate -> {
                HotUpdateManager.applyUpdate(context, msg.data)
                sendResponse(msg.commandId, mapOf("code" to 200, "message" to "UI updated"))
            }

            is ServerMessage.Unknown -> Logger.w("Unknown message type: ${msg.type}")
        }
    }

    private fun sendResponse(commandId: String, result: Map<String, Any?>) {
        if (!wsManager.isConnected) return
        val resp = mapOf(
            "type" to "response",
            "command_id" to commandId,
            "device_id" to deviceId,
            "code" to (result["code"] ?: 200),
            "message" to (result["message"] ?: "success"),
            "data" to (result["data"] ?: result),
            "timestamp" to (System.currentTimeMillis() / 1000),
        )
        wsManager.send(gson.toJson(resp))
    }
}
