package il.co.organicai.agent

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject

/**
 * Keeps the agent alive in the background: heartbeat every 30s, dispatch any
 * commands the worker returns, report results. This is the mobile equivalent of
 * the extension's service-worker alarm loop.
 *
 * When FCM is wired (PushService), commands arrive in ~1s and this loop becomes
 * the fallback heartbeat rather than the primary latency path (P2).
 */
class AgentService : Service() {

    private val scope = CoroutineScope(Dispatchers.IO + Job())
    private lateinit var worker: WorkerClient
    private lateinit var agent: AgentWebView
    private var started = false

    override fun onCreate() {
        super.onCreate()
        worker = WorkerClient(token = SessionStore.token(this))
        // The WebView must be built on the main thread.
        agent = AgentWebView(this)
        startForeground(1, ongoingNotification())
    }

    // START_STICKY: if Android kills us for memory, restart the agent. Guard so a
    // redelivered start intent doesn't spin up a second loop.
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (SessionStore.token(this) == null) { stopSelf(); return START_NOT_STICKY }
        if (!started) { started = true; loop() }
        return START_STICKY
    }

    private fun loop() = scope.launch {
        while (isActive) {
            runCatching {
                val commands = worker.heartbeat()
                for (i in 0 until commands.length()) dispatch(commands.getJSONObject(i))
            }
            delay(30_000)
        }
    }

    /** Map a device command to a WebView primitive and report the outcome. */
    private suspend fun dispatch(cmd: JSONObject) {
        val id = cmd.optString("id")
        val type = cmd.optString("type")
        val p = cmd.optJSONObject("payload") ?: JSONObject()
        try {
            val result: JSONObject = when (type) {
                "BROWSER_NAVIGATE" -> onMain { agent.navigate(p.getString("url")) }
                "BROWSER_EXTRACT"  -> onMain { agent.extract(p.optInt("maxElements", 60)) }
                "BROWSER_CLICK"    -> onMain { agent.click(p.opt("ref") as? Int, p.optStringOrNull("selector")) }
                "BROWSER_TYPE"     -> onMain { agent.type(p.opt("ref") as? Int, p.optStringOrNull("selector"), p.optString("text")) }
                "BROWSER_SCREENSHOT" -> {
                    val dataUrl = withContext(Dispatchers.Main) { agent.screenshot() }
                    JSONObject().put("ok", true).put("url", worker.uploadScreenshot(dataUrl))
                }
                "BROWSER_WAIT"     -> { delay(p.optLong("ms", 1000).coerceIn(100, 30_000)); JSONObject().put("ok", true) }
                "GET_STATUS", "COLLECT_DIAGNOSTICS" -> onMain { agent.status() }
                // High-level: navigate to the group (if given) then publish a text post.
                "PUBLISH_FACEBOOK" -> onMain {
                    p.optStringOrNull("groupUrl")?.let { agent.navigate(it) }
                    agent.publishFacebook(p.optString("text"))
                }
                // LinkedIn / X feed post. payload: { platform, text, feedUrl? }.
                "PUBLISH_FEED" -> onMain {
                    p.optStringOrNull("feedUrl")?.let { agent.navigate(it) }
                    agent.publishFeed(p.optString("platform"), p.optString("text"))
                }
                "PING"             -> JSONObject().put("pong", true)
                else               -> JSONObject().put("ok", false).put("error", "unsupported: $type")
            }
            worker.reportResult(id, result.optBoolean("ok", true), result, null)
        } catch (e: Exception) {
            worker.reportResult(id, false, null, e.message ?: "error")
        }
    }

    private suspend fun <T> onMain(block: suspend () -> T): T = withContext(Dispatchers.Main) { block() }

    private fun ongoingNotification(): Notification {
        val ch = "organicai-agent"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val nm = getSystemService(NotificationManager::class.java)
            nm.createNotificationChannel(NotificationChannel(ch, "OrganicAI Agent", NotificationManager.IMPORTANCE_LOW))
        }
        return Notification.Builder(this, ch)
            .setContentTitle("OrganicAI")
            .setContentText("הסוכן פעיל")
            .setSmallIcon(android.R.drawable.stat_notify_sync)
            .setOngoing(true)
            .build()
    }

    override fun onBind(intent: Intent?): IBinder? = null
    override fun onDestroy() { scope.coroutineContext[Job]?.cancel(); super.onDestroy() }
}
