package com.paarrot.app

import android.content.Context
import android.content.Intent
import com.getcapacitor.JSObject
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin

/**
 * Capacitor plugin that controls [MatrixSyncService] from JS.
 *
 * JS API:
 * - `start({ homeserverUrl, accessToken, userId, deviceId })` — persist credentials and register UnifiedPush
 * - `triggerPing({ reason })` — force a one-shot fetch (for testing/manual wake)
 * - `stop()` — clear persisted credentials and unregister UnifiedPush
 * - `setAppForeground({ foreground })` — tell the service whether the app UI is visible
 * - `getStatus()` — returns current fetch and UnifiedPush state
 * - `requestDistributorSetup()` — clear saved distributor and re-open OS picker
 * - `resolveUnifiedPushGateway({ endpoint })` — native Matrix gateway discovery
 * - `clearRoomNotifications({ roomId })` — dismiss native tray notifs for a room
 * - `setNotificationGroups({ rooms })` — persist space/DM grouping for tray nesting
 * - `showNotification({ title, body, roomId, groupId, groupName, kind, largeIconBase64 })`
 */
@CapacitorPlugin(name = "MatrixBackgroundSync")
class SyncServicePlugin : Plugin() {

    fun emitUnifiedPushEvent(eventName: String, payload: JSObject) {
        notifyListeners(eventName, payload, true)
    }

    override fun load() {
        super.load()
        UnifiedPushManager.setPlugin(this)
        NotificationNavStore.plugin = this
        NotificationNavStore.handleIntent(activity?.intent)
    }

    override fun handleOnNewIntent(intent: Intent?) {
        super.handleOnNewIntent(intent)
        if (intent != null) {
            activity?.intent = intent
        }
        NotificationNavStore.handleIntent(intent)
    }

    override fun handleOnDestroy() {
        UnifiedPushManager.clearPlugin(this)
        if (NotificationNavStore.plugin === this) {
            NotificationNavStore.plugin = null
        }
        super.handleOnDestroy()
    }

    /** Emit a notificationOpened event toward the JS layer. */
    fun emitNotificationOpened(path: String?, roomId: String?) {
        val payload = JSObject()
        if (path != null) payload.put("path", path)
        if (roomId != null) payload.put("roomId", roomId)
        notifyListeners("notificationOpened", payload, true)
    }

    /** Returns any pending notification navigation target from a tray tap. */
    @PluginMethod
    fun getPendingNotificationNav(call: PluginCall) {
        val (path, roomId) = NotificationNavStore.consume()
        val result = JSObject()
        result.put("path", path)
        result.put("roomId", roomId)
        call.resolve(result)
    }

    /** Persists Matrix credentials and starts UnifiedPush registration. */
    @PluginMethod
    fun start(call: PluginCall) {
        val homeserver = call.getString("homeserverUrl")
            ?: return call.reject("homeserverUrl required")
        val token = call.getString("accessToken")
            ?: return call.reject("accessToken required")
        val userId = call.getString("userId") ?: ""
        val deviceId = call.getString("deviceId") ?: ""

        context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit()
            .putString(MatrixSyncService.EXTRA_HOMESERVER, homeserver)
            .putString(MatrixSyncService.EXTRA_TOKEN, token)
            .putString(MatrixSyncService.EXTRA_USER_ID, userId)
            .putString(MatrixSyncService.EXTRA_DEVICE_ID, deviceId)
            .apply()

        UnifiedPushManager.register(context, activity)

        call.resolve()
    }

    /** Manually triggers a one-shot sync fetch (mainly for diagnostics/testing). */
    @PluginMethod
    fun triggerPing(call: PluginCall) {
        val reason = call.getString("reason") ?: "manual_plugin_ping"
        MatrixSyncService.requestSyncFetch(context, reason)
        call.resolve()
    }

    /** Stops the sync service and erases persisted credentials. */
    @PluginMethod
    fun stop(call: PluginCall) {
        context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().clear().apply()
        context.stopService(Intent(context, MatrixSyncService::class.java))
        UnifiedPushManager.unregister(context)
        call.resolve()
    }

