package com.beancount.mobile.accessibility

import android.util.Log
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.WritableNativeArray
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap

/**
 * 无障碍服务 RN 桥接模块（plan.md「3.6 无障碍服务」JS 接线）。
 *
 * SelectToSpeakService 是 AccessibilityService 子类（非 RN 模块），
 * 其方法无法直接从 JS 调用。本模块作为中间层，通过 instance 静态引用
 * 把 JS 调用委托给服务实例。
 *
 * 由 Config Plugin 的 withMainApplication 注入 add(AccessibilityBridgePackage())。
 */
class AccessibilityBridgeModule(private val reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {

    companion object {
        private const val TAG = "AccessibilityBridge"
    }

    init {
        ReactContextHolder.context = reactContext
    }

    override fun invalidate() {
        super.invalidate()
        if (ReactContextHolder.context === reactContext) {
            ReactContextHolder.context = null
        }
    }

    override fun getName() = "AccessibilityBridge"

    /** 无障碍服务是否已连接（用户已在系统设置中启用）。 */
    @ReactMethod
    fun isServiceRunning(promise: Promise) {
        promise.resolve(SelectToSpeakService.instance != null)
    }

    /**
     * 记住当前页面：把当前顶部 App 的 pkg|activity 加入白名单，
     * 之后该页面内容变化时自动截图 → OCR。
     */
    @ReactMethod
    fun rememberCurrentPage(promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service == null) {
            promise.reject("SERVICE_NOT_RUNNING", "无障碍服务未启用")
            return
        }
        val pkg = service.getTopPackage()
        if (pkg == null) {
            promise.reject("NO_TOP_PACKAGE", "当前没有检测到前台 App")
            return
        }
        service.rememberCurrentPage()
        val result = WritableNativeMap()
        result.putString("package", pkg)
        result.putString("activity", service.getTopActivity() ?: "")
        result.putString("signature", "$pkg|${service.getTopActivity() ?: ""}")
        promise.resolve(result)
    }

    /** 手动触发一次 OCR（截取当前屏幕并发送给 JS 层处理）。 */
    @ReactMethod
    fun triggerManualOcr(promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service == null) {
            promise.reject("SERVICE_NOT_RUNNING", "无障碍服务未启用")
            return
        }
        try {
            service.triggerManualOcr()
            promise.resolve(true)
        } catch (e: Exception) {
            promise.reject("OCR_TRIGGER_FAIL", e.message)
        }
    }

    /** 获取所有已记住的页面签名列表。 */
    @ReactMethod
    fun getPageSignatures(promise: Promise) {
        val service = SelectToSpeakService.instance
        val sigsSet = if (service != null) {
            service.getPageSignatures()
        } else {
            try {
                val prefs = reactContext.getSharedPreferences("billing_accessibility_prefs", android.content.Context.MODE_PRIVATE)
                prefs.getStringSet("page_signatures", emptySet()) ?: emptySet()
            } catch (e: Exception) {
                emptySet()
            }
        }
        val arr = WritableNativeArray()
        for (sig in sigsSet) {
            val parts = sig.split("|", limit = 2)
            val map = WritableNativeMap()
            map.putString("signature", sig)
            map.putString("package", parts.getOrNull(0) ?: "")
            map.putString("activity", parts.getOrNull(1) ?: "")
            arr.pushMap(map)
        }
        promise.resolve(arr)
    }

    /** 清空所有已记住的页面签名。 */
    @ReactMethod
    fun clearPageSignatures(promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service != null) {
            service.clearPageSignatures()
        } else {
            try {
                val prefs = reactContext.getSharedPreferences("billing_accessibility_prefs", android.content.Context.MODE_PRIVATE)
                prefs.edit().putStringSet("page_signatures", emptySet()).apply()
            } catch (e: Exception) {
                promise.reject("CLEAR_PREFS_FAIL", e.message)
                return
            }
        }
        promise.resolve(true)
    }

    /** 删除指定页面签名。 */
    @ReactMethod
    fun removePageSignature(signature: String, promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service != null) {
            service.removePageSignature(signature)
        } else {
            try {
                val prefs = reactContext.getSharedPreferences("billing_accessibility_prefs", android.content.Context.MODE_PRIVATE)
                val saved = prefs.getStringSet("page_signatures", emptySet()) ?: emptySet()
                val mutable = HashSet(saved)
                if (mutable.remove(signature)) {
                    prefs.edit().putStringSet("page_signatures", mutable).apply()
                }
            } catch (e: Exception) {
                promise.reject("REMOVE_PREFS_FAIL", e.message)
                return
            }
        }
        promise.resolve(true)
    }

    /** 获取支付 App 白名单（供 JS 端展示）。 */
    @ReactMethod
    fun getPaymentPackages(promise: Promise) {
        val arr = WritableNativeArray()
        for (pkg in SelectToSpeakService.PAYMENT_PACKAGES) {
            arr.pushString(pkg)
        }
        promise.resolve(arr)
    }

    /** 获取当前顶部 App 信息（供 JS 判断用户是否在支付页面）。 */
    @ReactMethod
    fun getTopApp(promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service == null) {
            promise.reject("SERVICE_NOT_RUNNING", "无障碍服务未启用")
            return
        }
        val map = WritableNativeMap()
        map.putString("package", service.getTopPackage() ?: "")
        map.putString("activity", service.getTopActivity() ?: "")
        promise.resolve(map)
    }

    /** 将当前应用拉起至前台，用以在后台识别出账单后，弹窗让用户进行交易确认 */
    @ReactMethod
    fun bringAppToForeground(promise: Promise) {
        val service = SelectToSpeakService.instance
        if (service == null) {
            promise.reject("SERVICE_NOT_RUNNING", "无障碍服务未启用")
            return
        }
        try {
            service.bringAppToForeground()
            promise.resolve(true)
        } catch (e: Exception) {
            promise.reject("FAIL", e.message)
        }
    }

    /** 显示账单浮窗（直接在当前其他应用上方渲染，不返回 App 内） */
    @ReactMethod
    fun showFloatingBill(
        amount: String,
        merchant: String,
        time: String,
        packageName: String,
        categories: ReadableArray,
        accounts: ReadableArray,
        direction: String,
        currency: String,
        draftId: String,
        promise: Promise
    ) {
        val context = SelectToSpeakService.instance ?: reactContext.currentActivity
        if (context == null) {
            promise.reject("NO_CONTEXT", "无法获取当前前台 Activity 或 AccessibilityService 实例")
            return
        }

        val categoryList = mutableListOf<Map<String, String>>()
        for (i in 0 until categories.size()) {
            val map = categories.getMap(i)
            categoryList.add(mapOf(
                "id" to (map?.getString("id") ?: ""),
                "name" to (map?.getString("name") ?: ""),
                "account" to (map?.getString("account") ?: ""),
                "type" to (map?.getString("type") ?: "")
            ))
        }

        val accountList = mutableListOf<String>()
        for (i in 0 until accounts.size()) {
            accountList.add(accounts.getString(i) ?: "")
        }

        android.os.Handler(android.os.Looper.getMainLooper()).post {
            try {
                val floatingView = FloatingBillView(context, draftId, amount, merchant, time, packageName, categoryList, accountList, direction, currency)
                floatingView.show()
                promise.resolve(true)
            } catch (e: Exception) {
                promise.reject("FAIL", e.message)
            }
        }
    }

    /** 设置悬浮球开关状态。 */
    @ReactMethod
    fun setFloatingBallEnabled(enabled: Boolean, promise: Promise) {
        SelectToSpeakService.floatingBallEnabled = enabled
        val service = SelectToSpeakService.instance
        if (service == null) {
            promise.resolve(true)
            return
        }
        if (!enabled) {
            service.dismissFloatingHelper()
        } else {
            service.refreshFloatingHelper()
        }
        promise.resolve(true)
    }

    /** 从 APK assets 复制 OCR 模型文件到 filesDir/ocr_models_v6。 */
    @ReactMethod
    fun copyOcrModelsFromAssets(promise: Promise) {
        try {
            val destDir = java.io.File(reactContext.filesDir, "ocr_models_v6")
            if (!destDir.exists()) destDir.mkdirs()
            val assetManager = reactContext.assets
            val files = listOf("ppocrv6_det.onnx", "ppocrv6_rec.onnx", "ppocrv6_dict.txt")
            for (filename in files) {
                val destFile = java.io.File(destDir, filename)
                if (destFile.exists() && destFile.length() > 0) continue
                assetManager.open(filename).use { input ->
                    java.io.FileOutputStream(destFile).use { output ->
                        input.copyTo(output)
                    }
                }
            }
            promise.resolve(destDir.absolutePath)
        } catch (e: Exception) {
            promise.reject("COPY_MODEL_FAIL", e.message)
        }
    }

    /** 检查通知监听服务是否已启用（反射调用 getEnabledListenerPackages，避免 API level lint 错误）。 */
    @ReactMethod
    fun isNotificationListenerEnabled(promise: Promise) {
        try {
            // 通过 Settings.Secure 读取系统设置（公开 API，无需反射，不受 hidden API 限制）
            // enabled_notification_listeners 格式：
            //   "com.pkg1/com.pkg1.Service:com.pkg2/com.pkg2.Service"
            val flat = android.provider.Settings.Secure.getString(
                reactContext.contentResolver,
                "enabled_notification_listeners"
            ) ?: ""
            val myPkg = reactContext.packageName
            promise.resolve(flat.split(":").any { it.startsWith(myPkg) })
        } catch (e: Exception) {
            Log.w(TAG, "isNotificationListenerEnabled 检测失败", e)
            promise.resolve(false)
        }
    }

    /** 检查悬浮窗权限（SYSTEM_ALERT_WINDOW）是否已授予。 */
    @ReactMethod
    fun canDrawOverlays(promise: Promise) {
        try {
            promise.resolve(android.provider.Settings.canDrawOverlays(reactContext))
        } catch (e: Exception) {
            promise.resolve(false)
        }
    }

    /**
     * 下发浮层 UI 配置（颜色 + 文案）。
     * JS 侧从 theme tokens + i18n 构建 config，
     * 原生存 SharedPreferences，三个浮层组件读取。
     */
    @ReactMethod
    fun setFloatingUiConfig(config: ReadableMap, promise: Promise) {
        try {
            FloatingUiConfigStore.save(reactContext, config)
            promise.resolve(true)
        } catch (e: Exception) {
            promise.reject("CONFIG_SAVE_FAIL", e.message)
        }
    }
}
