package com.tt.honeyDue.notifications

import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

/**
 * P4 Stream P — tests for [NotificationPreferencesStore].
 *
 * Robolectric-backed because the store both reads/writes DataStore and
 * rewrites Android [android.app.NotificationChannel] importance when a
 * category toggle flips.
 *
 * Mirrors the iOS behaviour in
 * `iosApp/iosApp/Profile/NotificationPreferencesView.swift` where each
 * category toggle persists independently and a master switch can disable
 * everything in one tap.
 */
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.TIRAMISU])
class NotificationPreferencesStoreTest {

    private lateinit var context: Context
    private lateinit var store: NotificationPreferencesStore
    private lateinit var manager: NotificationManager

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
        manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        // Clean slate
        manager.notificationChannels.forEach { manager.deleteNotificationChannel(it.id) }
        NotificationChannels.ensureChannels(context)
        store = NotificationPreferencesStore(context)
    }

    @After
    fun tearDown() = runTest {
        store.clearAll()
    }

    @Test
    fun defaults_allCategoriesEnabled() = runTest {
        assertTrue(store.isCategoryEnabled(NotificationChannels.TASK_REMINDER))
        assertTrue(store.isCategoryEnabled(NotificationChannels.TASK_OVERDUE))
        assertTrue(store.isCategoryEnabled(NotificationChannels.RESIDENCE_INVITE))
        assertTrue(store.isCategoryEnabled(NotificationChannels.SUBSCRIPTION))
    }

    @Test
    fun defaults_masterToggleEnabled() = runTest {
        assertTrue(store.isAllEnabled())
    }

    @Test
    fun setCategoryEnabled_false_persists() = runTest {
        store.setCategoryEnabled(NotificationChannels.TASK_REMINDER, false)
        assertFalse(store.isCategoryEnabled(NotificationChannels.TASK_REMINDER))
        // Other categories untouched
        assertTrue(store.isCategoryEnabled(NotificationChannels.TASK_OVERDUE))
    }

    @Test
    fun setCategoryEnabled_roundtrip_trueThenFalseThenTrue() = runTest {
        val id = NotificationChannels.TASK_OVERDUE
        store.setCategoryEnabled(id, false)
        assertFalse(store.isCategoryEnabled(id))
        store.setCategoryEnabled(id, true)
        assertTrue(store.isCategoryEnabled(id))
    }

    @Test
    fun setAllEnabled_false_disablesEveryCategory() = runTest {
        store.setAllEnabled(false)
        assertFalse(store.isAllEnabled())
        assertFalse(store.isCategoryEnabled(NotificationChannels.TASK_REMINDER))
        assertFalse(store.isCategoryEnabled(NotificationChannels.TASK_OVERDUE))
        assertFalse(store.isCategoryEnabled(NotificationChannels.RESIDENCE_INVITE))
        assertFalse(store.isCategoryEnabled(NotificationChannels.SUBSCRIPTION))
    }

    @Test
    fun setAllEnabled_true_reenablesEveryCategory() = runTest {
        store.setAllEnabled(false)
        store.setAllEnabled(true)
        assertTrue(store.isAllEnabled())
        assertTrue(store.isCategoryEnabled(NotificationChannels.TASK_REMINDER))
        assertTrue(store.isCategoryEnabled(NotificationChannels.TASK_OVERDUE))
        assertTrue(store.isCategoryEnabled(NotificationChannels.RESIDENCE_INVITE))
        assertTrue(store.isCategoryEnabled(NotificationChannels.SUBSCRIPTION))
    }

    @Test
    fun observePreferences_emitsInitialSnapshot() = runTest {
        val snapshot = store.observePreferences().first()
        assertEquals(true, snapshot[NotificationChannels.TASK_REMINDER])
        assertEquals(true, snapshot[NotificationChannels.TASK_OVERDUE])
        assertEquals(true, snapshot[NotificationChannels.RESIDENCE_INVITE])
        assertEquals(true, snapshot[NotificationChannels.SUBSCRIPTION])
    }

    @Test
    fun observePreferences_emitsUpdatesOnChange() = runTest {
        // Collect first two distinct emissions: the initial snapshot and the
        // update produced by flipping TASK_OVERDUE.
        val collected = mutableListOf<Map<String, Boolean>>()
        val job = launch {
            store.observePreferences().take(2).toList(collected)
        }
        // Let the first emission land, then flip the flag.
        yield()
        store.setCategoryEnabled(NotificationChannels.TASK_OVERDUE, false)
        job.join()

        assertEquals(2, collected.size)
        assertEquals(true, collected[0][NotificationChannels.TASK_OVERDUE])
        assertEquals(false, collected[1][NotificationChannels.TASK_OVERDUE])
    }

    @Test
    fun setCategoryEnabled_false_rewritesChannelImportanceToNone() = runTest {
        // Precondition: TASK_REMINDER was created with IMPORTANCE_DEFAULT.
        val before = manager.getNotificationChannel(NotificationChannels.TASK_REMINDER)
        assertEquals(NotificationManager.IMPORTANCE_DEFAULT, before.importance)

        store.setCategoryEnabled(NotificationChannels.TASK_REMINDER, false)

        val after = manager.getNotificationChannel(NotificationChannels.TASK_REMINDER)
        assertEquals(NotificationManager.IMPORTANCE_NONE, after.importance)
    }

    @Test
    fun setAllEnabled_false_silencesAllChannels() = runTest {
        store.setAllEnabled(false)

        listOf(
            NotificationChannels.TASK_REMINDER,
            NotificationChannels.TASK_OVERDUE,
            NotificationChannels.RESIDENCE_INVITE,
            NotificationChannels.SUBSCRIPTION,
        ).forEach { id ->
            val channel = manager.getNotificationChannel(id)
            assertEquals(
                "Channel $id should be IMPORTANCE_NONE after master toggle off",
                NotificationManager.IMPORTANCE_NONE,
                channel.importance,
            )
        }
    }
}