    /**
     * Notifies the service whether the Capacitor WebView UI is currently visible.
     * When `foreground == true` the service skips firing notifications because
     * the JS layer handles them directly via LocalNotifications.
     */
    @PluginMethod
    fun setAppForeground(call: PluginCall) {
        MatrixSyncService.appInForeground = call.getBoolean("foreground", false) ?: false
        call.resolve()
    }

    /** Returns whether the sync service is currently running. */
    @PluginMethod
    fun getStatus(call: PluginCall) {
        val am = context.getSystemService(Context.ACTIVITY_SERVICE)
            as android.app.ActivityManager
        @Suppress("DEPRECATION")
        val running = am.getRunningServices(Int.MAX_VALUE)
            .any { it.service.className == MatrixSyncService::class.java.name }
        val result = UnifiedPushManager.getStatus(context).apply { put("running", running) }
        call.resolve(result)
    }

    /**
     * Clears the saved UnifiedPush distributor and opens the OS default-distributor
     * picker (`unifiedpush://link`), then re-registers on success.
     */
    @PluginMethod
    fun requestDistributorSetup(call: PluginCall) {
        val currentActivity = activity
            ?: return call.reject("Activity required to select a UnifiedPush distributor")
        UnifiedPushManager.requestDistributorSetup(context, currentActivity) { success ->
            call.resolve(JSObject().put("success", success))
        }
    }

    /**
     * Resolves the Matrix push gateway for a UnifiedPush endpoint using native HTTP
     * so discovery is not blocked by missing CORS headers in the WebView.
     */
    @PluginMethod
    fun resolveUnifiedPushGateway(call: PluginCall) {
        val endpoint = call.getString("endpoint")
            ?: return call.reject("endpoint required")
        Thread {
            val gateway = UnifiedPushManager.resolveMatrixGateway(endpoint)
            bridge?.executeOnMainThread {
                call.resolve(JSObject().put("gatewayUrl", gateway))
            }
        }.start()
    }

    /**
     * Cancels native tray notifications for a Matrix room after it has been marked as read.
     */
    @PluginMethod
    fun clearRoomNotifications(call: PluginCall) {
        val roomId = call.getString("roomId")
            ?: return call.reject("roomId required")
        MatrixSyncService.clearRoomNotifications(context, roomId)
        call.resolve(JSObject().put("cleared", true))
    }

    /**
     * Persists room → space/group metadata from the JS layer so background
     * notifications can nest under Direct messages / Space name / Home.
     */
    @PluginMethod
    fun setNotificationGroups(call: PluginCall) {
        val rooms = call.getObject("rooms")
            ?: return call.reject("rooms required")
        context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit()
            .putString(MatrixSyncService.KEY_NOTIFICATION_GROUPS, rooms.toString())
            .apply()
        call.resolve(JSObject().put("success", true))
    }

    /**
     * Shows a message notification from the JS layer (supports dynamic avatar icons).
     * Capacitor LocalNotifications cannot load authenticated/dynamic large icons, so
     * we post through the same native NotificationManager path as background sync.
     */
    @PluginMethod
    fun showNotification(call: PluginCall) {
        val roomId = call.getString("roomId") ?: return call.reject("roomId required")
        val groupId = call.getString("groupId") ?: "paarrot_home"
        val groupName = call.getString("groupName") ?: "Home"
        val kind = call.getString("kind") ?: "home"
        val senderName = call.getString("senderName")
            ?: call.getString("title")
            ?: "Someone"
        val messageText = call.getString("messageText")
            ?: call.getString("body")
            ?: "New message"
        val conversationTitle = call.getString("conversationTitle")
        val path = call.getString("path")
        val largeIconBase64 = call.getString("largeIconBase64")
        val bigPictureBase64 = call.getString("bigPictureBase64")

        MatrixSyncService.postMessageNotification(
            context = context,
            roomId = roomId,
            senderName = senderName,
            messageText = messageText,
            conversationTitle = conversationTitle,
            groupId = groupId,
            groupName = groupName,
            kind = kind,
            largeIcon = MatrixSyncService.decodeBase64Bitmap(largeIconBase64),
            inlineImage = MatrixSyncService.decodeBase64Bitmap(bigPictureBase64),
            path = path,
        )
        call.resolve(JSObject().put("shown", true))
    }

    companion object {
        const val PREFS = "sync_service_prefs"
    }
}
