import { NativeModule, requireNativeModule } from 'expo';

import type {
  ActivateResult,
  DeviceLayers,
  DisableResult,
  HealthProbeOpts,
  HealthProbeResult,
  RebreakProtectionEvents,
  SyncBlocklistOpts,
  SyncBlocklistResult,
  SyncWebContentDomainsOpts,
  SyncWebContentDomainsResult,
  SystemSettingsTarget,
  WebContentFilterResult,
} from './RebreakProtection.types';

declare class RebreakProtectionModule extends NativeModule<RebreakProtectionEvents> {
  /**
   * iOS: read-only check ob NEFilter aktiv ist (egal ob via App-Code-saveToPreferences
   * oder via System-installiertes webcontent-filter Profile). Wenn `enabled=true`
   * → ContentFilter-Extension läuft → UI zeigt all-green ohne weiteren App-Action.
   *
   * Wenn `localizedDescription` "ReBreak" enthält → unser Filter.
   * Build 19 (2026-05-26): App aktiviert NEFilter NICHT mehr selbst — Profile-Pfad
   * (Sideload-non-removable) ist primary. App rolllt nur State um UI zu rendern.
   */
  isNeFilterActive(): Promise<{
    enabled: boolean;
    localizedDescription?: string;
    error?: string;
  }>;

  /**
   * iOS: probiert ein NEFilterDataProvider-Setup ohne persistent zu sein.
   * Wenn `enabled=true` zurückkommt → Device kann NEFilter (= ist MDM-managed).
   * Wenn `enabled=false` → iOS hat den Filter silent gecuttet → Device ist
   * nicht-MDM, App muss VPN-Pfad nutzen.
   *
   * Aufgerufen vom Settings-"Auto-Detect"-Button. Setzt KEIN AsyncStorage-Flag
   * — das macht die JS-Schicht. Bei `enabled=false` räumt die Function die
   * halbe NEFilter-Config wieder weg (removeFromPreferences).
   *
   * Auf Android: ignoriert (Web-Stub returns enabled=false).
   */
  probeContentFilter(): Promise<{ enabled: boolean; error?: string }>;

  /**
   * iOS: aktiviert Layer 1. Branch in Swift via `opts.supervised`:
   *  - true: klassisches NEFilterDataProvider (kein VPN-Toggle in Settings,
   *    kein User-Bypass). Nur auf MDM-managed Devices funktional.
   *  - false: NEPacketTunnelProvider DNS-Sinkhole (heute) — VPN-Eintrag in
   *    Settings sichtbar, MDM-frei, ab iOS 16
   *
   * Flag kommt aus AsyncStorage `protection:device-mdm-supervised`, JS-side
   * gemanaged. Auto-Detect-Button in Settings setzt's basierend auf
   * `probeContentFilter`.
   *
   * PIR-Felder sind Legacy — werden für API-Kompat in der Signatur gelassen.
   */
  activateUrlFilter(opts: {
    pirServerURL: string;
    pirAuthToken: string;
    supervised: boolean;
  }): Promise<{ enabled: boolean; error?: string }>;

  /**
   * iOS: aktiviert den NEURLFilter (iOS 26) via `NEURLFilterManager` mit dem
   * PIR-Server. INAKTIV — NICHT der Default-Filter. Behalten als optionaler
   * iOS-26-Upgrade-Pfad, falls Apple den NEURLFilter-DTS-Bug fixt. Der
   * Default-Layer-1 ist der Packet-Tunnel (`activateUrlFilter`).
   * Braucht `pirServerURL` + `pirAuthToken`. iOS 26+.
   */
  activateNeUrlFilter(opts: {
    pirServerURL: string;
    pirAuthToken: string;
  }): Promise<{ enabled: boolean; error?: string }>;

  /**
   * iOS: nach "Nicht erlauben" beim NEFilter-Permission-Dialog hat iOS den
   * Denied-State gecached und zeigt beim erneuten activateUrlFilter() den
   * Dialog nicht mehr (code 5 silent). resetUrlFilter() macht ein
   * removeFromPreferences vor dem saveToPreferences — iOS behandelt das als
   * brandneuen Permission-Request → frischer System-Dialog.
   *
   * Nicht aufrufen wenn der User schon einmal "Erlauben" getippt hat — dann
   * würde ein unnötiger Dialog kommen. Nur als Workaround bei code 5 nutzen.
   */
  resetUrlFilter(): Promise<{ enabled: boolean; error?: string }>;

  /**
   * iOS: prüft ob unser NETunnelProviderManager noch in loadAllFromPreferences
   * vorhanden ist; falls nicht (User hat „VPN löschen" in Settings getippt)
   * silent recreate via loadOrCreateTunnelManager + saveToPreferences +
   * startVPNTunnel. Bei jedem Foreground-/Polling-Tick durch
   * `protection.reconcileVpn()` aufgerufen.
   *
   * Wenn iOS wegen frischem Manager den Permission-Dialog zeigt: akzeptierte
   * Friktion. Idempotent: wenn Tunnel noch da ist → no-op + recreated=false.
   */
  reconcileUrlFilter(): Promise<{
    recreated: boolean;
    status?: string;
    error?: string;
  }>;

