/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.core.push

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

private val Context.pushDataStore: DataStore<Preferences> by preferencesDataStore(name = "push")

/**
 * Persists the app's push state (PLAN.md §11, M7 Part 2 work item 5). None of it is
 * secret — the ntfy topic/endpoint's protection is being unguessable plus the
 * content-free tickle — so plain DataStore is fine (tokens stay in the encrypted
 * store). Holds the shard's ntfy base URL (from `/public/settings`), the minted
 * topic + its endpoint URL, the backend-assigned device id (to unregister), and the
 * user's opt-in flag (the source of truth for "push should be running").
 */
@Singleton
class PushPreferences @Inject constructor(
    @param:ApplicationContext private val context: Context,
) {
    private val store = context.pushDataStore

    val enabled: Flow<Boolean> = store.data.map { it[KEY_ENABLED] ?: false }
    val ntfyUrl: Flow<String?> = store.data.map { it[KEY_NTFY_URL] }

    suspend fun snapshot(): Snapshot {
        val p = store.data.first()
        return Snapshot(
            enabled = p[KEY_ENABLED] ?: false,
            ntfyUrl = p[KEY_NTFY_URL],
            topic = p[KEY_TOPIC],
            endpoint = p[KEY_ENDPOINT],
            deviceId = p[KEY_DEVICE_ID],
        )
    }

    suspend fun setNtfyUrl(url: String?) = store.edit {
        if (url.isNullOrBlank()) it.remove(KEY_NTFY_URL) else it[KEY_NTFY_URL] = url
    }

    suspend fun setEnabled(value: Boolean) = store.edit { it[KEY_ENABLED] = value }

    /** Record the minted topic + its endpoint URL and the assigned device id together. */
    suspend fun setRegistration(topic: String, endpoint: String, deviceId: Long) = store.edit {
        it[KEY_TOPIC] = topic
        it[KEY_ENDPOINT] = endpoint
        it[KEY_DEVICE_ID] = deviceId
    }

    /**
     * Forget the ephemeral device registration (topic/endpoint/device id) — used on
     * sign-out and on an explicit disable. Deliberately leaves [KEY_ENABLED] and
     * [KEY_NTFY_URL] intact so the user's opt-in intent survives a sign-out and push
     * re-registers on the next sign-in; an explicit disable also calls [setEnabled]`(false)`.
     */
    suspend fun clearRegistration() = store.edit {
        it.remove(KEY_TOPIC)
        it.remove(KEY_ENDPOINT)
        it.remove(KEY_DEVICE_ID)
    }

    data class Snapshot(
        val enabled: Boolean,
        val ntfyUrl: String?,
        val topic: String?,
        val endpoint: String?,
        val deviceId: Long?,
    )

    private companion object {
        val KEY_ENABLED = booleanPreferencesKey("enabled")
        val KEY_NTFY_URL = stringPreferencesKey("ntfy_url")
        val KEY_TOPIC = stringPreferencesKey("topic")
        val KEY_ENDPOINT = stringPreferencesKey("endpoint")
        val KEY_DEVICE_ID = longPreferencesKey("device_id")
    }
}
