package expo.modules.rebreakprotection.accessibility

import android.accessibilityservice.AccessibilityService
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.Toast
import expo.modules.rebreakprotection.vpn.RebreakVpnService

/**
 * Tamper-Lock — sichert den Schutz gegen versehentliches/impulsives Abschalten.
 *
 * **Was dieser Service NICHT (mehr) tut:** Glücksspielseiten blocken. Das macht
 * ausschließlich der `RebreakVpnService` (DNS-Filter, ~208k Domains) — der greift
 * network-level, also in Browsern *und* Apps, und übersteht Browser-Updates. Den
 * früheren Browser-Adressleisten-Filter haben wir entfernt: redundant zum VPN,
 * fragil (View-IDs pro Browser), und führte zu Geister-Blockaden (VPN aus, a11y
 * blockt trotzdem noch).
 *
 * **Was dieser Service tut:** Wenn der User „App-Lock" aktiviert hat
 * (`tamper_armed == true`, opt-in über den App-Button) und der Schutz aktiv ist
 * (`filter_enabled == true`), verhindert er, dass schutz-relevante System-Settings
 * geöffnet werden können:
 *  - Settings → VPN → ReBreak abschalten / „Always-on" / Profil löschen
 *  - App-Info / Play-Store → ReBreak deinstallieren / Daten löschen / Force-Stop
 *  - Settings → Bedienungshilfen → den ReBreak-a11y-Service selbst abschalten
 *
 * Strategie: lauscht auf `TYPE_WINDOW_STATE_CHANGED` (Activity-Wechsel) +
 * `TYPE_WINDOW_CONTENT_CHANGED` (Dialog-Inhalt lädt bei manchen OEMs nach) aus
 * den Settings-/Installer-Packages → matcht Activity-Klasse bzw. Window-Text gegen
 * gefährliche Patterns → `GLOBAL_ACTION_BACK` + Toast. Rauskommen geht nur über
 * den regulären Deaktivierungs-Cooldown (der disarmed `tamper_armed` und stoppt
 * das VPN — danach ist dieser Service vollständig passiv).
 *
 * Bypass-Vektoren die das NICHT abdeckt: Safe-Mode-Reboot (Apps off), ADB/Root.
 */
class RebreakAccessibilityService : AccessibilityService() {

    private val mainHandler = Handler(Looper.getMainLooper())
    private var lastSettingsCheck: Long = 0L
    /** Nach einem TAMPER-BLOCK: kurz keinen weiteren Block triggern. Verhindert
     *  Toast-Spam wenn alte Keyword-Matches in Page-Transitions nachziehen. */
    private var lastBlockAt: Long = 0L
    private val POST_BLOCK_COOLDOWN_MS = 3000L

    override fun onServiceConnected() {
        super.onServiceConnected()
        instance = this
        Log.i(TAG, "tamper-lock service connected")
    }

    override fun onDestroy() {
        if (instance === this) instance = null
        super.onDestroy()
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
        if (event == null) return

        // Globaler Kill-Switch: Dieser Service tut NUR etwas, wenn der Schutz
        // aktiv ist (`filter_enabled`) UND der User „App-Lock" explizit armed hat
        // (`tamper_armed`). Beides muss stimmen — sonst vollständig passiv (auch
        // kein Logging). Deckt ab: frisches Onboarding, abgelaufener Cooldown
        // (disarmTamperLock), und den Desync-Fall „tamper noch armed aber Schutz
        // aus" (z.B. VPN extern revoked). Der a11y-Service kann sich nicht selbst
        // deaktivieren, also ist das hier die einzige Stelle wo wir ihn stilllegen.
        if (!isTamperLockArmed() || !isProtectionEnabled()) return

        val pkg = event.packageName?.toString() ?: return
        if (pkg !in WATCHED_SETTINGS_PACKAGES) return
        if (event.eventType != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
            event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) return

        // Bei neuem Activity-Wechsel (STATE_CHANGED) den Throttle-Timer resetten,
        // damit der nachfolgende CONTENT_CHANGED (Seite lädt Inhalt nach) sofort
        // geprüft wird — sonst entsteht ein 400ms-Fenster in dem "Stopp erzwingen"
        // getappt werden kann bevor wir den Block auslösen.
        if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            lastSettingsCheck = 0L
        }