  /**
   * iOS: aktiviert NUR Family Controls (Auth + denyAppRemoval = der Lock).
   * Triggert iOS-Dialog "Bildschirmzeit verwalten".
   * Sobald aktiv, kann der User den Schutz nur über Cooldown deaktivieren.
   */
  activateFamilyControls(): Promise<{ enabled: boolean; error?: string }>;

  /**
   * Aktiviert ALLE Schutz-Layer in einem Call (legacy, beide Dialoge nacheinander).
   * Bevorzugt activateUrlFilter() + activateFamilyControls() einzeln aufrufen.
   */
  activate(): Promise<ActivateResult>;

  /**
   * Schaltet ALLE Schutz-Layer ab. NUR aufrufen wenn JS-Layer verifiziert
   * hat dass der 24h-Cooldown abgelaufen ist. Native-Modul prüft das nicht
   * — der Backend-Cooldown ist Single Source of Truth, das ist Aufgabe der
   * JS-Schicht.
   */
  disable(): Promise<DisableResult>;

  /**
   * iOS Layer 2 — webContent-Filter (ManagedSettings). Bestimmt das Land via
   * Locale.current.region, lädt die gebündelte Top-Gambling-Domain-Liste für
   * dieses Land (≤50 Domains, Apple-Hartlimit) und setzt
   * `ManagedSettingsStore().webContent.blockedByFilter = .auto(...)` — blockt
   * die Domains in WebKit (Safari u.a.) plus systemseitig Adult-Content.
   *
   * Setzt eine gültige Family-Controls-Authorization voraus (wie der App-Lock).
   * Auf Android/iOS<16 no-op. Stilles Sicherheitsnetz; KEINE Auto-Trigger-Logik
   * — muss explizit aufgerufen werden (siehe TODO(layer2-gating) im Swift-Modul).
   */
  applyWebContentFilter(): Promise<WebContentFilterResult>;

  /**
   * iOS Layer 2 — setzt den webContent-Filter zurück (blockedByFilter = .none).
   * Rührt denyAppRemoval (App-Lock) NICHT an. Auf Android/iOS<16 no-op.
   */
  clearWebContentFilter(): Promise<{ cleared: boolean; error?: string }>;

  /**
   * iOS Layer 2 — synct die kuratierte Gambling-Domain-Liste vom Backend
   * (`GET /api/protection/webcontent-domains`) und cached sie als
   * `webcontent-domains.json` im App-Group-Container. `loadWebContentDomains`
   * liest danach cache-first; die gebündelte `gambling-domains.json` bleibt
   * nur noch Offline-Seed/Fallback.
   *
   * Gespiegelt von `syncBlocklist`: baseURL + authToken aus der Supabase-
   * Session, Bearer-Auth, ETag/If-None-Match, Retry mit Backoff. Nach
   * erfolgreichem Sync wird — wenn Family Controls authorisiert ist —
   * `applyWebContentLayer()` erneut ausgeführt, damit die neue Liste sofort
   * greift. Server respondet 304 wenn ETag matched → updated=false.
   */
  syncWebContentDomains(
    opts: SyncWebContentDomainsOpts,
  ): Promise<SyncWebContentDomainsResult>;

  /** Aktueller Device-State. Polling- und Health-Check-Pfad. */
  getDeviceState(): Promise<DeviceLayers>;

  /**
   * Lädt blocklist.bin vom Server, schreibt atomisch in App-Group/internal
   * storage, postet Reload-Notification an die Filter-Extension. Server
   * respondet 304 wenn ETag matched → updated=false. Plan-aware:
   * Free → nur personal-domains (≤5), Pro/Legend → 208k+ + personal.
   */
  syncBlocklist(opts: SyncBlocklistOpts): Promise<SyncBlocklistResult>;

  /**
   * E2E-Verifikation: Hidden WebView lädt eine bekannte Gambling-Domain,
   * prüft ob WebKit/Browser den Load aborted (Filter funktioniert) oder die
   * Page lädt (Filter ist tot — Alarm).
   */
  runHealthProbe(opts?: HealthProbeOpts): Promise<HealthProbeResult>;

  /** Öffnet System-Settings auf dem entsprechenden Tab. */
  openSystemSettings(target?: SystemSettingsTarget): Promise<void>;

  /**
   * iOS: liest die nativen Protection-Logs (SharedLogStore — NEFilter/
   * FamilyControls Flow) aus dem App-Group-UserDefaults. Für Debug-Page,
   * damit TestFlight-Tester den nativen Flow ohne Mac/Console.app sehen.
   */
  getProtectionLogs(): Promise<string[]>;

  /** iOS: leert die nativen Protection-Logs. */
  clearProtectionLogs(): Promise<void>;

  // ─── Android-spezifische Methoden (auf iOS undefined zur Laufzeit) ───────

  /** Android: Live-Check ob unser AccessibilityService aktuell als enabled
   *  registriert ist (Settings.Secure + AccessibilityManager). */
  isAccessibilityEnabled(): Promise<{ enabled: boolean }>;

