package wang.yaojia.webterm.push

import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresPermission
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.hilt.android.AndroidEntryPoint
import java.util.Optional
import javax.inject.Inject

/**
 * The FCM entry point (plan §1 P1 push / §4.5 / A30). The server sends **data-only** high-priority
 * messages (no server `notification` block); this service builds the lock-screen UI LOCALLY:
 *
 *  - [onMessageReceived] parses the untrusted data map into a [PushPayload], maps `cls` → a
 *    [NotificationBuilder.NotificationPlan], and posts it. A gate (`needs-input`) card carries the
 *    Allow/Deny actions with their `FLAG_IMMUTABLE` intents; `done` is informational.
 *  - [onNewToken] forwards the rotated registration token to the A31 [PushTokenSink] seam — this
 *    service does NOT implement registration (that is A31); it only calls the injected hook.
 *
 * Injected via Hilt (`@AndroidEntryPoint`). The single-use capability token is only ever placed into
 * the action PendingIntents by [NotificationBuilder]; it is never logged here.
 */
@AndroidEntryPoint
public class FcmService : FirebaseMessagingService() {

    /** A31 seam: forward token rotations to the registrar. `Optional.empty()` until A31 binds it. */
    @Inject
    public lateinit var pushTokenSink: Optional<PushTokenSink>

    override fun onMessageReceived(message: RemoteMessage) {
        val payload = PushPayload.parse(message.data)
        if (payload == null) {
            Log.w(TAG, "dropping malformed push payload")
            return
        }
        val plan = NotificationBuilder.plan(payload.cls)
        if (plan == null) {
            Log.d(TAG, "no notification for class '${payload.cls}'")
            return
        }
        if (!hasNotificationPermission()) {
            Log.w(TAG, "POST_NOTIFICATIONS not granted; skipping push notification")
            return
        }
        postNotification(payload, plan)
    }

    override fun onNewToken(token: String) {
        // Do NOT implement registration here (A31). Just hand the token to the seam if A31 bound one.
        if (pushTokenSink.isPresent) pushTokenSink.get().onTokenRefreshed(token)
    }

    @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
    private fun postNotification(payload: PushPayload, plan: NotificationBuilder.NotificationPlan) {
        val notification = NotificationBuilder.build(this, payload, plan)
        val id = NotificationBuilder.notificationIdFor(payload.sessionId.toString())
        NotificationManagerCompat.from(this).notify(id, notification)
    }

    /** POST_NOTIFICATIONS is a runtime permission on API 33+; below that it is implicitly granted. */
    private fun hasNotificationPermission(): Boolean =
        Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
            ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
            PackageManager.PERMISSION_GRANTED

    private companion object {
        const val TAG = "FcmService"
    }
}