        handleProtectedSettingsBlock(pkg, event)
    }

    override fun onInterrupt() {
        Log.w(TAG, "service interrupted")
    }

    /**
     * Tamper-Protection: User versucht in System-Settings eine Schutz-relevante
     * Seite zu öffnen (VPN deaktivieren, App löschen, A11y für Rebreak abschalten).
     *
     * Aktiviert nur wenn der App-Lock armed UND der Schutz aktiv ist (`filter_enabled`).
     * Letzteres lässt den User nach einem legitim abgelaufenen Cooldown wieder raus.
     *
     * **Strikte Match-Regel (v2 nach Field-Bug-Reports):** Wir blocken NUR wenn die
     * aktuelle Window-Hierarchie unsere App tatsächlich im Text führt. Heißt konkret:
     *  - HIGH_CONFIDENCE_KEYWORD ("rebreak filter", "sichert den schutz", …) → sofort
     *  - ODER (dangerous activity-class wie VpnSettings/Uninstaller) UND Wort
     *    "rebreak" im Page-Text → block
     *  - Spezialfall `com.android.vpndialogs`: Dieses System-Package gibt es NUR für
     *    aktive VPN-Sessions. Da unser Schutz hier per Vorbedingung an ist (Early
     *    Exit oben), ist jeder Trennen-Dialog hier sicher unser eigener → class-match
     *    reicht.
     *
     * Die alte „2-Keyword-Cluster"-Heuristik („vpn"+„trennen", „eingabehilfe"+„apps",
     * „rebreak"+„speicher" …) wurde entfernt — sie blockte legitime Pages wie die
     * Verbindungs-Übersicht, die Einstellungen-Hauptseite und die Play-Store-„Apps
     * verwalten"-Liste (false-positive über Generika wie „Speicher", „Apps", „VPN").
     *
     * @return true wenn die Activity geblockt wurde
     */
    private fun handleProtectedSettingsBlock(pkg: String, event: AccessibilityEvent): Boolean {
        if (!isProtectionEnabled()) return false
        if (pkg !in WATCHED_SETTINGS_PACKAGES) return false

        // Throttle: max alle 400ms eine Settings-Inspection (CONTENT_CHANGED
        // feuert sonst hunderte Mal pro Sekunde während User scrollt)
        val now = System.currentTimeMillis()
        if (now - lastSettingsCheck < 400) return false
        lastSettingsCheck = now

        // Post-Block Cooldown: nach einem Block 3s lang keinen weiteren Block
        // damit User legitim in Settings navigieren kann ohne dass alte
        // Keyword-Matches in Page-Transitions repeatedly auslösen.
        if (now - lastBlockAt < POST_BLOCK_COOLDOWN_MS) {
            Log.d(TAG, "settings-watch: cooldown active, skipping block")
            return false
        }

        val className = event.className?.toString() ?: return false

        // DEBUG: alle Settings-Activities mitloggen damit wir OEM-Variationen sehen
        Log.i(TAG, "settings-watch: $pkg / $className")

        // Window-Text einmal sammeln — wird sowohl für High-Confidence- als auch
        // Class+Rebreak-Check gebraucht. Kann null sein wenn root window flackert.
        val pageText = collectWindowText()?.lowercase().orEmpty()

        // 0) VPN-Surface-Sperre NUR auf klaren Activity-Klassen — keine
        // Text-Heuristik mehr, damit die Settings-Suche und generelle Settings-
        // Navigation nicht fälschlich gesperrt werden.
        if (isVpnSurface(className) && shouldBlockVpnSurface(pkg, pageText)) {
            return doBlock(pkg, className, "vpn-surface", now)
        }

        // 1) High-confidence Keyword im Text → sofortiger Block (Rebreak-spezifisch)
        val highConfHit = HIGH_CONFIDENCE_KEYWORDS.firstOrNull { pageText.contains(it) }
        if (highConfHit != null) {
            return doBlock(pkg, className, "high-confidence:$highConfHit", now)
        }

        // 1b) Samsung/OEM-Fallback: manche OneUI-Updates ändern Klassenamen,
        // behalten aber den sichtbaren Screen-Text (Rebreak + konkrete Aktion).
        // Strikt gehalten, damit keine globalen Settings-False-Positives entstehen.
        val actionTextMatch = hasRebreakScopedDangerAction(pkg, className, pageText)
        if (actionTextMatch != null) {
            return doBlock(pkg, className, "rebreak-action:$actionTextMatch", now)
        }

        // 2) Activity-Class-Match — aber NUR blocken wenn Page klar über Rebreak
        //    ist (Wort "rebreak" im Text). Sonst würde z.B. die App-Info-Page einer
        //    beliebigen anderen App geblockt werden.
        val classMatchDangerous = DANGEROUS_ACTIVITY_PATTERNS.any { pattern ->
            className.contains(pattern, ignoreCase = true)
        }
        if (classMatchDangerous) {
            // Spezialfall: vpndialogs gehört System-seitig nur zur aktuell aktiven
            // VPN-Session. Da hier per Vorbedingung unser Schutz an ist, ist der
            // Dialog garantiert über uns — auch wenn der Profil-Name noch nicht
            // in den Knoten gerendert wurde.
            if (pkg == "com.android.vpndialogs") {
                return doBlock(pkg, className, "vpn-dialog", now)
            }
            if (pageText.contains("rebreak")) {
                return doBlock(pkg, className, "class+rebreak", now)
            }
            Log.d(TAG, "settings-watch: dangerous class $className but no 'rebreak' in text → skip")
        }

        return false
    }

    private fun shouldBlockVpnSurface(pkg: String, pageText: String): Boolean {
        // System VPN-Dialog ist immer ein echter Manipulationspfad.
        if (pkg == "com.android.vpndialogs") return true
        // Für generische VPN-Seiten nur blocken, wenn Rebreak klar sichtbar ist
        // und Always-on-Kontext vorhanden ist. So bleibt der allgemeine VPN-Menu-
        // Überblick navigierbar.
        if (pageText.isBlank()) return false
        val hasRebreak = pageText.contains("rebreak") || pageText.contains("re break")
        if (!hasRebreak) return false
        return STRICT_VPN_LOCK_CONTEXT_KEYWORDS.any { pageText.contains(it) }
    }

    /**
     * Enge Text-Heuristik für OEM-Varianten:
     * - Seite muss klar zu Rebreak gehören (außer Force-Stop-Dialog — der nennt
     *   die App nie, aber ist eindeutig durch seinen spezifischen Text erkennbar)
     * - und eine konkrete gefährliche Aktion zeigen
     */
    private fun hasRebreakScopedDangerAction(pkg: String, className: String, pageText: String): String? {
        if (pageText.isBlank()) return null

        // ⚠️ Force-Stop wird NICHT mehr per a11y geblockt: der OS-Geräteadmin graut
        // "Stopp erzwingen" für einen aktiven Device-Admin ohnehin aus (genau wie den
        // Uninstall-Button) → für ReBreak gar nicht tippbar. Die alte Regel matchte den
        // generischen Dialog-Text ("stopp erzwingen"+"abbrechen"+"ok") OHNE "rebreak"-
        // Guard → hätte auch das Force-Stop ANDERER Apps geblockt = unerwünschte
        // Einschränkung. Deny-Removal + Force-Stop sind Admin-only.

        val hasRebreak = pageText.contains("rebreak") || pageText.contains("re break")
        if (!hasRebreak) return null

        val isGenericSettingsPkg = pkg == "com.android.settings" || pkg == "com.samsung.android.app.settings"

        // VPN-Surface: nur wenn "Always-on"-Kontext sichtbar.
        if (isGenericSettingsPkg && STRICT_VPN_LOCK_CONTEXT_KEYWORDS.any { pageText.contains(it) }) {
            return "vpn-surface:rebreak"
        }

        // VPN-Trennen-Bestätigung: tippt der User die VPN-ZEILE (statt Zahnrad), kommt
        // ein AlertDialog "ReBreak" / "Das Gerät wird von diesem VPN getrennt." /
        // [Abbrechen][Trennen]. Der hängt im SubSettings-Window → keine VPN-Surface-
        // Klasse, kein Always-on-Text → fiel bisher durch. "rebreak" (oben geprüft) +
        // "trennen" + "vpn" ist eindeutig: die VPN-LISTE zeigt nur "Verbunden", nie
        // "Trennen" → list-safe. Fängt zusätzlich die Zahnrad-Detailseite ab.
        if (isGenericSettingsPkg &&
            pageText.contains("trennen") &&
            pageText.contains("vpn")) {
            return "vpn-disconnect:rebreak"
        }

        // ⚠️ Deny-Removal (Uninstall) ist BEWUSST KEIN a11y-Fall mehr: das übernimmt der
        // Geräteadmin. Der OS-Geräteadmin deaktiviert den Deinstallieren-Button; der
        // einzige Bypass — Admin deaktivieren — läuft über DeviceAdminAdd, und DIESE
        // Admin-Menü-Seite ist weiterhin a11y-geschützt (class+rebreak / High-Confidence
        // "rebreak administrator"). So bleibt der allgemeine App-Lösch-Flow für ANDERE
        // Apps völlig unangetastet (User fühlt sich nicht eingeschränkt), ohne Bypass.
        // Force-Stop bleibt über den Dialog-Spezialfall ganz oben geschützt.

        // VPN-/A11y-Actions: weiterhin classGuard erforderlich (diese Keywords
        // kommen auch auf harmlosen Settings-Seiten vor).
        if (isGenericSettingsPkg) {
            val classGuard = FALLBACK_CLASS_GUARD_PATTERNS.any {
                className.contains(it, ignoreCase = true)
            }
            if (!classGuard) return null
        }

        val vpnAction = DANGER_ACTION_KEYWORDS_VPN.firstOrNull { pageText.contains(it) }
        if (vpnAction != null && DANGER_CONTEXT_KEYWORDS_VPN.any { pageText.contains(it) }) {
            return "vpn:$vpnAction"
        }

        val a11yAction = DANGER_ACTION_KEYWORDS_A11Y.firstOrNull { pageText.contains(it) }
        if (a11yAction != null && DANGER_CONTEXT_KEYWORDS_A11Y.any { pageText.contains(it) }) {
            return "a11y:$a11yAction"
        }

        return null
    }

    private fun isVpnSurface(className: String): Boolean {
        return VPN_SURFACE_ACTIVITY_PATTERNS.any {
            className.contains(it, ignoreCase = true)
        }
    }

    /** Hilft Toast+Back-Action wiederzuverwenden (DRY für die beiden Block-Pfade). */
    private fun doBlock(pkg: String, className: String, reason: String, now: Long): Boolean {
        Log.w(TAG, "TAMPER-BLOCK: $pkg / $className (reason=$reason)")
        lastBlockAt = now  // post-block cooldown startet jetzt
        // Doppel-BACK: einmal um Activity zu schließen, einmal als Backup falls
        // erste BACK nur einen Dialog dismissed.
        performGlobalAction(GLOBAL_ACTION_BACK)
        mainHandler.postDelayed({
            performGlobalAction(GLOBAL_ACTION_BACK)
        }, 200)

        // Toast in Lyra's Stimme statt eines kühlen Shield-Icons (Android 11+
        // ignoriert setView/Custom-Layouts für App-Toasts → kein echtes Avatar-
        // Bild möglich; deshalb signiert der Text mit „Lyra:"). lyra-persona darf
        // den Wortlaut gern noch feinschleifen.
        mainHandler.post {
            Toast.makeText(
                applicationContext,
                "Lyra: Das ist während deines Schutzes gesperrt 💛 Wenn du wirklich raus willst, geht das in der App.",
                Toast.LENGTH_LONG,
            ).show()
        }
        return true
    }

    /**
     * Sammelt den gesamten sichtbaren Text der aktuellen Window-Hierarchie
     * (Text + ContentDescription, bis Tiefe 10). Lowercased-Vereinigung wird
     * vom Caller gegen Keywords / "rebreak" gematcht. Returnt null wenn keine
     * Root-Window verfügbar (Page transitioning).
     */
    private fun collectWindowText(): String? {
        val root = rootInActiveWindow ?: return null
        val texts = mutableListOf<String>()
        collectAllText(root, texts, depth = 0)
        if (texts.isEmpty()) return null
        val joined = texts.joinToString(" | ")
        Log.d(TAG, "settings-content-text: ${joined.take(500)}")
        return joined
    }

    private fun collectAllText(node: AccessibilityNodeInfo?, sink: MutableList<String>, depth: Int) {
        if (node == null || depth > 10) return
        node.text?.toString()?.takeIf { it.isNotBlank() }?.let { sink.add(it) }
        node.contentDescription?.toString()?.takeIf { it.isNotBlank() }?.let { sink.add(it) }
        for (i in 0 until node.childCount) {
            collectAllText(node.getChild(i), sink, depth + 1)
        }
    }

    /**
     * Tamper nur wenn Schutz WIRKLICH live ist:
     * - `filter_enabled` muss true sein (User-Wunsch)
     * - UND VpnService muss tatsächlich laufen (`isRunning`)
     *
     * So verhindern wir, dass A11y-Blockaden aktiv sind während der VPN-Layer
     * nach Reboot/Crash noch nicht wieder hochgefahren ist.
     */
    private fun isProtectionEnabled(): Boolean {
        return try {
            val prefs = applicationContext.getSharedPreferences(
                "rebreak_filter_prefs",
                android.content.Context.MODE_PRIVATE,
            )
            prefs.getBoolean("filter_enabled", false) && RebreakVpnService.isRunning
        } catch (_: Exception) {
            false
        }
    }

    /** "Verriegelt"-Flag — User hat über App-Button „App-Lock" bestätigt dass
     *  Settings-Tampering blockiert werden soll. Default: false damit Onboarding
     *  nicht aussperrt; nach Cooldown-Ende disarmed `disarmTamperLock` ihn wieder. */
    private fun isTamperLockArmed(): Boolean {
        return try {
            val prefs = applicationContext.getSharedPreferences(
                "rebreak_filter_prefs",
                android.content.Context.MODE_PRIVATE,
            )
            prefs.getBoolean("tamper_armed", false)
        } catch (_: Exception) {
            false
        }
    }

    companion object {
        private const val TAG = "RebreakA11y"

        @Volatile
        private var instance: RebreakAccessibilityService? = null

        fun requestPowerDialog(): Boolean {
            val svc = instance ?: return false
            return try {
                svc.performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)
            } catch (_: Exception) {
                false
            }
        }

        // Settings-Apps die wir auf Tamper-Versuche überwachen.
        // Stock + Samsung One UI haben unterschiedliche Package-Namen,
        // wir whitelist'n alle bekannten.
        val WATCHED_SETTINGS_PACKAGES = setOf(
            "com.android.settings",
            "com.android.vpndialogs",
            "com.android.permissioncontroller",
            "com.google.android.permissioncontroller",
            "com.samsung.android.app.settings",
            "com.samsung.accessibility",
            // ⚠️ Package-Installer + Play Store (com.*.packageinstaller / com.android.vending)
            // sind BEWUSST RAUS: Deny-Removal ist Admin-only, der Uninstall-Flow wird gar
            // nicht mehr überwacht → andere Apps löschen bleibt völlig frei.
        )

        /**
         * High-confidence Keywords — wenn EINER davon im Window-Content auftaucht,
         * blocken wir sofort (kein Class-Guard nötig). Diese Texte erscheinen
         * AUSSCHLIESSLICH auf echten Abschalt-/Deinstallier-Bestätigungen, NIE in
         * einer Liste — deshalb safe ohne Listen-Over-Block.
         *
         * Müssen lowercase sein (Text wird vor Match lowercased).
         */
        val HIGH_CONFIDENCE_KEYWORDS = listOf(
            // ⚠️ NUR Aktions-Bestätigungen — Texte die AUSSCHLIESSLICH auf einem echten
            // Gefahr-Dialog erscheinen, NIE in einer Liste. Die bare Service-Summary
            // ("rebreak schutz") wurde RAUSGENOMMEN: sie steht auch in der a11y-Dienste-
            // LISTE (neben fremden Diensten) → blockte die ganze Liste = andere Apps
            // benachteiligt = Play-Reject-Risiko. a11y-Schutz greift jetzt erst bei der
            // Abschalt-Bestätigung "ReBreak Schutz ausschalten?".
            // a11y-Abschalt-Bestätigung:
            "rebreak schutz ausschalten",
            "rebreak — schutz ausschalten",
            // Geräteadmin-Menü (= einziger Uninstall-Bypass; Deny-Removal selbst macht
            // der OS-Geräteadmin). Uninstall-Dialog-Keywords ("rebreak deinstallieren"
            // etc.) sind BEWUSST RAUS: Deny-Removal ist Admin-only, der allgemeine
            // App-Lösch-Flow bleibt für andere Apps unangetastet.
            "rebreak administrator",
            "rebreak geräteadministrator",
            "rebreak device administrator",
        )

        val DANGEROUS_ACTIVITY_PATTERNS = listOf(
            // VPN-Settings + VPN-Profil-Dialoge
            "VpnSettings",
            "VpnConfig",
            "VpnDialog",
            "ManageDialog",         // com.android.vpndialogs.ManageDialog
            "ConfirmAddOns",        // com.android.vpndialogs.ConfirmAddOnsActivity

            // ⚠️ App-Info-Page (InstalledAppDetails / ApplicationDetail) UND der
            // Uninstall-Dialog (UninstallerActivity) sind BEWUSST RAUS: Deny-Removal
            // macht der OS-Geräteadmin (deaktiviert den Deinstallieren-Button), der
            // allgemeine App-Lösch-Flow bleibt für andere Apps frei. Force-Stop greift
            // über den Dialog-Spezialfall in hasRebreakScopedDangerAction.

            // ⚠️ NUR die EINZEL-Geräteadmin-Seite (DeviceAdminAdd = "ReBreak deaktivieren
            // & deinstallieren"), NICHT die Admin-LISTE (DeviceAdminSettings). Die Liste
            // zeigt fremde Admins neben ReBreak → würde mit class+rebreak die ganze Liste
            // sperren = andere Apps benachteiligt = Play-Reject. Per Logcat belegt:
            // com.android.settings...deviceadmin.DeviceAdminAdd auch auf Samsung.
            "DeviceAdminAdd",

            // ⚠️ Accessibility-LISTEN-Klassen (AccessibilitySettings / InstalledService /
            // AccessibilityDetails) sind BEWUSST RAUS: sie listen fremde Dienste neben
            // ReBreak → class+rebreak sperrte die ganze a11y-Liste. Der a11y-Abschalt-
            // Schutz läuft jetzt ausschließlich über den Dialog-Text
            // ("rebreak schutz ausschalten", HIGH_CONFIDENCE_KEYWORDS) — der erscheint
            // NUR auf der Abschalt-Bestätigung, nie in einer Liste.
        )

        /** VPN-Surface-Activities die wir immer blocken solange Tamper aktiv ist. */
        val VPN_SURFACE_ACTIVITY_PATTERNS = listOf(
            "VpnSettings",
            "VpnConfig",
            "VpnDialog",
            "ManageDialog",
            "ConfirmAddOns",
            "AlwaysOnVpn",
        )

        // Text-Fallback (strictly scoped): nur mit sichtbarem Rebreak-Bezug.
        // Alles lowercase halten.
        val DANGER_ACTION_KEYWORDS_VPN = listOf(
            "trennen",
            "disconnect",
            "deaktivieren",
            "disable",
            "ausschalten",
            "turn off",
            "stoppen",
            "stop",
            "entfernen",
            "remove",
            "löschen",
            "delete",
        )

        val DANGER_CONTEXT_KEYWORDS_VPN = listOf(
            "vpn",
            "always-on",
            "immer eingeschaltet",
            "immer aktiviert",
            "immer ein",
            "verbunden",
            "connected",
            "vpn-profil",
            "vpn profile",
        )

        val STRICT_VPN_LOCK_CONTEXT_KEYWORDS = listOf(
            "immer ein",
            "always-on",
            "always on",
            "immer eingeschaltet",
            "immer aktiviert",
        )

        // (DANGER_ACTION_KEYWORDS_UNINSTALL entfernt — Deny-Removal ist jetzt Admin-only,
        //  kein a11y-Uninstall-Block mehr. Force-Stop läuft über den Dialog-Spezialfall.)

        val DANGER_ACTION_KEYWORDS_A11Y = listOf(
            "deaktivieren",
            "disable",
            "ausschalten",
            "turn off",
            "stoppen",
            "stop",
        )

        val DANGER_CONTEXT_KEYWORDS_A11Y = listOf(
            "eingabehilfe",
            "barrierefreiheit",
            "accessibility",
            "installierte dienste",
            "installed services",
            "dienste",
            "services",
        )

        val FALLBACK_CLASS_GUARD_PATTERNS = listOf(
            "Vpn",
            "Accessibility",
            "Uninstall",
            "InstalledAppDetails",
            "ApplicationDetails",
            "ApplicationDetail",    // Samsung OneUI (kein 's')
            "Package",
            "Permission",
            "InstalledService",
            "Manage",
            "Details",
            "Detail",               // Samsung fallback (kein 's')
        )

    }
}