  /** Android: Öffnet Settings → Bedienungshilfen, möglichst tief auf die
   *  Rebreak-Detail-Page (deep-link). Fallback: generelle A11y-Liste. Bei
   *  Fallback startet automatisch ein Repeating-Toast-Hint („Tippe …") der
   *  alle 3s wiederholt wird bis a11y aktiviert oder ~30s vergangen. */
  openAccessibilitySettings(): Promise<{ opened: boolean; via?: string }>;

  /** Android: Hat die App „Nutzungszugriff" (PACKAGE_USAGE_STATS)? Damit erkennt
   *  der State-aware a11y-Setup-Guide den aktuellen Settings-Screen. */
  hasUsageAccess(): Promise<{ granted: boolean }>;

  /** Android: Öffnet die „Nutzungsdaten-Zugriff"-Settings zum Freigeben. */
  openUsageAccessSettings(): Promise<{ opened: boolean }>;

  /** Android: Ist die App von der Akku-Optimierung ausgenommen? Ohne Ausnahme
   *  schläfert Samsung & Co. die App ein → a11y-Service entbunden → Schutz weg. */
  isBatteryOptimizationIgnored(): Promise<{ ignored: boolean }>;

  /** Android: System-Dialog „Akku-Optimierung ignorieren?" (ein Tap „Zulassen").
   *  alreadyIgnored=true wenn bereits ausgenommen; fallback=true wenn nur die
   *  generische Liste geöffnet werden konnte. */
  requestIgnoreBatteryOptimizations(): Promise<{
    opened: boolean;
    alreadyIgnored?: boolean;
    fallback?: boolean;
    error?: string;
  }>;

  /** Android: Öffnet die App-Detail-Settings — für Samsung der Weg zu
   *  „Akku → Uneingeschränkt" + raus aus „Schlafende/Tief schlafende Apps". */
  openAppDetailsSettings(): Promise<{ opened: boolean; error?: string }>;

  /** Android: Hat die App „Über anderen Apps anzeigen" (für das passive Guide-Overlay)? */
  hasOverlayPermission(): Promise<{ granted: boolean }>;

  /** Android: Öffnet die „Über anderen Apps anzeigen"-Settings zum Freigeben. */
  openOverlayPermissionSettings(): Promise<{ opened: boolean }>;

  /** Android: Stoppt den Repeating-Toast-Hint manuell. JS sollte das bei
   *  AppState → 'active' aufrufen, damit nicht weiter Toasts über die App
   *  hereinflattern wenn User zurückgekommt ist. */
  dismissAccessibilityHint(): Promise<void>;

  /** Android: Öffnet den System-Power-Dialog über den aktiven A11y-Service.
   *  Liefert `opened=false`, wenn der Service nicht aktiv ist oder OEM blockt. */
  openPowerDialog(): Promise<{ opened: boolean }>;

  /** Android: Aktiviert Tamper-Lock-Watchdog (Settings-Page-Blockade durch
   *  AccessibilityService). Wirft `preconditions_not_met` wenn VPN oder A11y
   *  nicht beide live. */
  armTamperLock(): Promise<{ armed: boolean }>;

  /** Android: Disarm Tamper-Lock. Schutz-Layers laufen weiter, aber Settings-
   *  Watchdog blockt nicht mehr. Im normalen Flow nur nach Cooldown-Ablauf. */
  disarmTamperLock(): Promise<{ armed: boolean }>;

  /** Android: kombinierter Status aller 3 Layers + Blocklist-Count. */
  getProtectionStatus(): Promise<{
    vpnEnabled: boolean;
    accessibilityEnabled: boolean;
    blocklistCount: number;
    tamperArmed: boolean;
  }>;

  /** Android: Wenn der Schutz an sein soll (`filter_enabled`) der VpnService
   *  aber nicht läuft (Reinstall / Low-Mem-Kill) → neu starten. Bei App-Start /
   *  Foreground aufrufen, damit der State nicht „an aber tot" bleibt. */
  reconcileVpn(): Promise<{ restarted: boolean; needsConsent?: boolean }>;

  /**
   * Android: loest den System-Dialog aus der den User fragt ob er ReBreak
   * als Geraeteadministrator aktivieren will. Dialog laeuft asynchron.
   * Ergebnis via AppState-Return + isDeviceAdminActive pruefen.
   */
  requestDeviceAdmin(): Promise<{ launched: boolean }>;

  /** Android: prueft ob der DeviceAdminReceiver aktuell aktiver Admin ist. */
  isDeviceAdminActive(): Promise<{ active: boolean }>;

  /**
   * Android: entfernt den Device-Admin-Receiver. MUSS im Cooldown-Resolve-Pfad
   * aufgerufen werden, damit der User nach Cooldown-Ablauf die App deinstallieren
   * kann. Safety-Auflage: legitimer Ausstieg MUSS immer moeglich sein.
   */
  removeDeviceAdmin(): Promise<{ removed: boolean }>;
}

export default requireNativeModule<RebreakProtectionModule>('RebreakProtection');
