package com.tt.honeyDue.notifications

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
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.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

/**
 * DataStore instance backing [NotificationPreferencesStore]. Kept at file
 * scope so the delegate creates exactly one instance per process, as
 * required by `preferencesDataStore`.
 */
private val Context.notificationPreferencesDataStore: DataStore<Preferences> by preferencesDataStore(
    name = "notification_preferences",
)

/**
 * P4 Stream P — per-category notification preferences for Android.
 *
 * Two distinct concepts are kept in the same DataStore file:
 *
 *   1. Per-category Boolean flags (one key per [NotificationChannels] id).
 *      These back the UI switches on `NotificationPreferencesScreen`.
 *
 *   2. A master "all enabled" flag that, when toggled off, silences every
 *      category in one write.
 *
 * On every write, the matching Android [android.app.NotificationChannel]'s
 * importance is rewritten:
 *
 *   * enabled  → restored to the original importance from
 *                [NotificationChannels.ensureChannels] (DEFAULT/HIGH/LOW).
 *   * disabled → [NotificationManager.IMPORTANCE_NONE] so the system
 *                silences it without requiring the user to open system
 *                settings.
 *
 * **Caveat (documented on purpose, not a bug):** Android only allows apps
 * to *lower* channel importance after creation. If the user additionally
 * disabled a channel via system settings, re-enabling it in our UI cannot
 * raise its importance back — the user must restore it in system settings.
 * The "Open system settings" button on the screen surfaces this path, and
 * our DataStore flag still tracks the user's intent so the UI stays in
 * sync with reality if they re-enable it later.
 *
 * Mirrors the iOS per-category toggle behaviour in
 * `iosApp/iosApp/Profile/NotificationPreferencesView.swift`.
 */
class NotificationPreferencesStore(private val context: Context) {

    private val store get() = context.notificationPreferencesDataStore

    private val notificationManager: NotificationManager by lazy {
        context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }

    /** All channel ids this store manages, in display order. */
    private val categoryIds: List<String> = listOf(
        NotificationChannels.TASK_REMINDER,
        NotificationChannels.TASK_OVERDUE,
        NotificationChannels.RESIDENCE_INVITE,
        NotificationChannels.SUBSCRIPTION,
    )

    private fun categoryKey(channelId: String) = booleanPreferencesKey("cat_$channelId")
    private val masterKey = booleanPreferencesKey("master_enabled")

    // ---------------------------------------------------------------------
    // Reads
    // ---------------------------------------------------------------------

    suspend fun isCategoryEnabled(channelId: String): Boolean =
        store.data.first()[categoryKey(channelId)] ?: true

    suspend fun isAllEnabled(): Boolean = store.data.first()[masterKey] ?: true

    /**
     * Cold [Flow] that emits the full category → enabled map on every
     * DataStore change. Always includes every [categoryIds] entry, even if
     * it hasn't been explicitly written yet (defaults to `true`).
     */
    fun observePreferences(): Flow<Map<String, Boolean>> = store.data.map { prefs ->
        categoryIds.associateWith { id -> prefs[categoryKey(id)] ?: true }
    }

    // ---------------------------------------------------------------------
    // Writes
    // ---------------------------------------------------------------------

    suspend fun setCategoryEnabled(channelId: String, enabled: Boolean) {
        store.edit { prefs ->
            prefs[categoryKey(channelId)] = enabled
            // Keep the master flag coherent: if any category is disabled,
            // master is false; if every category is enabled, master is true.
            val everyEnabled = categoryIds.all { id ->
                if (id == channelId) enabled else prefs[categoryKey(id)] ?: true
            }
            prefs[masterKey] = everyEnabled
        }
        applyChannelImportance(channelId, enabled)
    }

    suspend fun setAllEnabled(enabled: Boolean) {
        store.edit { prefs ->
            prefs[masterKey] = enabled
            categoryIds.forEach { id -> prefs[categoryKey(id)] = enabled }
        }
        categoryIds.forEach { id -> applyChannelImportance(id, enabled) }
    }

    /** Remove every key owned by this store. Used on logout / test teardown. */
    suspend fun clearAll() {
        store.edit { prefs ->
            prefs.remove(masterKey)
            categoryIds.forEach { id -> prefs.remove(categoryKey(id)) }
        }
    }

    // ---------------------------------------------------------------------
    // Channel importance rewrite
    // ---------------------------------------------------------------------

    /**
     * Map a channel id to the importance it was created with in
     * [NotificationChannels]. Keep this table in sync with the `when`
     * chain there.
     */
    private fun defaultImportanceFor(channelId: String): Int = when (channelId) {
        NotificationChannels.TASK_OVERDUE -> NotificationManager.IMPORTANCE_HIGH
        NotificationChannels.SUBSCRIPTION -> NotificationManager.IMPORTANCE_LOW
        NotificationChannels.TASK_REMINDER,
        NotificationChannels.RESIDENCE_INVITE,
        -> NotificationManager.IMPORTANCE_DEFAULT
        else -> NotificationManager.IMPORTANCE_DEFAULT
    }

    /**
     * Rewrite the channel importance. On O+ we reach for the platform
     * NotificationManager API directly; on older releases channels do not
     * exist and this is a no-op (legacy handling in
     * [NotificationChannels.ensureChannels]).
     */
    private fun applyChannelImportance(channelId: String, enabled: Boolean) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return

        val existing = notificationManager.getNotificationChannel(channelId)
        if (existing == null) {
            // Channel hasn't been created yet — bail out. It will be
            // created with the right importance the next time
            // NotificationChannels.ensureChannels runs, and future writes
            // will see it.
            return
        }
        val targetImportance = if (enabled) {
            defaultImportanceFor(channelId)
        } else {
            NotificationManager.IMPORTANCE_NONE
        }
        if (existing.importance == targetImportance) return

        // Android only lets us LOWER importance via updateNotificationChannel.
        // To silence → always safe (NONE < everything else).
        // To re-enable (raise) → attempt the update; if the system refused
        // to raise it (user disabled via system settings) the importance
        // remains as-is and the user must restore via system settings.
        val rewritten = NotificationChannel(existing.id, existing.name, targetImportance).apply {
            description = existing.description
            group = existing.group
            setShowBadge(existing.canShowBadge())
        }
        notificationManager.createNotificationChannel(rewritten)
    }
}
