package com.dtourolle.jellytau

import android.app.Activity
import android.webkit.JavascriptInterface
import android.webkit.WebView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

/**
 * Publishes the Activity's real window insets to the WebView as CSS custom
 * properties.
 *
 * TRACES: UR-066 | IR-031, DR-112
 *
 * ## Why this is necessary
 *
 * MainActivity calls `enableEdgeToEdge()`, and the app targets SDK 36 —
 * edge-to-edge is mandatory from SDK 35 and the opt-out is ignored from SDK 36
 * — so the Tauri WebView always spans the whole window, underneath the status
 * bar, the navigation/gesture bar and the display cutout.
 *
 * The web layer cannot discover that by itself. Android WebView maps only the
 * **display cutout** into `env(safe-area-inset-*)` (and only with
 * `viewport-fit=cover`); the status bar and navigation bar are never reported.
 * Unlike iOS Safari there is no CSS-visible system-bar inset. So the frontend's
 * `env()`-based padding evaluated to 0 on every device and the bottom nav
 * rendered underneath the navigation bar.
 *
 * How badly that showed depended entirely on the device's navigation mode: a
 * thin translucent gesture pill overlaps almost harmlessly, while a tall opaque
 * 3-button bar swallows the nav outright.
 *
 * ## Contract with the frontend
 *
 * Insets are reported in **CSS pixels** (density-independent), because that is
 * the unit CSS will use them in — dividing by `displayMetrics.density` here is
 * what keeps the padding correct across screen densities.
 *
 * - **Push**: on every inset change (rotation, navigation-mode switch, PiP
 *   enter/exit) the four `--jt-inset-*` custom properties are written onto
 *   `document.documentElement` and `jellytau-insets-changed` is dispatched.
 * - **Pull**: `AndroidInsets.get()` returns the same payload as JSON. Required
 *   because the first inset pass normally lands before the SvelteKit document
 *   exists, and a page load discards any inline style a push had set.
 *
 * See `src/lib/utils/safeArea.ts` and the `--safe-*` vars in `src/app.css`.
 */
object WindowInsetsBridge {

    /** Latest insets in CSS pixels. Written on the main thread, read from the WebView binder thread. */
    @Volatile
    private var top = 0
    @Volatile
    private var right = 0
    @Volatile
    private var bottom = 0
    @Volatile
    private var left = 0

    /** Cached so a WebView found later (or re-found on resume) can be primed. */
    private var webView: WebView? = null

    /**
     * Start listening for window insets on [activity].
     *
     * Call from `onCreate` right after `enableEdgeToEdge()`. The listener
     * returns the insets **unconsumed** so the WebView still receives them for
     * its own display-cutout handling.
     */
    fun install(activity: Activity) {
        val density = activity.resources.displayMetrics.density

        ViewCompat.setOnApplyWindowInsetsListener(activity.window.decorView) { _, insets ->
            // systemBars() covers the status bar and the navigation/gesture bar;
            // displayCutout() covers notches and punch-holes, which in landscape
            // land on a side edge that systemBars() does not describe.
            val i = insets.getInsets(
                WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
            )

            val toCssPx = { px: Int -> if (density > 0f) Math.round(px / density) else px }
            top = toCssPx(i.top)
            right = toCssPx(i.right)
            bottom = toCssPx(i.bottom)
            left = toCssPx(i.left)

            android.util.Log.d(
                "WindowInsetsBridge",
                "insets (css px): top=$top right=$right bottom=$bottom left=$left"
            )
            push()

            // Do NOT return CONSUMED - other views (and the WebView's own cutout
            // handling) still need to see these.
            insets
        }

        // The first pass may already have happened before the listener existed.
        ViewCompat.requestApplyInsets(activity.window.decorView)
    }

    /**
     * Adopt the WebView carrying the UI and push the current insets into it.
     *
     * Safe to call repeatedly (MainActivity re-finds the WebView on every
     * resume): this only writes CSS properties, unlike `addJavascriptInterface`,
     * which must run exactly once per WebView.
     */
    fun attachWebView(view: WebView) {
        webView = view
        push()
    }

    /** Current insets as JSON in CSS pixels — the payload `AndroidInsets.get()` returns. */
    fun currentJson(): String =
        """{"top":$top,"right":$right,"bottom":$bottom,"left":$left}"""

    /** The `AndroidInsets` @JavascriptInterface object for the pull path. */
    fun jsInterface(): Any = object : Any() {
        @JavascriptInterface
        fun get(): String = currentJson()
    }

    /** Write the custom properties into the live document and signal the change. */
    private fun push() {
        val view = webView ?: return
        val js = """
            (function() {
              var s = document.documentElement.style;
              s.setProperty('--jt-inset-top', '${top}px');
              s.setProperty('--jt-inset-right', '${right}px');
              s.setProperty('--jt-inset-bottom', '${bottom}px');
              s.setProperty('--jt-inset-left', '${left}px');
              window.dispatchEvent(new CustomEvent('jellytau-insets-changed'));
            })();
        """.trimIndent()

        view.post { view.evaluateJavascript(js, null) }
    }
}
