import Foundation
import React
import WebKit
import NoibuSessionReplay

/// RN native module over the NoibuSessionReplay Swift package (kmp/ios) — fully-native capture
/// (replay, taps, keyboard, HTTP via NoibuURLProtocol, crashes), the iOS counterpart of the
/// Android bridge. Legacy-module shape on purpose: RN's interop layer bridges it on the new
/// architecture too.
@objc(NoibuReactNative)
class NoibuReactNative: NSObject {

    /// Injected by RN for RCTBridgeModule — used to resolve react view tags (webview tracking).
    @objc var bridge: RCTBridge!

    @objc static func requiresMainQueueSetup() -> Bool { false }

    @objc(initialize:resolver:rejecter:)
    func initialize(
        _ config: NSDictionary,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        guard let domain = config["domain"] as? String, !domain.isEmpty else {
            reject("E_INIT", "NoibuConfig.domain is required", nil)
            return
        }
        let logLevel = (config["logLevel"] as? String).flatMap { NoibuLogLevel(rawValue: $0.uppercased()) }
        // Fields the native NoibuConfig supports and the JS config surface shares (parity with
        // Android's ConfigMapper); unset keys keep the native defaults.
        let autoTrackNavigation = (config["autoTrackNavigation"] as? NSNumber)?.boolValue ?? false
        let ingestHost = config["ingestHost"] as? String
        DispatchQueue.main.async {
            Noibu.shared.initialize(configuration: NoibuConfig(
                domain: domain,
                logLevel: logLevel,
                autoTrackNavigation: autoTrackNavigation,
                ingestHost: ingestHost
            ))
            resolve(true)
        }
    }

    /// NoibuWebView (JS): resolve the react view for `viewTag` and enable Noibu tracking on the
    /// WKWebView inside it. The JS wrapper withholds `source` until this resolves, so the injected
    /// recorder is in place before the first document loads — same contract as Android and the
    /// native `NoibuWebViewTracking.enable`.
    @objc(enableWebViewTracking:resolver:rejecter:)
    func enableWebViewTracking(
        _ viewTag: NSNumber,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        // uiManager resolves the tag on the main thread with the view registry. The view mounts a
        // frame after the JS effect fires, so retry briefly before failing (mirrors Android).
        var attempts = 0
        func attempt() {
            bridge.uiManager.addUIBlock { _, viewRegistry in
                let view = viewRegistry?[viewTag]
                if let webView = view.flatMap(Self.findWebView) {
                    NoibuWebViewTracking.enable(webView)
                    resolve(true)
                } else if attempts < 20 {
                    attempts += 1
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { attempt() }
                } else {
                    reject("E_WEBVIEW", "no WKWebView under react view \(viewTag)", nil)
                }
            }
        }
        attempt()
    }

    private static func findWebView(_ view: UIView) -> WKWebView? {
        if let webView = view as? WKWebView { return webView }
        for subview in view.subviews {
            if let found = findWebView(subview) { return found }
        }
        return nil
    }

    @objc(trackNavigation:)
    func trackNavigation(_ name: String?) {
        Noibu.shared.didNavigate(pageName: name)
    }

    @objc(addCustomAttribute:value:)
    func addCustomAttribute(_ name: String, value: String) {
        Noibu.shared.addCustomAttribute(name: name, value: value)
    }

    @objc(addError:stack:)
    func addError(_ message: String, stack: String?) {
        Noibu.shared.addError(message: message, stack: stack)
    }

    @objc(recordNetwork:)
    func recordNetwork(_ event: NSDictionary) {
        // No-op on iOS: NoibuURLProtocol already captures HTTP natively; the JS fallback monitor
        // is an Android/other-runtime escape hatch.
    }
}
