package com.agentui.desktop

import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.util.concurrent.TimeUnit

class TerminalListWidgetService : RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
        return TerminalListFactory(applicationContext)
    }
}

data class TerminalItem(
    val ephemeralSessionId: String,
    val agent: String,
    val label: String,
    val status: String,
    val alive: Boolean,
    val hookBadges: String,
    val lastUserPrompt: String,
    val statusColor: Int,
    val terminalIndex: Int // 1-based, maps to /transcript-debug/:terminalIndex
)

class TerminalListFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory {

    companion object {
        private const val TAG = "WidgetListFactory"

        // Refresh button states
        private const val ICON_NORMAL = "\u21BB"   // ↻
        private const val ICON_LOADING = "\u23F3"  // ⏳
        private const val ICON_OK = "\u2713"       // ✓
        private const val ICON_ERROR = "\u26A0"    // ⚠

        private const val COLOR_NORMAL = 0xFF8888FF.toInt()
        private const val COLOR_LOADING = 0xFF60a5fa.toInt()
        private const val COLOR_OK = 0xFF4ade80.toInt()
        private const val COLOR_ERROR = 0xFFf87171.toInt()

        private val client = OkHttpClient.Builder()
            .connectTimeout(8, TimeUnit.SECONDS)
            .readTimeout(8, TimeUnit.SECONDS)
            .build()

        private val STATUS_COLORS = mapOf(
            "idle" to 0xFF6b7280.toInt(),
            "thinking" to 0xFF60a5fa.toInt(),
            "reading" to 0xFF22d3ee.toInt(),
            "writing" to 0xFF4ade80.toInt(),
            "toolUse" to 0xFFfbbf24.toInt(),
            "permissionRequest" to 0xFFfb923c.toInt(),
            "interrupted" to 0xFFf87171.toInt(),
            "error" to 0xFFf87171.toInt(),
            "sessionStart" to 0xFF60a5fa.toInt(),
            "sessionEnd" to 0xFF6b7280.toInt()
        )

        private val TOOL_EVENTS = setOf("PreToolUse", "PostToolUse", "PostToolUseFailure")
        private val PERM_EVENTS = setOf("PermissionRequest")
        private val SESSION_EVENTS = setOf("SessionStart", "UserPromptSubmit", "Stop", "SessionEnd")
    }

    private var items = listOf<TerminalItem>()
    private val mainHandler = Handler(Looper.getMainLooper())

    override fun onCreate() {}

    override fun onDataSetChanged() {
        setRefreshButton(ICON_LOADING, COLOR_LOADING)

        val result = fetchTerminals()

        if (result == null) {
            // Network error — keep previous items, show error on button
            setRefreshButton(ICON_ERROR, COLOR_ERROR)
            scheduleResetButton(3000)
        } else {
            items = result
            // Brief success flash, then back to normal
            setRefreshButton(ICON_OK, COLOR_OK)
            scheduleResetButton(1500)
        }
    }

    override fun onDestroy() {
        items = emptyList()
    }

    override fun getCount(): Int = items.size

