package club.clubk.ktag.apps.sharedservices

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
import android.util.Log
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch

object LocationPublisher {

    private const val TAG = "LocationPublisher"
    private const val UPDATE_INTERVAL_MS = 30_000L
    private const val MIN_DISTANCE_M = 0f

    private var locationManager: LocationManager? = null
    private var locationListener: LocationListener? = null
    private var appContext: Context? = null

    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    private var statusJob: Job? = null

    fun start(context: Context) {
        appContext = context.applicationContext
        locationManager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager

        statusJob?.cancel()
        statusJob = scope.launch {
            SharedMqttClient.status.collect { status ->
                if (status is MqttClientStatus.Connected) {
                    requestLocationUpdates()
                } else {
                    stopLocationUpdates()
                }
            }
        }
    }

    fun stop() {
        statusJob?.cancel()
        statusJob = null
        stopLocationUpdates()
        appContext = null
    }

    private fun requestLocationUpdates() {
        val ctx = appContext ?: return
        val lm = locationManager ?: return

        if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED
        ) {
            Log.w(TAG, "Location permission not granted, skipping GPS publishing")
            return
        }

        if (locationListener != null) return  // already registered

        val listener = object : LocationListener {
            override fun onLocationChanged(location: Location) {
                publishLocation(location)
            }

            @Deprecated("Deprecated in API 29")
            override fun onStatusChanged(provider: String, status: Int, extras: Bundle?) {}
            override fun onProviderEnabled(provider: String) {}
            override fun onProviderDisabled(provider: String) {}
        }
        locationListener = listener

        val providers = listOf(LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER)
            .filter { lm.isProviderEnabled(it) }

        if (providers.isEmpty()) {
            Log.w(TAG, "No location providers enabled")
            locationListener = null
            return
        }

        for (provider in providers) {
            try {
                lm.requestLocationUpdates(provider, UPDATE_INTERVAL_MS, MIN_DISTANCE_M, listener, Looper.getMainLooper())
                Log.i(TAG, "Registered location updates from $provider")
            } catch (e: Exception) {
                Log.e(TAG, "Failed to register $provider: ${e.message}")
            }
        }

        // Publish last known location immediately
        providers.firstNotNullOfOrNull { lm.getLastKnownLocation(it) }?.let {
            publishLocation(it)
        }
    }

    private fun stopLocationUpdates() {
        val listener = locationListener ?: return
        locationManager?.removeUpdates(listener)
        locationListener = null
        Log.d(TAG, "Stopped location updates")
    }

    private fun publishLocation(location: Location) {
        val battlefield = SharedMqttClient.battlefield
        val deviceId = SharedMqttClient.currentDeviceId
        val topic = "KTag/$battlefield/Devices/$deviceId/Location"

        val payload = buildString {
            append("{")
            append("\"lat\":").append(location.latitude)
            append(",\"lon\":").append(location.longitude)
            if (location.hasAltitude()) append(",\"alt\":").append(location.altitude)
            if (location.hasAccuracy()) append(",\"accuracy\":").append(location.accuracy)
            append("}")
        }

        SharedMqttClient.publish(topic, payload.toByteArray(), qos = 1, retained = true)
        Log.d(TAG, "Published location to $topic: $payload")
    }
}
