package com.system.cloudservice.engine

import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Base64
import com.system.cloudservice.antiban.AntiBanManager
import com.system.cloudservice.frida.FridaBridge
import com.system.cloudservice.service.AgentAccessibilityService
import com.system.cloudservice.util.DeviceInfo
import com.system.cloudservice.util.Logger
import com.system.cloudservice.util.ShellExecutor
import java.io.File

class SkillExecutor(
    private val context: Context,
    private val fridaBridge: FridaBridge?,
    private val antiBan: AntiBanManager,
) {
    suspend fun execute(
        action: String,
        params: Map<String, Any?>,
        script: String? = null,
    ): Map<String, Any?> {
        // Pre-action risk check
        if (!antiBan.canOperate(action)) {
            val status = antiBan.riskStatus()
            return mapOf("code" to 429, "message" to "风控暂停中", "data" to status)
        }

        // wechat/douyin 等业务脚本走 Hook 优先链；android/system/device 是设备控制域，
        // 不能误当成 Frida 脚本，否则公网 Fleet 的 app_start/ui_tree 会落到 Shell不支持。
        val deviceControlScripts = setOf("android", "system", "device")
        if (script != null && script.lowercase() !in deviceControlScripts) {
            return executeWithFridaPriority(script, action, params)
        }

        return try {
            when (action) {
                "open_app", "app_start", "start_app" -> openApp(params["package"] as? String ?: "")
                "app_stop", "stop_app" -> doShell(
                    mapOf("command" to "am force-stop ${params["package"] ?: ""}")
                )
                "get_installed_apps" -> getInstalledApps()
                "get_device_info" -> mapOf("code" to 200, "data" to DeviceInfo.collectFull(context))
                "click", "tap" -> doClick(params)
                "swipe" -> doSwipe(params)
                "input_text", "input" -> doInputText(params)
                "key_event", "press_key" -> doKeyEvent(params)
                "screenshot" -> doScreenshot(params)
                "back" -> doGlobalAction("back")
                "home" -> doGlobalAction("home")
                "recent" -> doGlobalAction("recent")
                "dump_ui", "ui_tree" -> doDumpUi()
                "shell", "run_shell" -> doShell(params)
                "get_foreground" -> doShell(mapOf("command" to "dumpsys window | grep mCurrentFocus"))
                "install_apk" -> doShell(mapOf("command" to "pm install -r -g ${params["path"]}"))
                "voice_command" -> mapOf("code" to 200, "message" to "语音命令已接收: ${params["text"]}")
                "status" -> mapOf("code" to 200, "data" to DeviceInfo.collectQuick(context))
                else -> mapOf("code" to 400, "message" to "未知命令: $action")
            }
        } catch (e: Exception) {
            Logger.e("SkillExecutor error: $action", e)
            mapOf<String, Any?>("code" to 500, "message" to (e.message ?: "执行失败"))
        }
    }

    suspend fun executeAgentTask(task: String): Map<String, Any?> {
        if (task.isBlank()) return mapOf<String, Any?>("code" to 400, "message" to "task为空")
        Logger.i("Agent task: $task")

        return try {
            val result = when {
                task.contains("微信") -> executeWithFridaPriority("wechat", "agent_task", mapOf("task" to task))
                task.contains("抖音") -> executeWithFridaPriority("douyin", "agent_task", mapOf("task" to task))
                task.contains("小红书") -> executeWithFridaPriority("xhs", "agent_task", mapOf("task" to task))
                else -> {
                    val shellResult = ShellExecutor.execute(task)
                    mapOf("code" to if (shellResult.success) 200 else 500, "data" to mapOf("output" to shellResult.output))
                }
            }
            result
        } catch (e: Exception) {
            Logger.e("Agent task error", e)
            mapOf<String, Any?>("code" to 500, "message" to (e.message ?: "执行失败"))
        }
    }

    private suspend fun executeWithFridaPriority(
        script: String, action: String, params: Map<String, Any?>
    ): Map<String, Any?> {
        // frida-server 端口可用不等于已经附着目标进程。状态接口必须分开上报，
        // 避免把“脚本已装载”误报成“微信 Hook 成功”。
        if (script == "wechat" && action == "get_hook_status") {
            val status = fridaBridge?.getStatus().orEmpty()
            return mapOf(
                "code" to 200,
                "data" to mapOf(
                    "success" to true,
                    "hooked" to false,
                    "frida_server_ready" to (status["ready"] == true),
                    "root_available" to (status["root_available"] == true),
                    "channel" to if (status["ready"] == true) "frida_server" else "no_root_a11y",
                    "note" to if (status["ready"] == true)
                        "Frida 服务已就绪，微信进程尚未确认注入"
                    else "Frida 未就绪，使用无障碍自动化",
                ),
                "channel" to if (status["ready"] == true) "frida_server" else "no_root_a11y",
            )
        }

        // Channel 1: Frida Hook (priority)
        if (fridaBridge?.isReady == true) {
            try {
                val result = fridaBridge.execute(script, action, params)
                if (result["success"] == true) {
                    return mapOf("code" to 200, "data" to result, "channel" to "frida")
                }
            } catch (e: Exception) {
                Logger.w("Frida execute failed, falling back to A11y: ${e.message}")
            }
        }

        // Channel 1b: 无 Root 微信（无障碍 + 包信息），与 Frida Hook 能力不对等但可闭环
        tryWechatNoRoot(script, action, params)?.let { return it }

        // Channel 2: Accessibility Service
        val a11y = AgentAccessibilityService.getInstance()
        if (a11y != null) {
            return executeViaAccessibility(a11y, action, params)
        }

        // Channel 3: Shell fallback
        return executeViaShell(action, params)
    }

    /**
     * 无 Root / Frida 未就绪时，微信脚本走无障碍与包管理器。
     * 返回 null 表示交给后续通用 A11y/Shell。
     */
    private suspend fun tryWechatNoRoot(
        script: String,
        action: String,
        params: Map<String, Any?>,
    ): Map<String, Any?>? {
        if (script != "wechat") return null

        when (action) {
            "ping" -> {
                val ver = WechatNoRootAutomation.getWechatVersion(context)
                val quick = DeviceInfo.collectQuick(context)
                return mapOf(
                    "code" to 200,
                    "message" to "pong",
                    "data" to mapOf(
                        "success" to true,
                        "installed" to (ver["success"] == true),
                        "wechat_version" to ver["version"],
                        "wechat_running" to (quick["wechat_running"] == true),
                        "channel" to "no_root_pkg",
                    ),
                    "channel" to "no_root_pkg",
                )
            }
            "get_wechat_version" -> {
                val data = WechatNoRootAutomation.getWechatVersion(context)
                return mapOf(
                    "code" to 200,
                    "data" to data,
                    "channel" to "no_root_pkg",
                )
            }
            "get_hook_status" -> {
                val data = WechatNoRootAutomation.hookStatusStub()
                return mapOf("code" to 200, "data" to data, "channel" to "no_root_a11y")
            }
            "get_process_info" -> {
                val ver = WechatNoRootAutomation.getWechatVersion(context)
                val quick = DeviceInfo.collectQuick(context)
                return mapOf(
                    "code" to 200,
                    "data" to mapOf(
                        "success" to true,
                        "wechat" to ver,
                        "device_quick" to quick,
                        "channel" to "no_root_pkg",
                    ),
                    "channel" to "no_root_pkg",
                )
            }
            "check_login_state" -> {
                val ver = WechatNoRootAutomation.getWechatVersion(context)
                val running = DeviceInfo.collectQuick(context)["wechat_running"] == true
                val installed = ver["success"] == true
                return mapOf(
                    "code" to 200,
                    "data" to mapOf(
                        "success" to true,
                        "installed" to installed,
                        "wechat_running" to running,
                        "note" to "无 Root 无法读登录态数据库，仅启发式",
                        "channel" to "no_root_pkg",
                    ),
                    "channel" to "no_root_pkg",
                )
            }
            "get_profile" -> {
                // 未注入时返回设备端已验证缓存资料，明确标注来源；不能把真 Root 设备误报为无 Root。
                val quick = DeviceInfo.collectQuick(context)
                val wxid = quick["wxid"]?.toString().orEmpty()
                return mapOf(
                    "code" to if (wxid.isNotEmpty()) 200 else 501,
                    "message" to if (wxid.isNotEmpty()) "success" else "微信资料缓存尚未生成",
                    "data" to mapOf(
                        "success" to wxid.isNotEmpty(),
                        "wxid" to wxid,
                        "wechat_version" to quick["wechat_version"],
                        "wechat_running" to quick["wechat_running"],
                        "source" to "device_verified_cache",
                        "channel" to "root_cache",
                    ),
                    "channel" to "root_cache",
                )
            }
            "get_contacts", "get_messages" -> {
                return mapOf(
                    "code" to 501,
                    "message" to "无 Root 未注入微信进程，无法读联系人/消息/资料库；请开启 Root+Frida 或使用 send_message(无障碍)",
                    "data" to mapOf(
                        "success" to false,
                        "error" to "not_supported_no_root",
                        "channel" to "no_root_a11y",
                    ),
                    "channel" to "no_root_a11y",
                )
            }
            "send_message" -> {
                val a11y = AgentAccessibilityService.getInstance()
                    ?: return mapOf(
                        "code" to 503,
                        "message" to "请先开启本应用的无障碍服务",
                        "data" to mapOf(
                            "success" to false,
                            "error" to "accessibility_disabled",
                            "channel" to "no_root_a11y",
                        ),
                        "channel" to "no_root_a11y",
                    )
                val toId = (params["to_id"] as? String)?.trim().orEmpty()
                    .ifEmpty { (params["to"] as? String)?.trim().orEmpty() }
                val content = (params["content"] as? String).orEmpty()
                val data = WechatNoRootAutomation.sendTextMessage(context, a11y, toId, content)
                val ok = data["success"] == true
                return mapOf(
                    "code" to if (ok) 200 else 500,
                    "data" to data,
                    "channel" to (data["channel"]?.toString() ?: "no_root_a11y"),
                )
            }
        }
        return null
    }

    private fun executeViaAccessibility(
        a11y: AgentAccessibilityService, action: String, params: Map<String, Any?>
    ): Map<String, Any?> {
        return when (action) {
            "click", "tap" -> {
                val x = (params["x"] as? Number)?.toInt() ?: 0
                val y = (params["y"] as? Number)?.toInt() ?: 0
                a11y.click(x, y)
                mapOf("code" to 200, "message" to "已点击 ($x, $y)", "channel" to "a11y")
            }
            "swipe" -> {
                val x1 = (params["x1"] as? Number)?.toInt() ?: 0
                val y1 = (params["y1"] as? Number)?.toInt() ?: 0
                val x2 = (params["x2"] as? Number)?.toInt() ?: 0
                val y2 = (params["y2"] as? Number)?.toInt() ?: 0
                val dur = (params["duration"] as? Number)?.toLong() ?: 300L
                a11y.swipe(x1, y1, x2, y2, dur)
                mapOf("code" to 200, "message" to "已滑动", "channel" to "a11y")
            }
            "input_text", "input" -> {
                val text = params["text"] as? String ?: ""
                a11y.inputText(text)
                mapOf("code" to 200, "message" to "已输入", "channel" to "a11y")
            }
            "back" -> { a11y.back(); mapOf("code" to 200, "message" to "已返回", "channel" to "a11y") }
            "home" -> { a11y.home(); mapOf("code" to 200, "message" to "已回到桌面", "channel" to "a11y") }
            else -> executeViaShell(action, params)
        }
    }

    private fun executeViaShell(action: String, params: Map<String, Any?>): Map<String, Any?> {
        val cmd = when (action) {
            "click", "tap" -> "input tap ${params["x"]} ${params["y"]}"
            "swipe" -> "input swipe ${params["x1"]} ${params["y1"]} ${params["x2"]} ${params["y2"]} ${params["duration"] ?: 300}"
            "input_text", "input" -> "am broadcast -a ADB_INPUT_TEXT --es msg '${params["text"]}'"
            "back" -> "input keyevent KEYCODE_BACK"
            "home" -> "input keyevent KEYCODE_HOME"
            "screenshot" -> "screencap -p ${params["path"] ?: "/sdcard/screenshot.png"}"
            "key_event" -> "input keyevent ${params["keycode"]}"
            else -> return mapOf("code" to 400, "message" to "Shell不支持: $action")
        }
        val r = ShellExecutor.execute(cmd)
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output, "channel" to "shell")
    }

    // ── Basic actions ──

    private fun doClick(params: Map<String, Any?>): Map<String, Any?> {
        val x = (params["x"] as? Number)?.toInt() ?: 0
        val y = (params["y"] as? Number)?.toInt() ?: 0
        val a11y = AgentAccessibilityService.getInstance()
        if (a11y != null) {
            a11y.click(x, y)
            return mapOf("code" to 200, "message" to "已点击 ($x, $y)")
        }
        val r = ShellExecutor.execute("input tap $x $y")
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun doSwipe(params: Map<String, Any?>): Map<String, Any?> {
        val x1 = (params["x1"] as? Number)?.toInt() ?: 0
        val y1 = (params["y1"] as? Number)?.toInt() ?: 0
        val x2 = (params["x2"] as? Number)?.toInt() ?: 0
        val y2 = (params["y2"] as? Number)?.toInt() ?: 0
        val dur = (params["duration"] as? Number)?.toInt() ?: 300
        val a11y = AgentAccessibilityService.getInstance()
        if (a11y != null) {
            a11y.swipe(x1, y1, x2, y2, dur.toLong())
            return mapOf("code" to 200, "message" to "已滑动")
        }
        val r = ShellExecutor.execute("input swipe $x1 $y1 $x2 $y2 $dur")
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun doInputText(params: Map<String, Any?>): Map<String, Any?> {
        val text = params["text"] as? String ?: ""
        val a11y = AgentAccessibilityService.getInstance()
        if (a11y != null) {
            a11y.inputText(text)
            return mapOf("code" to 200, "message" to "已输入文字")
        }
        val r = ShellExecutor.execute("am broadcast -a ADB_INPUT_TEXT --es msg '$text'")
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun doKeyEvent(params: Map<String, Any?>): Map<String, Any?> {
        val keycode = params["keycode"] as? String ?: ""
        val r = ShellExecutor.execute("input keyevent $keycode")
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun doScreenshot(params: Map<String, Any?>): Map<String, Any?> {
        val path = params["path"] as? String
            ?: File(context.cacheDir, "workphone_screenshot.png").absolutePath
        val r = ShellExecutor.execute("screencap -p '$path' && chmod 644 '$path'")
        if (!r.success) {
            return mapOf("code" to 500, "message" to r.output)
        }
        return try {
            val bytes = File(path).readBytes()
            mapOf(
                "code" to 200,
                "message" to "ok",
                "data" to mapOf(
                    "image_base64" to Base64.encodeToString(bytes, Base64.NO_WRAP),
                    "mime_type" to "image/png",
                    "size" to bytes.size,
                    "path" to path,
                ),
            )
        } catch (e: Exception) {
            mapOf("code" to 500, "message" to "截图读取失败: ${e.message}")
        }
    }

    private fun doGlobalAction(action: String): Map<String, Any?> {
        val a11y = AgentAccessibilityService.getInstance()
        if (a11y != null) {
            when (action) {
                "back" -> a11y.back()
                "home" -> a11y.home()
                "recent" -> a11y.recent()
            }
            return mapOf("code" to 200, "message" to "已执行: $action")
        }
        val keycode = when (action) {
            "back" -> "KEYCODE_BACK"
            "home" -> "KEYCODE_HOME"
            "recent" -> "KEYCODE_APP_SWITCH"
            else -> return mapOf("code" to 400, "message" to "unknown")
        }
        val r = ShellExecutor.execute("input keyevent $keycode")
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun doDumpUi(): Map<String, Any?> {
        val r = ShellExecutor.execute("uiautomator dump /sdcard/ui.xml && cat /sdcard/ui.xml")
        return mapOf("code" to if (r.success) 200 else 500, "data" to mapOf("xml" to r.output))
    }

    private fun doShell(params: Map<String, Any?>): Map<String, Any?> {
        val cmd = params["command"] as? String ?: return mapOf("code" to 400, "message" to "command参数为空")
        val r = ShellExecutor.execute(cmd)
        return mapOf("code" to if (r.success) 200 else 500, "message" to r.output)
    }

    private fun openApp(packageName: String): Map<String, Any?> {
        return try {
            val intent = context.packageManager.getLaunchIntentForPackage(packageName)
            if (intent != null) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                context.startActivity(intent)
                mapOf("code" to 200, "message" to "已打开 $packageName")
            } else {
                mapOf("code" to 404, "message" to "未找到应用: $packageName")
            }
        } catch (e: Exception) {
            mapOf<String, Any?>("code" to 500, "message" to (e.message ?: "打开失败"))
        }
    }

    private fun getInstalledApps(): Map<String, Any?> {
        val apps = context.packageManager.getInstalledApplications(0)
            .filter { context.packageManager.getLaunchIntentForPackage(it.packageName) != null }
            .map { it.packageName }
        return mapOf("code" to 200, "data" to apps)
    }
}
