package com.marcus.meditationtimer

import android.content.Context
import android.content.SharedPreferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.json.JSONArray
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL

data class Session(
    val id: String,
    val startedAt: String,
    val endedAt: String?,
    val plannedMin: Int,
    val actualMin: Int,
    val completed: Boolean,
    val pauseCount: Int,
    val rating: Int? = null,
    val note: String? = null,
    val synced: Boolean = false,
) {
    val completionPct: Int
        get() = if (plannedMin > 0) minOf(100, Math.round(actualMin * 100f / plannedMin)) else 0
}

/**
 * Local session log (SharedPreferences-backed) that syncs to the health API
 * at meditation.rehbock.xyz. The bearer token is never shipped in the APK —
 * it is entered in-app (or seeded via `adb shell am start ... --es sync_token X`)
 * and lives only in local app storage.
 */
object SessionStore {
    private const val PREFS = "sessions"
    private const val KEY_LIST = "list"
    private const val KEY_TOKEN = "sync_token"
    private const val BASE_URL = "https://meditation.rehbock.xyz"

    private lateinit var prefs: SharedPreferences
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private val lock = Any()

    val sessions = MutableStateFlow<List<Session>>(emptyList())
    val pendingSync = MutableStateFlow(0)
    val lastSessionId = MutableStateFlow<String?>(null)

    fun init(context: Context) {
        synchronized(lock) {
            if (::prefs.isInitialized) return
            prefs = context.applicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
            publish(load())
        }
        syncPending()
    }

    var token: String
        get() = synchronized(lock) { prefs.getString(KEY_TOKEN, "") ?: "" }
        set(value) {
            synchronized(lock) { prefs.edit().putString(KEY_TOKEN, value.trim()).apply() }
            syncPending()
        }

    fun record(session: Session) {
        synchronized(lock) { persist(load() + session) }
        syncPending()
    }

    fun updateRating(id: String, rating: Int?, note: String?) {
        synchronized(lock) {
            persist(load().map {
                if (it.id == id) it.copy(rating = rating, note = note?.ifBlank { null }, synced = false) else it
            })
        }
        syncPending()
    }

    fun syncPending() {
        val tok = token
        if (tok.isEmpty()) return
        scope.launch {
            val pending = synchronized(lock) { load().filter { !it.synced } }
            for (s in pending) {
                if (postSession(s, tok)) {
                    synchronized(lock) {
                        persist(load().map { if (it.id == s.id) it.copy(synced = true) else it })
                    }
                }
            }
        }
    }

    private fun postSession(s: Session, tok: String): Boolean = try {
        val conn = URL("$BASE_URL/v1/sessions").openConnection() as HttpURLConnection
        conn.requestMethod = "POST"
        conn.setRequestProperty("Authorization", "Bearer $tok")
        conn.setRequestProperty("Content-Type", "application/json")
        conn.connectTimeout = 10_000
        conn.readTimeout = 10_000
        conn.doOutput = true
        val body = JSONObject().apply {
            put("id", s.id)
            put("startedAt", s.startedAt)
            s.endedAt?.let { put("endedAt", it) }
            put("plannedMin", s.plannedMin)
            put("actualMin", s.actualMin)
            put("completed", s.completed)
            put("pauseCount", s.pauseCount)
            s.rating?.let { put("rating", it) }
            s.note?.let { put("note", it) }
        }
        conn.outputStream.use { it.write(body.toString().toByteArray()) }
        val ok = conn.responseCode in 200..299
        conn.disconnect()
        ok
    } catch (_: Exception) {
        false
    }

    private fun load(): List<Session> {
        val raw = prefs.getString(KEY_LIST, "[]") ?: "[]"
        return try {
            val arr = JSONArray(raw)
            (0 until arr.length()).map { i ->
                val o = arr.getJSONObject(i)
                Session(
                    id = o.getString("id"),
                    startedAt = o.getString("startedAt"),
                    endedAt = o.optString("endedAt").ifEmpty { null },
                    plannedMin = o.getInt("plannedMin"),
                    actualMin = o.getInt("actualMin"),
                    completed = o.getBoolean("completed"),
                    pauseCount = o.optInt("pauseCount", 0),
                    rating = if (o.has("rating")) o.getInt("rating") else null,
                    note = o.optString("note").ifEmpty { null },
                    synced = o.optBoolean("synced", false),
                )
            }
        } catch (_: Exception) {
            emptyList()
        }
    }

    private fun persist(list: List<Session>) {
        val arr = JSONArray()
        list.forEach { s ->
            arr.put(JSONObject().apply {
                put("id", s.id)
                put("startedAt", s.startedAt)
                s.endedAt?.let { put("endedAt", it) }
                put("plannedMin", s.plannedMin)
                put("actualMin", s.actualMin)
                put("completed", s.completed)
                put("pauseCount", s.pauseCount)
                s.rating?.let { put("rating", it) }
                s.note?.let { put("note", it) }
                put("synced", s.synced)
            })
        }
        prefs.edit().putString(KEY_LIST, arr.toString()).apply()
        publish(list)
    }

    private fun publish(list: List<Session>) {
        sessions.value = list.sortedByDescending { it.startedAt }
        pendingSync.value = list.count { !it.synced }
    }
}
