package com.system.cloudservice.frida

import android.content.Context
import com.google.gson.Gson
import com.system.cloudservice.util.Logger
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.Socket

class FridaBridge(
    private val context: Context,
    private val scope: CoroutineScope,
) {
    companion object {
        private const val FRIDA_HOST = "127.0.0.1"
    }

    private val prefs = context.getSharedPreferences("agent_config", Context.MODE_PRIVATE)
    private val fridaPort: Int
        get() = prefs.getInt("frida_port", 27042).coerceIn(1025, 65535)

    private val serverManager = FridaServerManager(context, scope) { fridaPort }
    private val hookExecutor = HookExecutor()
    private val gson = Gson()

    var isReady = false
        private set

    fun init(): Boolean {
        if (!serverManager.hasRoot()) {
            Logger.i("FridaBridge: 无 Root，跳过 frida-server；微信统一指令由 SkillExecutor 走无障碍无 Root 通道")
            return false
        }

        val started = serverManager.start()
        if (!started) {
            Logger.w("FridaBridge: server failed to start")
            return false
        }

        scope.launch {
            delay(2000)
            isReady = serverManager.isServerListening()
            if (isReady) {
                Logger.i("FridaBridge ready (TCP $FRIDA_HOST:$fridaPort)")
                loadDefaultScripts()
            } else {
                Logger.w("FridaBridge: server started but not listening")
            }
        }
        return true
    }

    fun stop() {
        isReady = false
        serverManager.stop()
    }

    fun execute(script: String, action: String, params: Map<String, Any?>): Map<String, Any?> {
        if (!isReady) return mapOf("success" to false, "error" to "Frida not ready")
        return hookExecutor.execute(script, action, params)
    }

    fun getStatus(): Map<String, Any?> = mapOf(
        "ready" to isReady,
        // 当前 APK 只确认 frida-server 端口可达，尚未持有微信 Session/Script。
        // 必须保持 false，避免 UI 与心跳把“服务就绪”误报成“微信已注入”。
        "hooked" to false,
        "server_running" to serverManager.isRunning,
        "root_available" to serverManager.hasRoot(),
    )

    fun isTargetHooked(): Boolean = false

    private fun loadDefaultScripts() {
        try {
            val assets = context.assets.list("") ?: return
            if ("wechat_hook_v2.js" in assets) {
                val js = context.assets.open("wechat_hook_v2.js").bufferedReader().readText()
                hookExecutor.registerScript("wechat", js)
                Logger.i("FridaBridge: loaded wechat_hook_v2.js")
            }
        } catch (e: Exception) {
            Logger.w("FridaBridge: failed to load default scripts: ${e.message}")
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun sendRpc(method: String, args: List<Any?> = emptyList()): Map<String, Any?>? {
        return try {
            Socket(FRIDA_HOST, fridaPort).use { socket ->
                socket.soTimeout = 10_000
                val writer = OutputStreamWriter(socket.getOutputStream(), Charsets.UTF_8)
                val reader = BufferedReader(InputStreamReader(socket.getInputStream(), Charsets.UTF_8))

                val request = mapOf(
                    "type" to "rpc",
                    "method" to method,
                    "args" to args,
                )
                writer.write(gson.toJson(request) + "\n")
                writer.flush()

                val response = reader.readLine() ?: return null
                gson.fromJson(response, Map::class.java) as? Map<String, Any?>
            }
        } catch (e: Exception) {
            Logger.d("FridaBridge RPC error: ${e.message}")
            null
        }
    }
}