    override fun getViewAt(position: Int): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.widget_terminal_item)

        if (position >= items.size) return views
        val item = items[position]

        views.setTextColor(R.id.item_dot, item.statusColor)

        val statusLabel = if (item.alive) item.status else "closed"
        views.setTextViewText(R.id.item_name, "T${item.terminalIndex} ${item.agent}  $statusLabel")

        views.setTextViewText(R.id.item_badges, item.hookBadges)

        // Always show the registry label (unique per terminal)
        views.setTextViewText(R.id.item_label, item.label)

        val fillIntent = Intent().apply {
            putExtra("terminalIndex", item.terminalIndex)
            putExtra("agent", item.agent)
        }
        views.setOnClickFillInIntent(R.id.item_root, fillIntent)

        return views
    }

    override fun getLoadingView(): RemoteViews? = null
    override fun getViewTypeCount(): Int = 1
    override fun getItemId(position: Int): Long = position.toLong()
    override fun hasStableIds(): Boolean = false

    // ── Refresh button state management ──

    /**
     * Update just the refresh button via partiallyUpdateAppWidget.
     * This doesn't reset the ListView adapter.
     */
    private fun setRefreshButton(icon: String, color: Int) {
        try {
            val views = RemoteViews(context.packageName, R.layout.widget_transcript)
            views.setTextViewText(R.id.btn_refresh, icon)
            views.setTextColor(R.id.btn_refresh, color)

            val mgr = AppWidgetManager.getInstance(context)
            val ids = mgr.getAppWidgetIds(
                ComponentName(context, TranscriptWidgetProvider::class.java)
            )
            mgr.partiallyUpdateAppWidget(ids, views)
        } catch (e: Exception) {
            Log.w(TAG, "Failed to update refresh button", e)
        }
    }

    /**
     * Schedule resetting the button back to normal after a delay.
     */
    private fun scheduleResetButton(delayMs: Long) {
        mainHandler.postDelayed({
            setRefreshButton(ICON_NORMAL, COLOR_NORMAL)
        }, delayMs)
    }

    // ── Data fetching ──

    /**
     * Returns list on success, null on error.
     * Items keep the same order as the terminal registry (T1, T2, T3...)
     * so the index maps directly to /transcript-debug/:terminalIndex
     */
    private fun fetchTerminals(): List<TerminalItem>? {
        val apiBase = ServerConfig.apiBaseUrl(context) ?: return emptyList()

        try {
            val url = "$apiBase/session-state"
            val req = Request.Builder().url(url).build()
            val resp = client.newCall(req).execute()
            if (!resp.isSuccessful) return null

            val json = JSONObject(resp.body?.string() ?: "{}")
            val registry = json.optJSONArray("registry")
            val agents = json.optJSONObject("agents")

            if (registry == null || registry.length() == 0) return emptyList()

            val result = mutableListOf<TerminalItem>()

            for (i in 0 until registry.length()) {
                val entry = registry.getJSONObject(i)
                val agentName = entry.optString("agent", "")
                val ephId = entry.optString("ephemeralSessionId", "")
                val label = entry.optString("label", "")
                val alive = entry.optBoolean("alive", false)
                val terminalIndex = i + 1 // 1-based, maps to /transcript-debug/:terminalIndex

                val agentState = agents?.optJSONObject(agentName)

                val status = agentState?.optString("status", if (alive) "idle" else "closed")
                    ?: if (alive) "idle" else "closed"
                val statusColor = STATUS_COLORS[status] ?: STATUS_COLORS["idle"]!!

                val lastUserPrompt = if (agentState != null) extractLastUserPrompt(agentState) else ""
                val hookBadges = if (agentState != null) buildBadgeString(agentState) else ""

                result.add(
                    TerminalItem(
                        ephemeralSessionId = ephId,
                        agent = agentName,
                        label = label,
                        status = status,
                        alive = alive,
                        hookBadges = hookBadges,
                        lastUserPrompt = lastUserPrompt,
                        statusColor = statusColor,
                        terminalIndex = terminalIndex
                    )
                )
            }

            // Keep registry order (don't sort) — index must match T1, T2, T3...
            return result

        } catch (e: Exception) {
            Log.w(TAG, "Failed to fetch terminals", e)
            return null
        }
    }

    private fun extractLastUserPrompt(state: JSONObject): String {
        val history = state.optJSONArray("hookHistory") ?: return ""
        for (i in history.length() - 1 downTo 0) {
            val entry = history.optJSONObject(i) ?: continue
            if (entry.optString("event") == "UserPromptSubmit") {
                val detail = entry.optString("detail", "")
                if (detail.isNotEmpty()) return detail.take(120)
            }
        }
        val stopResp = state.optString("lastStopResponse", "")
        if (stopResp.isNotEmpty()) return "< ${stopResp.take(100)}"
        return ""
    }

    private fun buildBadgeString(state: JSONObject): String {
        val history = state.optJSONArray("hookHistory") ?: return ""
        var tools = 0; var perms = 0; var sessions = 0
        for (i in 0 until history.length()) {
            val entry = history.optJSONObject(i) ?: continue
            val event = entry.optString("event", "")
            when {
                event in TOOL_EVENTS -> tools++
                event in PERM_EVENTS -> perms++
                event in SESSION_EVENTS -> sessions++
            }
        }
        val parts = mutableListOf<String>()
        if (tools > 0) parts.add("T:$tools")
        if (perms > 0) parts.add("P:$perms")
        if (sessions > 0) parts.add("S:$sessions")
        return parts.joinToString(" ")
    }
}
