package app.closer.notifications

import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import app.closer.core.navigation.AppRoute
import app.closer.domain.repository.AppSettings
import app.closer.domain.repository.SettingsRepository
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkConstructor
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class PartnerNotificationManagerTest {

    private val context = mockk<Context>(relaxed = true)
    private val settingsRepository = mockk<SettingsRepository>()
    private val quietHoursManager = mockk<QuietHoursManager>()
    private val rateLimiter = mockk<NotificationRateLimiter>(relaxed = true)
    private val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)

    private lateinit var manager: PartnerNotificationManager

    @Before
    fun setup() {
        coEvery { settingsRepository.settings } returns flowOf(
            AppSettings(
                dailyReminderEnabled = true,
                partnerAnsweredEnabled = true
            )
        )
        every { quietHoursManager.isInQuietHours(any(), any()) } returns false
        every { rateLimiter.canSend(any()) } returns true

        mockkStatic(NotificationManagerCompat::from)
        every { NotificationManagerCompat.from(context) } returns notificationManagerCompat
        every { notificationManagerCompat.areNotificationsEnabled() } returns true

        mockkConstructor(NotificationCompat.Builder::class)
        every { anyConstructed<NotificationCompat.Builder>().setSmallIcon(any<Int>()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().setContentTitle(any()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().setContentText(any()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().setAutoCancel(any()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().setContentIntent(any()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().setCategory(any()) } returns mockk(relaxed = true)
        every { anyConstructed<NotificationCompat.Builder>().build() } returns mockk(relaxed = true)

        manager = PartnerNotificationManager(context, settingsRepository, quietHoursManager, rateLimiter)
    }

    @After
    fun tearDown() {
        unmockkAll()
    }

    @Test
    fun `show records rate limit when enabled`() = runTest {
        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "couple_1")

        verify { rateLimiter.record(NotificationRateLimiter.Type.PARTNER_TRIGGER) }
    }

    @Test
    fun `show skips when opt-out disabled`() = runTest {
        coEvery { settingsRepository.settings } returns flowOf(
            AppSettings(
                dailyReminderEnabled = true,
                partnerAnsweredEnabled = false
            )
        )

        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "couple_1")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }

    @Test
    fun `show skips during quiet hours`() = runTest {
        every { quietHoursManager.isInQuietHours(any(), any()) } returns true

        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "couple_1")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }

    @Test
    fun `show skips when rate limit exhausted`() = runTest {
        every { rateLimiter.canSend(any()) } returns false

        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "couple_1")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }

    @Test
    fun `handleRemote maps known FCM types to local types`() = runTest {
        manager.handleRemote("reveal_ready", "couple_1", PartnerNotificationPayload(questionId = "q1"))

        verify { rateLimiter.record(NotificationRateLimiter.Type.PARTNER_TRIGGER) }
    }

    @Test
    fun `handleRemote ignores unknown remote types`() = runTest {
        manager.handleRemote("malicious_backend_title", "couple_1")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }

    @Test
    fun `show skips blank couple id`() = runTest {
        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }

    @Test
    fun `deep link routes are correct for each notification type`() {
        assertEquals(AppRoute.DAILY_QUESTION, PartnerNotificationType.PARTNER_ANSWERED.routeFor(PartnerNotificationPayload()))
        assertEquals(AppRoute.answerReveal("q1"), PartnerNotificationType.REVEAL_READY.routeFor(PartnerNotificationPayload(questionId = "q1")))
        assertEquals(AppRoute.ANSWER_HISTORY, PartnerNotificationType.REVEAL_READY.routeFor(PartnerNotificationPayload()))
        assertEquals(AppRoute.PLAY, PartnerNotificationType.PARTNER_STARTED_GAME.routeFor(PartnerNotificationPayload()))
        assertEquals(AppRoute.PLAY, PartnerNotificationType.PARTNER_COMPLETED_PART.routeFor(PartnerNotificationPayload()))
        assertEquals(AppRoute.CONNECTION_CHALLENGES, PartnerNotificationType.CHALLENGE_WAITING.routeFor(PartnerNotificationPayload()))
        assertEquals(AppRoute.MEMORY_LANE, PartnerNotificationType.CAPSULE_UNLOCKED.routeFor(PartnerNotificationPayload()))
    }

    @Test
    fun `notification type rate mapping is correct`() {
        assertTrue(
            listOf(
                PartnerNotificationType.PARTNER_ANSWERED,
                PartnerNotificationType.REVEAL_READY,
                PartnerNotificationType.PARTNER_STARTED_GAME,
                PartnerNotificationType.PARTNER_COMPLETED_PART,
                PartnerNotificationType.CAPSULE_UNLOCKED
            ).all { it.rateType == NotificationRateLimiter.Type.PARTNER_TRIGGER }
        )
        assertEquals(NotificationRateLimiter.Type.REMINDER, PartnerNotificationType.CHALLENGE_WAITING.rateType)
    }

    @Test
    fun `notification copy never includes answer or question text`() {
        PartnerNotificationType.entries.forEach { type ->
            assertTrue("${type.name} title must be static", type.title !in listOf("{questionId}", "{answer}", "{prompt}"))
            assertTrue("${type.name} body must be static", type.body !in listOf("{questionId}", "{answer}", "{prompt}"))
            assertTrue("${type.name} title must not contain placeholder", !type.title.contains("{") && !type.title.contains("}"))
            assertTrue("${type.name} body must not contain placeholder", !type.body.contains("{") && !type.body.contains("}"))
        }
    }

    @Test
    fun `partner answered maps to opt-out setting`() = runTest {
        coEvery { settingsRepository.settings } returns flowOf(
            AppSettings(
                dailyReminderEnabled = true,
                partnerAnsweredEnabled = true
            )
        )

        manager.show(PartnerNotificationType.PARTNER_ANSWERED, "couple_1")

        verify { rateLimiter.record(NotificationRateLimiter.Type.PARTNER_TRIGGER) }
    }

    @Test
    fun `challenge waiting maps to reminder opt-out setting`() = runTest {
        coEvery { settingsRepository.settings } returns flowOf(
            AppSettings(
                dailyReminderEnabled = false,
                partnerAnsweredEnabled = true
            )
        )

        manager.show(PartnerNotificationType.CHALLENGE_WAITING, "couple_1")

        verify(exactly = 0) { rateLimiter.record(any()) }
    }
}
