package app.closer.notifications

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import app.closer.MainActivity
import app.closer.R
import app.closer.core.navigation.AppRoute
import app.closer.domain.repository.AppSettings
import app.closer.domain.repository.SettingsRepository
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.first
import javax.inject.Inject

/**
 * Manages local and FCM-driven partner-triggered notifications.
 *
 * Responsibilities:
 *  - Enforce notification opt-outs from [SettingsRepository].
 *  - Respect quiet hours via [QuietHoursManager].
 *  - Apply rate limits via [NotificationRateLimiter].
 *  - Collapse duplicate notifications by deriving the notification ID from
 *    the notification type and couple ID hash.
 *  - Deep link each notification to the correct screen.
 *
 * Security: notification titles and bodies are static. Answer text, prompt
 * text, and other sensitive content are never included.
 */
class PartnerNotificationManager @Inject constructor(
    @ApplicationContext private val context: Context,
    private val settingsRepository: SettingsRepository,
    private val quietHoursManager: QuietHoursManager,
    private val rateLimiter: NotificationRateLimiter
) {

    /**
     * Shows a partner-trigger notification if the user has not opted out,
     * quiet hours are not active, and rate limits allow it.
     *
     * @param type The notification type (decides copy, channel, and deep link).
     * @param coupleId The couple ID used for duplicate collapse.
     * @param payload Optional IDs needed to build the exact deep link route.
     */
    suspend fun show(
        type: PartnerNotificationType,
        coupleId: String,
        payload: PartnerNotificationPayload = PartnerNotificationPayload()
    ) {
        if (coupleId.isBlank()) return

        val settings = settingsRepository.settings.first()

        if (!isEnabled(type, settings)) return
        if (quietHoursManager.isInQuietHours(settings.quietHours)) return
        if (!rateLimiter.canSend(type.rateType)) return

        rateLimiter.record(type.rateType)

        val route = type.routeFor(payload)
        val notificationId = collapseId(type, coupleId)

        showNotification(notificationId, type, route)
    }

    /**
     * Maps a remote FCM message type to a [PartnerNotificationType] and shows it.
     *
     * Unknown types are ignored so the backend cannot force arbitrary notification text.
     */
    suspend fun handleRemote(
        type: String,
        coupleId: String,
        payload: PartnerNotificationPayload = PartnerNotificationPayload()
    ) {
        val notificationType = PartnerNotificationType.fromRemoteType(type) ?: return
        show(notificationType, coupleId, payload)
    }

    private fun isEnabled(type: PartnerNotificationType, settings: AppSettings): Boolean {
        return when (type.rateType) {
            NotificationRateLimiter.Type.PARTNER_TRIGGER -> settings.partnerAnsweredEnabled
            NotificationRateLimiter.Type.REMINDER -> settings.dailyReminderEnabled
        }
    }

    private fun showNotification(id: Int, type: PartnerNotificationType, route: String) {
        if (!NotificationManagerCompat.from(context).areNotificationsEnabled()) return

        val deepLinkUri = Uri.parse("${DEEP_LINK_SCHEME}://$DEEP_LINK_HOST/$route")
        val intent = Intent(context, MainActivity::class.java).apply {
            action = Intent.ACTION_VIEW
            data = deepLinkUri
            flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
        }
        val pendingIntent = PendingIntent.getActivity(
            context,
            id,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        val notification = NotificationCompat.Builder(context, type.channelId)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(type.title)
            .setContentText(type.body)
            .setAutoCancel(true)
            .setContentIntent(pendingIntent)
            .setCategory(NotificationCompat.CATEGORY_SOCIAL)
            .build()

        NotificationManagerCompat.from(context).notify(id, notification)
    }

    private fun collapseId(type: PartnerNotificationType, coupleId: String): Int {
        // Same type + same couple always collapses to the same ID,
        // preventing duplicate notifications in the shade.
        val hash = type.name.hashCode() * 31 + coupleId.hashCode()
        return (hash and 0x7FFFFFFF) % ID_MODULO
    }

    companion object {
        private const val DEEP_LINK_SCHEME = "closer"
        private const val DEEP_LINK_HOST = "closer.app"
        private const val ID_MODULO = 100_000
    }
}

/**
 * The supported partner-trigger notification types.
 *
 * Titles and bodies are fixed strings; no user-generated content is ever shown.
 */
enum class PartnerNotificationType(
    val title: String,
    val body: String,
    val channelId: String,
    val rateType: NotificationRateLimiter.Type
) {
    PARTNER_ANSWERED(
        title = "Your partner answered.",
        body = "Your turn to unlock the reveal.",
        channelId = NotificationChannelSetup.CHANNEL_PARTNER_ACTIONS,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    ),
    REVEAL_READY(
        title = "Your reveal is ready.",
        body = "Open it when you're both ready.",
        channelId = NotificationChannelSetup.CHANNEL_PARTNER_ACTIONS,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    ),
    PARTNER_STARTED_GAME(
        title = "Your partner started a game for the two of you.",
        body = "Tap to join them.",
        channelId = NotificationChannelSetup.CHANNEL_GAMES,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    ),
    PARTNER_COMPLETED_PART(
        title = "Your partner finished their part.",
        body = "Open yours when you're ready.",
        channelId = NotificationChannelSetup.CHANNEL_PARTNER_ACTIONS,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    ),
    CHALLENGE_WAITING(
        title = "Tonight's small challenge is waiting.",
        body = "A little shared moment is ready.",
        channelId = NotificationChannelSetup.CHANNEL_REMINDERS,
        rateType = NotificationRateLimiter.Type.REMINDER
    ),
    CAPSULE_UNLOCKED(
        title = "A memory capsule just unlocked.",
        body = "Something you sealed together is ready to open.",
        channelId = NotificationChannelSetup.CHANNEL_PARTNER_ACTIONS,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    ),
    GENTLE_REMINDER(
        title = "Your partner is thinking about you.",
        body = "They left tonight's question open. Answer when you're ready.",
        channelId = NotificationChannelSetup.CHANNEL_PARTNER_ACTIONS,
        rateType = NotificationRateLimiter.Type.PARTNER_TRIGGER
    );

    /**
     * Builds the deep link route for this notification type.
     */
    fun routeFor(payload: PartnerNotificationPayload): String = when (this) {
        PARTNER_ANSWERED -> AppRoute.DAILY_QUESTION
        REVEAL_READY -> payload.questionId?.let { AppRoute.answerReveal(it) } ?: AppRoute.ANSWER_HISTORY
        PARTNER_STARTED_GAME -> AppRoute.PLAY
        PARTNER_COMPLETED_PART -> AppRoute.PLAY
        CHALLENGE_WAITING -> AppRoute.CONNECTION_CHALLENGES
        CAPSULE_UNLOCKED -> AppRoute.MEMORY_LANE
        GENTLE_REMINDER -> AppRoute.DAILY_QUESTION
    }

    companion object {
        /**
         * Maps backend FCM message types to local notification types.
         * Returns null for unknown types so arbitrary backend text cannot be shown.
         */
        fun fromRemoteType(type: String): PartnerNotificationType? = when (type) {
            "partner_answered" -> PARTNER_ANSWERED
            "reveal_ready" -> REVEAL_READY
            "partner_started_game" -> PARTNER_STARTED_GAME
            "partner_completed_part" -> PARTNER_COMPLETED_PART
            "challenge_waiting" -> CHALLENGE_WAITING
            "memory_capsule_unlocked" -> CAPSULE_UNLOCKED
            "gentle_reminder" -> GENTLE_REMINDER
            else -> null
        }
    }
}

/**
 * Optional IDs used to build exact deep link routes.
 *
 * No answer text, prompt text, or decrypted content is included.
 */
data class PartnerNotificationPayload(
    val questionId: String? = null,
    val gameSessionId: String? = null,
    val capsuleId: String? = null,
    val challengeId: String? = null
)
