package com.tt.honeyDue.notifications

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log

/**
 * Schedules a "snooze" redelivery of a task notification using [AlarmManager].
 *
 * iOS achieves snooze via UNNotificationRequest with a 30-minute
 * UNTimeIntervalNotificationTrigger (see
 * `iosApp/iosApp/PushNotifications/NotificationCategories.swift`). Android
 * has no equivalent, so we lean on AlarmManager to wake us in 30 minutes
 * and rebroadcast to [NotificationActionReceiver], which re-posts the
 * notification via [FcmService]'s builder path.
 *
 * Exact alarms (`setExactAndAllowWhileIdle`) require the `SCHEDULE_EXACT_ALARM`
 * permission on Android 12+. Because P4 Stream O is scoped to receiver-only
 * changes in the manifest, we probe [AlarmManager.canScheduleExactAlarms] at
 * runtime and fall back to the inexact `setAndAllowWhileIdle` variant when
 * the permission has not been granted — snooze fidelity in that case is
 * "roughly 30 min" which is within Android's Doze tolerance and acceptable.
 */
object SnoozeScheduler {

    private const val TAG = "SnoozeScheduler"

    /**
     * Schedule a snooze alarm [delayMs] into the future for the given [taskId].
     * If an alarm already exists for this task id, it is replaced (pending
     * intents are reused by request code = taskId).
     */
    fun schedule(
        context: Context,
        taskId: Long,
        delayMs: Long = NotificationActions.SNOOZE_DELAY_MS,
        title: String? = null,
        body: String? = null,
        type: String? = null
    ) {
        val alarm = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
        if (alarm == null) {
            Log.w(TAG, "AlarmManager unavailable; cannot schedule snooze for task=$taskId")
            return
        }

        val triggerAt = System.currentTimeMillis() + delayMs
        val pi = pendingIntent(context, taskId, title, body, type, create = true)
            ?: run {
                Log.w(TAG, "Unable to build snooze PendingIntent for task=$taskId")
                return
            }

        val useExact = Build.VERSION.SDK_INT < Build.VERSION_CODES.S ||
            alarm.canScheduleExactAlarms()

        if (useExact) {
            alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pi)
        } else {
            // Fallback path when SCHEDULE_EXACT_ALARM is revoked. Inexact but
            // still wakes the device from Doze.
            alarm.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pi)
        }
        Log.d(TAG, "Scheduled snooze for task=$taskId at $triggerAt (exact=$useExact)")
    }

    /** Cancel a scheduled snooze alarm for [taskId], if any. */
    fun cancel(context: Context, taskId: Long) {
        val alarm = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager ?: return
        val pi = pendingIntent(context, taskId, null, null, null, create = false) ?: return
        alarm.cancel(pi)
        pi.cancel()
    }

    private fun pendingIntent(
        context: Context,
        taskId: Long,
        title: String?,
        body: String?,
        type: String?,
        create: Boolean
    ): PendingIntent? {
        val intent = Intent(context, NotificationActionReceiver::class.java).apply {
            action = NotificationActions.SNOOZE_FIRE
            putExtra(NotificationActions.EXTRA_TASK_ID, taskId)
            title?.let { putExtra(NotificationActions.EXTRA_TITLE, it) }
            body?.let { putExtra(NotificationActions.EXTRA_BODY, it) }
            type?.let { putExtra(NotificationActions.EXTRA_TYPE, it) }
        }
        val baseFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        val lookupFlags = if (create) baseFlags else PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
        val requestCode = requestCode(taskId)
        return PendingIntent.getBroadcast(context, requestCode, intent, lookupFlags)
    }

    /** Stable request code for per-task snoozes so cancel() finds the same PI. */
    private fun requestCode(taskId: Long): Int {
        // Fold 64-bit task id into a stable 32-bit request code.
        return (taskId xor (taskId ushr 32)).toInt() xor SNOOZE_REQUEST_SALT
    }

    private const val SNOOZE_REQUEST_SALT = 0x534E5A45.toInt() // "SNZE"
}
