package com.fmdxconnector

import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.View
import android.webkit.JavascriptInterface
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.concurrent.atomic.AtomicReference
import java.util.Locale

// ---------------------------
// Live-Container für GPS-Daten
// ---------------------------
data class GpsData(
    val lat: String,
    val lon: String,
    val alt: String,
    val mode: String,
    val ts: Long = System.currentTimeMillis()
)

object GpsStore {
    private val ref = AtomicReference(GpsData("", "", "0", "2", 0L))
    private val _flow = MutableStateFlow(ref.get())
    val flow: StateFlow<GpsData> = _flow

    fun update(newData: GpsData) {
        ref.set(newData)
        _flow.value = newData
    }

    fun latest(): GpsData = ref.get()
}

// ---------------------------------
// Hilfsklasse für Headless WebView
// ---------------------------------
class GpsWebViewHelper(
    private val context: Context,
    private val onLog: (String) -> Unit = {},
    private val onGps: (lat: String, lon: String, alt: String, mode: String) -> Unit = { _, _, _, _ -> },
    private val prefsName: String = "gps_cache"
) {

    private val mainHandler = Handler(Looper.getMainLooper())
    @Volatile private var webView: WebView? = null

    // ------------- Public API -------------

    /** Startet Headless-WebView, injiziert WS-Listener und persistiert QTH/GPS in SharedPreferences. */
    fun start(serverAddress: String, watchMillis: Long = 60_000L) {
        if (webView != null) {
            logI("GPS watcher: already running, start ignored.")
            return
        }

        val baseUrl = normalizeUrl(serverAddress)
        val hostKey = normalizeHostKey(serverAddress)

        logI("GPS watcher: load $baseUrl (hostKey=$hostKey)")

        mainHandler.post {
            // Sicherheits-Stopp bevor wir neu starten
            internalStop(destroy = true)

            webView = WebView(context.applicationContext).apply {
                visibility = View.GONE
                settings.javaScriptEnabled = true
                settings.domStorageEnabled = true

                val bridge = GpsJsBridge(
                    hostKey = hostKey,
                    saveGlobal = { la, lo, al, md -> saveGpsToPrefs(la, lo, al, md) },
                    saveForHost = { la, lo, al, md -> saveGpsToPrefsForHost(hostKey, la, lo, al, md) },
                    onLog = { onLog(it) },
                    onGpsCallback = { la, lo, al, md ->
                        GpsStore.update(GpsData(la, lo, al, md))
                        onGps(la, lo, al, md)
                    },
                    stopWebView = { internalStop(true) }   // 🔥 NEU
                )

                addJavascriptInterface(bridge, "Android")

                webViewClient = object : WebViewClient() {
                    override fun onPageFinished(view: WebView, url: String) {
                        evaluateJavascript(buildInjectedJs(watchMillis), null)
                    }

                    override fun onReceivedError(
                        view: WebView,
                        request: WebResourceRequest,
                        error: WebResourceError
                    ) {
                        // logW("GPS watcher: WebView error: $error")
                    }
                }
            }

            webView?.loadUrl(baseUrl)

            // Hard cleanup nach watchMillis + 2s
            mainHandler.postDelayed({ internalStop(destroy = true) }, watchMillis + 2000L)
        }
    }

    /** Stoppt WS im Page-Kontext und zerstört die WebView. */
    fun stop() {
        mainHandler.post { internalStop(destroy = true) }
    }

    // ------------- Internals -------------

    private fun logI(msg: String) { Log.i("GpsWebViewHelper", msg); onLog(msg) }
    private fun logW(msg: String) { Log.w("GpsWebViewHelper", msg); onLog(msg) }

    private fun normalizeUrl(raw: String): String {
        val a = raw.trim()
        val base = when {
            a.startsWith("http://", true) || a.startsWith("https://", true) -> a
            a.startsWith("ws://", true)  -> "http://" + a.removePrefix("ws://")
            a.startsWith("wss://", true) -> "https://" + a.removePrefix("wss://")
            else -> "http://$a"
        }
        return if (base.endsWith("/")) base else "$base/"
    }

    private fun normalizeHostKey(raw: String): String {
        val a = raw.trim()
        val base = when {
            a.startsWith("http://", true) || a.startsWith("https://", true) -> a
            a.startsWith("ws://", true)  -> "http://" + a.removePrefix("ws://")
            a.startsWith("wss://", true) -> "https://" + a.removePrefix("wss://")
            else -> "http://$a"
        }
        return try {
            val uri = Uri.parse(base)
            val host = uri.host ?: a
            val port = if (uri.port != -1) uri.port else if (uri.scheme == "https") 443 else 80
            "$host:$port"
        } catch (_: Exception) { a }
    }

    private fun prefs(): SharedPreferences =
        context.applicationContext.getSharedPreferences(prefsName, Context.MODE_PRIVATE)

    private inline fun SharedPreferences.edit(commit: Boolean = false, block: SharedPreferences.Editor.() -> Unit) {
        val e = edit(); e.block()
        if (commit) e.commit() else e.apply()
    }

    private fun saveGpsToPrefs(lat: String, lon: String, alt: String, mode: String) {
        prefs().edit {
            putString("qth_lat", lat)
            putString("qth_lon", lon)
            putString("qth_alt", alt)
            putString("qth_mode", mode)
        }
    }

    private fun saveGpsToPrefsForHost(hostKey: String, lat: String, lon: String, alt: String, mode: String) {
        prefs().edit {
            putString("qth_lat_$hostKey", lat)
            putString("qth_lon_$hostKey", lon)
            putString("qth_alt_$hostKey", alt)
            putString("qth_mode_$hostKey", mode)
        }
    }

    private fun internalStop(destroy: Boolean) {
        try {
            webView?.evaluateJavascript(
                "(function(){try{window.__gpsStop&&window.__gpsStop();}catch(e){}})();",
                null
            )
        } catch (_: Exception) {}
        if (destroy) {
            try { webView?.destroy() } catch (_: Exception) {}
            webView = null
            // logI("GPS watcher: stopped")
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun buildInjectedJs(watchMillis: Long): String = """
(function(){
  try{
    var qthLat = localStorage.getItem('qthLatitude') || "";
    var qthLon = localStorage.getItem('qthLongitude') || "";

    window.__gpsStopped = false;
    window.__gpsWS = null;
    window.__gpsNoGpsTimer = null;

    function sendNoGpsOnce(){
      if (window.__gpsStopped) return;
      Android.onNoGps(String(qthLat), String(qthLon));
    }

    // Sofortiger Push falls statische QTH vorhanden + Fallback nach 5s
    if (qthLat || qthLon) { sendNoGpsOnce(); }
    window.__gpsNoGpsTimer = setTimeout(sendNoGpsOnce, 5000);

    window.__gpsStop = function(){
      try { window.__gpsStopped = true; } catch(e){}
      try { if (window.__gpsNoGpsTimer) { clearTimeout(window.__gpsNoGpsTimer); window.__gpsNoGpsTimer = null; } } catch(e){}
      try { if (window.__gpsWS) { window.__gpsWS.close(); window.__gpsWS = null; } } catch(e){}
    };

    var loc = window.location;
    var wsScheme = (loc.protocol === 'https:') ? 'wss://' : 'ws://';
    var base = loc.host + loc.pathname;
    if (!base.endsWith('/')) base += '/';
    var wsUrl = wsScheme + base + 'data_plugins';

    var ws = new WebSocket(wsUrl);
    window.__gpsWS = ws;

    ws.onmessage = function(ev){
      if (window.__gpsStopped) return;
      try {
        var obj = JSON.parse(ev.data);
        if (obj && obj.type === 'GPS' && obj.value) {
          if (String(obj.value.status).toLowerCase() === 'active') {
            if (window.__gpsNoGpsTimer) { clearTimeout(window.__gpsNoGpsTimer); window.__gpsNoGpsTimer = null; }
            Android.onGps(
              String(obj.value.lat || ""),
              String(obj.value.lon || ""),
              String(obj.value.alt || ""),
              String(obj.value.mode || "")
            );
          }
        }
      } catch(e) {}
    };

    setTimeout(function(){
      if (!window.__gpsStopped) { try { ws.close(); } catch(e){} }
    }, ${watchMillis});
  }catch(e){
    try { Android.onNoGps(""); } catch(_){}
  }
})();
""".trimIndent()

    // -------- JS-Bridge --------
    private class GpsJsBridge(
        private val hostKey: String,
        private val saveGlobal: (String, String, String, String) -> Unit,
        private val saveForHost: (String, String, String, String) -> Unit,
        private val onLog: (String) -> Unit,
        private val onGpsCallback: (String, String, String, String) -> Unit,
        private val stopWebView: () -> Unit
    ) {
        @Volatile private var noGpsLogged = false
        @Volatile private var lastGpsLogMs: Long = 0L
        private val LOG_INTERVAL_MS = 15_000L

        private fun round6(value: String): String =
            value.toDoubleOrNull()?.let { String.format(Locale.US, "%.6f", it) } ?: value

        private fun round1(value: String): String =
            value.toDoubleOrNull()?.let { String.format(Locale.US, "%.1f", it) } ?: value

        @JavascriptInterface
        fun onGps(lat: String?, lon: String?, alt: String?, mode: String?) {
            val la = round6(lat?.trim().orEmpty())
            val lo = round6(lon?.trim().orEmpty())
            val al = round1((alt ?: "").trim().ifEmpty { "0" })
            val md = (mode ?: "").trim().ifEmpty { "2" }

            val now = android.os.SystemClock.elapsedRealtime()
            if (lastGpsLogMs == 0L || now - lastGpsLogMs >= LOG_INTERVAL_MS) {
                lastGpsLogMs = now
                // onLog("GPS → lat=$la lon=$lo alt=$al mode=$md")  // <- ENTFERNT
            }

            saveGlobal(la, lo, al, md)
            saveForHost(la, lo, al, md)
            onGpsCallback(la, lo, al, md)
            stopWebView()
        }

        @JavascriptInterface
        fun onNoGps(lat: String?, lon: String?) {
            if (noGpsLogged) return
            noGpsLogged = true

            val la = round6(lat?.trim().orEmpty())
            val lo = round6(lon?.trim().orEmpty())
            val al = "0"
            val md = "2"

            // Diese Methode meldet jetzt einfach die Daten. Der NetworkService entscheidet, was er damit macht.
            // onLog("NO GPS → lat=$la lon=$lo alt=$al mode=$md") // Loggen passiert jetzt im NetworkService

            saveGlobal(la, lo, al, md)
            saveForHost(la, lo, al, md)
            onGpsCallback(la, lo, al, md)
        }
    }

}