package me.khodak.claudeusage

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.SizeF
import android.widget.RemoteViews
import me.khodak.claudeusage.data.PreferencesManager
import me.khodak.claudeusage.data.UsageData
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit

class ClaudeUsageWidget : AppWidgetProvider() {

    override fun onUpdate(context: Context, manager: AppWidgetManager, ids: IntArray) {
        ids.forEach { updateWidget(context, manager, it) }
        if (PreferencesManager(context).isLoggedIn()) {
            UsageUpdateWorker.schedulePeriodicRefresh(context)
            UsageUpdateWorker.triggerImmediateRefresh(context)
        }
    }

    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)
        if (intent.action == ACTION_REFRESH) {
            val manager = AppWidgetManager.getInstance(context)
            val ids = manager.getAppWidgetIds(
                android.content.ComponentName(context, ClaudeUsageWidget::class.java)
            )
            isRefreshing = true
            ids.forEach { updateWidget(context, manager, it) }
            UsageUpdateWorker.triggerImmediateRefresh(context)
        }
    }

    companion object {
        const val ACTION_REFRESH = "me.khodak.claudeusage.ACTION_REFRESH"
        @Volatile internal var isRefreshing = false
        @Volatile internal var currentRotation = 0f

        fun updateWidget(context: Context, manager: AppWidgetManager, widgetId: Int) {
            val prefs = PreferencesManager(context)
            val apiData = prefs.getUsageData()
            val views = responsiveViews(context, prefs, apiData, widgetId)
            manager.updateAppWidget(widgetId, views)
        }

        fun updateWidget(context: Context, manager: AppWidgetManager, widgetId: Int, data: UsageData?) {
            val prefs = PreferencesManager(context)
            val views = responsiveViews(context, prefs, data, widgetId)
            manager.updateAppWidget(widgetId, views)
        }

        private fun responsiveViews(
            context: Context, prefs: PreferencesManager, apiData: UsageData?, widgetId: Int
        ): RemoteViews {
            val refreshPi = PendingIntent.getBroadcast(
                context, widgetId,
                Intent(context, ClaudeUsageWidget::class.java).apply { action = ACTION_REFRESH },
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )
            val openPi = PendingIntent.getActivity(
                context, widgetId + 1000,
                Intent(context, MainActivity::class.java),
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            fun attach(v: RemoteViews): RemoteViews {
                v.setOnClickPendingIntent(R.id.btn_refresh, refreshPi)
                v.setOnClickPendingIntent(R.id.widget_root, openPi)
                return v
            }

            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                RemoteViews(mapOf(
                    SizeF(50f, 50f)  to attach(buildSmallViews(context, prefs, apiData)),
                    SizeF(50f, 120f) to attach(buildViews(context, prefs, apiData))
                ))
            } else {
                attach(buildViews(context, prefs, apiData))
            }
        }

        private fun buildSmallViews(context: Context, prefs: PreferencesManager, apiData: UsageData?): RemoteViews {
            val v = RemoteViews(context.packageName, R.layout.widget_layout_small)
            if (!prefs.isLoggedIn()) {
                v.setTextViewText(R.id.tv_session_value, "—")
                v.setTextViewText(R.id.tv_session_label, "Not signed in")
                v.setTextViewText(R.id.tv_weekly_value, "—")
                v.setTextViewText(R.id.tv_weekly_label, "")
                v.setTextViewText(R.id.tv_status, "")
                v.setProgressBar(R.id.progress_bar, 100, 0, false)
                v.setProgressBar(R.id.progress_bar_weekly, 100, 0, false)
                return v
            }
            val hasUtilization = apiData != null && apiData.fiveHourUtilization >= 0f
            val hasApiMessages = apiData != null && apiData.effectiveRemaining >= 0 && apiData.messagesLimit > 0
            val sessionStart = prefs.getSessionStart()
            when {
                hasUtilization -> {
                    val pct = apiData!!.fiveHourUtilization.toInt()
                    v.setTextViewText(R.id.tv_session_value, "$pct%")
                    v.setProgressBar(R.id.progress_bar, 100, pct, false)
                    v.setTextViewText(R.id.tv_session_label, formatReset(apiData.utilizationResetAtEpoch))
                }
                hasApiMessages -> {
                    val rem = apiData!!.effectiveRemaining
                    val lim = apiData.messagesLimit
                    v.setTextViewText(R.id.tv_session_value, "$rem/$lim")
                    v.setProgressBar(R.id.progress_bar, 100, apiData.progressPercent, false)
                    v.setTextViewText(R.id.tv_session_label, formatReset(apiData.resetAtEpoch))
                }
                sessionStart > 0 -> {
                    val elapsedMs = System.currentTimeMillis() - sessionStart
                    val h = TimeUnit.MILLISECONDS.toHours(elapsedMs)
                    val m = TimeUnit.MILLISECONDS.toMinutes(elapsedMs) % 60
                    v.setTextViewText(R.id.tv_session_value, if (h > 0) "${h}h${m}m" else "${m}m")
                    v.setProgressBar(R.id.progress_bar, 100, 0, false)
                    v.setTextViewText(R.id.tv_session_label, "active")
                }
                else -> {
                    v.setTextViewText(R.id.tv_session_value, "—")
                    v.setProgressBar(R.id.progress_bar, 100, 0, false)
                    v.setTextViewText(R.id.tv_session_label, "")
                }
            }
            val hasWeekly = apiData != null && apiData.weeklyUtilization >= 0f
            if (hasWeekly) {
                val wPct = apiData!!.weeklyUtilization.toInt()
                v.setTextViewText(R.id.tv_weekly_value, "$wPct%")
                v.setProgressBar(R.id.progress_bar_weekly, 100, wPct, false)
                v.setTextViewText(R.id.tv_weekly_label, formatReset(apiData.weeklyResetAtEpoch))
            } else {
                val weeklyDays = Integer.bitCount(prefs.getWeeklyMask())
                v.setTextViewText(R.id.tv_weekly_value, "${weeklyDays}d")
                v.setProgressBar(R.id.progress_bar_weekly, 100, 0, false)
                v.setTextViewText(R.id.tv_weekly_label, "")
            }
            val resetEpoch = apiData?.effectiveResetEpoch ?: -1L
            val status = when {
                isRefreshing -> "Refreshing…"
                apiData?.isRateLimited == true -> "Rate limited · ${formatReset(resetEpoch)}"
                else -> ""
            }
            val updatedMs = apiData?.lastUpdated ?: 0L
            v.setTextViewText(R.id.tv_status,
                if (status.isNotBlank()) status else if (updatedMs > 0) formatTime(updatedMs) else "")
            v.setInt(R.id.btn_refresh, "setColorFilter",
                if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt())
            v.setFloat(R.id.btn_refresh, "setRotation", currentRotation)
            return v
        }

        private fun buildViews(context: Context, prefs: PreferencesManager, apiData: UsageData?): RemoteViews {
            val v = RemoteViews(context.packageName, R.layout.widget_layout)

            // ── Not logged in ────────────────────────────────────────────────
            if (!prefs.isLoggedIn()) {
                v.setTextViewText(R.id.tv_session_value, "—")
                v.setTextViewText(R.id.tv_session_label, "Not signed in")
                v.setTextViewText(R.id.tv_weekly_value, "—")
                v.setTextViewText(R.id.tv_weekly_label, "Open app to sign in")
                v.setTextViewText(R.id.tv_status, "")
                v.setProgressBar(R.id.progress_bar, 100, 0, false)
                return v
            }

            // ── 5-hour window bar ────────────────────────────────────────────
            val hasUtilization = apiData != null && apiData.fiveHourUtilization >= 0f
            val hasApiMessages = apiData != null && apiData.effectiveRemaining >= 0 && apiData.messagesLimit > 0
            val sessionStart = prefs.getSessionStart()

            when {
                hasUtilization -> {
                    val pct = apiData!!.fiveHourUtilization.toInt()
                    v.setTextViewText(R.id.tv_session_value, "$pct%")
                    v.setProgressBar(R.id.progress_bar, 100, pct, false)
                    v.setTextViewText(R.id.tv_session_label, formatReset(apiData.utilizationResetAtEpoch))
                }
                hasApiMessages -> {
                    val rem = apiData!!.effectiveRemaining
                    val lim = apiData.messagesLimit
                    v.setTextViewText(R.id.tv_session_value, "$rem / $lim")
                    v.setProgressBar(R.id.progress_bar, 100, apiData.progressPercent, false)
                    v.setTextViewText(R.id.tv_session_label, formatReset(apiData.resetAtEpoch))
                }
                sessionStart > 0 -> {
                    val elapsedMs = System.currentTimeMillis() - sessionStart
                    val h = TimeUnit.MILLISECONDS.toHours(elapsedMs)
                    val m = TimeUnit.MILLISECONDS.toMinutes(elapsedMs) % 60
                    val display = if (h > 0) "${h}h ${m}m" else if (m > 0) "${m}m" else "Just started"
                    v.setTextViewText(R.id.tv_session_value, display)
                    v.setProgressBar(R.id.progress_bar, 100, 0, false)
                    v.setTextViewText(R.id.tv_session_label, "session active")
                }
                else -> {
                    v.setTextViewText(R.id.tv_session_value, "—")
                    v.setProgressBar(R.id.progress_bar, 100, 0, false)
                    v.setTextViewText(R.id.tv_session_label, "")
                }
            }

            // ── 7-day usage bar ──────────────────────────────────────────────
            val hasWeekly = apiData != null && apiData.weeklyUtilization >= 0f
            if (hasWeekly) {
                val wPct = apiData!!.weeklyUtilization.toInt()
                v.setTextViewText(R.id.tv_weekly_value, "$wPct%")
                v.setProgressBar(R.id.progress_bar_weekly, 100, wPct, false)
                v.setTextViewText(R.id.tv_weekly_label, formatReset(apiData.weeklyResetAtEpoch))
            } else {
                val weeklyDays = Integer.bitCount(prefs.getWeeklyMask())
                v.setTextViewText(R.id.tv_weekly_value, "$weeklyDays d")
                v.setProgressBar(R.id.progress_bar_weekly, 100, 0, false)
                v.setTextViewText(R.id.tv_weekly_label, "active this week")
            }

            // ── Footer ───────────────────────────────────────────────────────
            val resetEpoch = apiData?.effectiveResetEpoch ?: -1L
            val status = when {
                isRefreshing -> "Refreshing…"
                apiData?.isRateLimited == true -> "Rate limited · ${formatReset(resetEpoch)}"
                else -> ""
            }
            val updatedMs = apiData?.lastUpdated ?: 0L
            v.setTextViewText(R.id.tv_status,
                if (status.isNotBlank()) status
                else if (updatedMs > 0) formatTime(updatedMs)
                else ""
            )
            v.setInt(R.id.btn_refresh, "setColorFilter",
                if (isRefreshing) 0xFFCC785C.toInt() else 0xFF999999.toInt())
            v.setFloat(R.id.btn_refresh, "setRotation", currentRotation)

            return v
        }

        private fun formatReset(epochMs: Long): String {
            if (epochMs <= 0) return ""
            val now = System.currentTimeMillis()
            if (epochMs <= now) return "Resets soon"
            val diffH = TimeUnit.MILLISECONDS.toHours(epochMs - now)
            val timeStr = SimpleDateFormat("h:mm a", Locale.US).format(Date(epochMs))
            return when {
                diffH < 24 -> "Resets at $timeStr"
                diffH < 48 -> "Resets tomorrow $timeStr"
                else -> "Resets ${SimpleDateFormat("EEE", Locale.US).format(Date(epochMs))} $timeStr"
            }
        }

        private fun formatTime(ms: Long) =
            SimpleDateFormat("h:mm a", Locale.US).format(Date(ms))
    }
}
