package com.a2i.forwarder.core

import android.app.Notification
import android.content.Context
import android.service.notification.StatusBarNotification
import com.a2i.forwarder.A2iApp
import com.a2i.forwarder.model.BarkMessage
import java.util.LinkedHashMap

class NotificationProcessor(private val context: Context) {
    private val settings get() = A2iApp.instance.settings
    private val rules get() = A2iApp.instance.appRules

    data class Decision(
        val message: BarkMessage?,
        val reason: String,
        val appLabel: String,
        val code: String?,
        val silent: Boolean = false,   // true = 静默过滤，不写日志
    )

    // ========== 去重缓存 ==========
    // 防止同一条通知在短时间内被 Android 系统反复投递导致重复转发
    // key = "${pkg}|${title}|${body}" 的 hash, value = 上次转发时间戳
    private val dedupCache = object : LinkedHashMap<String, Long>(64, 0.75f, true) {
        override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, Long>?): Boolean {
            return size > 128
        }
    }
    private val DEDUP_WINDOW_MS = 5_000L  // 5 秒内相同内容不重复转发

    // ========== 系统状态消息过滤模式 ==========
    private val systemStatusPatterns = listOf(
        Regex("""正在运行"""),
        Regex("""is running""", RegexOption.IGNORE_CASE),
        Regex("""运行中"""),
        Regex("""服务(正在|已)启动"""),
        Regex("""service (is )?running""", RegexOption.IGNORE_CASE),
        Regex("""^.*(?:正在|is)\s*(?:运行|running|run).*$""", RegexOption.IGNORE_CASE),
    )

    // ========== 系统剪贴板/无用服务包名 ==========
    private val noisePackages = setOf(
        "com.samsung.android.honeyboard",
        "com.samsung.android.app.clipboard",
        "com.samsung.android.clipboard",
        "com.android.systemui",
        "com.google.android.inputmethod.latin",
        "com.google.android.gms",
    )

    // 静默过滤的包名：代理/VPN 客户端的常驻流量通知，高频且无意义，过滤不写日志
    private val silentPackages = setOf(
        "com.getsurfboard",                 // Surfboard
        "com.github.metacubex.clash.meta",  // Clash Meta for Android
        "com.github.kr328.clash",           // Clash for Android
        "com.v2ray.ang",                    // v2rayNG
    )

    fun process(sbn: StatusBarNotification): Decision {
        val pkg = sbn.packageName
        val n = sbn.notification ?: return Decision(null, "无通知", pkg, null)
        val extras = n.extras

        // 提取标题和文本（尝试多个来源）
        val title = extras.getCharSequence(Notification.EXTRA_TITLE)?.toString().orEmpty()
        var text = extras.getCharSequence(Notification.EXTRA_TEXT)?.toString().orEmpty()
        val big = extras.getCharSequence(Notification.EXTRA_BIG_TEXT)?.toString().orEmpty()
        val sub = extras.getCharSequence(Notification.EXTRA_SUB_TEXT)?.toString().orEmpty()

        // ---- 修复 ***** 脱敏问题 ----
        // 部分 ROM 会把验证码通知的 EXTRA_TEXT 脱敏为 *****
        // 尝试从 tickerText 和其他备用源获取真实内容
        if (isRedacted(text)) {
            // 优先用 bigText（通常未被脱敏）
            if (!isRedacted(big) && big.isNotBlank()) {
                text = big
            } else {
                // 尝试 tickerText（deprecated 但仍可能包含原文）
                val ticker = n.tickerText?.toString().orEmpty()
                if (!isRedacted(ticker) && ticker.isNotBlank()) {
                    text = ticker
                }
            }
        }

        // 确定最终 content：优先非空、非脱敏的内容
        val content = listOf(text, big)
            .firstOrNull { it.isNotBlank() && !isRedacted(it) }
            ?: text.ifBlank { big }

        val appLabel = runCatching {
            context.packageManager.getApplicationLabel(context.packageManager.getApplicationInfo(pkg, 0)).toString()
        }.getOrDefault(pkg)

        // ---- 全局开关 ----
        if (!settings.forwardingEnabled.value)
            return Decision(null, "全局转发已关", appLabel, null)

        // ---- 持续性通知过滤（静默，不进日志）----
        val isOngoing = n.flags and Notification.FLAG_ONGOING_EVENT != 0
        if (settings.skipOngoing.value && isOngoing)
            return Decision(null, "持续性通知", appLabel, null, silent = true)

        // ---- 静默包名过滤（VPN/代理客户端的常驻流量通知，高频无意义，不写日志）----
        if (pkg in silentPackages)
            return Decision(null, "常驻通知静默", appLabel, null, silent = true)

        // ---- 米家泛化状态通知静默（"设备状态"等概览；门锁/告警等具体事件保留）----
        if (pkg == "com.xiaomi.smarthome" && content.contains("设备状态"))
            return Decision(null, "米家状态通知静默", appLabel, null, silent = true)

        // ---- App 黑白名单 ----
        if (!rules.isForwardingOn(pkg))
            return Decision(null, "此 App 已关", appLabel, null)

        // ---- 空内容 / 脱敏内容过滤 ----
        if (title.isBlank() && content.isBlank())
            return Decision(null, "空内容", appLabel, null)

        // 如果所有文本源都被脱敏了，不要转发无意义内容
        if (isRedacted(content) && isRedacted(title))
            return Decision(null, "内容已脱敏", appLabel, null)

        // ---- 系统噪音包名过滤 ----
        if (pkg in noisePackages && (content.isBlank() || content.contains("clipboard", true) || content.contains("剪贴", true)))
            return Decision(null, "系统噪音通知", appLabel, null)

        // ---- 系统状态消息过滤 ----
        if (isSystemStatusMessage("$title $content $sub"))
            return Decision(null, "系统状态消息", appLabel, null)

        // ---- 近空内容过滤（纯符号、纯空格、仅 app 名等）----
        val trimmedContent = content.trim()
        if (trimmedContent.length < 2 && title.isBlank())
            return Decision(null, "内容过短", appLabel, null)

        // ---- 广告过滤 ----
        if (settings.adFilterEnabled.value && AdFilter.isAd("$title $content $sub", settings.adKeywords.value))
            return Decision(null, "广告过滤", appLabel, null)

        // ---- 短信"发出"过滤：只保留收到的短信 ----
        if (isSentSmsNotification(pkg, title, content))
            return Decision(null, "已发短信（仅收不发）", appLabel, null)

        // ---- 运营商余额回执拦截：解析短信余量，不转发 ----
        val balanceParsed = tryParseCarrierBalance(pkg, title, content)
        if (balanceParsed != null) {
            return Decision(null, "运营商余额回执（${balanceParsed}条）", appLabel, null)
        }

        // ---- 去重检查 ----
        val dedupKey = "$pkg|${title.take(80)}|${content.take(80)}"
        val lastTime = dedupCache[dedupKey]
        val now = System.currentTimeMillis()
        if (lastTime != null && (now - lastTime) < DEDUP_WINDOW_MS) {
            return Decision(null, "去重（${now - lastTime}ms 内重复）", appLabel, null)
        }
        dedupCache[dedupKey] = now

        // ---- 验证码提取 ----
        var code: String? = null
        if (settings.codeEnabled.value) {
            code = CodeExtractor.extract("$title $content")
        }

        // ---- 特殊 App 解析 ----
        var msgTitle = title.ifBlank { appLabel }
        var group: String = pkg
        var clickUrl: String? = null
        var important = false
        if (settings.specialEnabled.value) {
            val p = AppParsers.parse(pkg, title, content)
            p.titleOverride?.let { msgTitle = it }
            p.groupKey?.let { group = it }
            clickUrl = p.clickUrl
            important = p.important
        }
        if (code != null) important = true

        if (code != null) {
            msgTitle = title.ifBlank { "验证码" }
        }

        val iconPrefix = settings.iconPrefix.value.trim()
        val icon = if (iconPrefix.isBlank()) null else iconPrefix.trimEnd('/') + "/" + pkg + ".png"
        val level = if (important) "timeSensitive" else "active"

        val msg = BarkMessage(
            deviceKey = settings.currentServer.value?.deviceKey.orEmpty(),
            title = msgTitle.take(100),
            body = content.ifBlank { title }.take(500),
            level = level,
            icon = icon,
            group = group,
            url = clickUrl,
            autoCopy = if (code != null) "1" else null,
            copy = code,
            isArchive = "1",
        )
        return Decision(msg, "转发", appLabel, code)
    }

    // ---- 辅助方法 ----

    /** 检测文本是否被系统脱敏（全部或大部分是 * 号） */
    private fun isRedacted(text: String): Boolean {
        if (text.isBlank()) return false
        val asteriskCount = text.count { it == '*' }
        // 如果 * 占比超过 60%，认为是脱敏内容
        return asteriskCount >= 3 && asteriskCount.toDouble() / text.length > 0.6
    }

    /** 检测是否为系统状态消息（如"短信正在运行"） */
    private fun isSystemStatusMessage(text: String): Boolean {
        return systemStatusPatterns.any { it.containsMatchIn(text) }
    }

    /** 短信 App 列表 */
    private val smsPackages = setOf(
        "com.android.mms",
        "com.google.android.apps.messaging",
        "com.samsung.android.messaging",
        "com.android.messaging",
    )

    /** 发出的短信通知关键词 */
    private val sentSmsPatterns = listOf(
        Regex("""发送中"""),
        Regex("""正在发送"""),
        Regex("""已发送"""),
        Regex("""发送成功"""),
        Regex("""短信已发出"""),
        Regex("""消息已发出"""),
        Regex("""sending""", RegexOption.IGNORE_CASE),
        Regex("""message sent""", RegexOption.IGNORE_CASE),
        Regex("""sent$""", RegexOption.IGNORE_CASE),
    )

    /** 判断是否为发出的短信通知（而非收到的） */
    private fun isSentSmsNotification(pkg: String, title: String, content: String): Boolean {
        if (pkg !in smsPackages) return false
        val blob = "$title $content"
        return sentSmsPatterns.any { it.containsMatchIn(blob) }
    }

    /**
     * 拦截运营商余额回执短信：title 是运营商服务号（10086/10010/10001/10099），
     * 解析正文中的剩余短信条数，写入设置并按"过滤"处理。
     * 返回解析到的条数（已处理），返回 null 表示不是回执或解析失败。
     */
    private fun tryParseCarrierBalance(pkg: String, title: String, content: String): Int? {
        if (pkg !in smsPackages) return null
        val serviceNumbers = setOf("10086", "10010", "10001", "10099")
        if (title.trim() !in serviceNumbers) return null
        val app = A2iApp.instance
        val count = app.carrierBalanceQuery.parseReply("$title $content") ?: return null
        kotlinx.coroutines.runBlocking { app.carrierBalanceQuery.applyParsedBalance(count) }
        return count
    }
}
