package me.khodak.claudeusage

import android.app.AlarmManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import androidx.work.*
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.khodak.claudeusage.data.PreferencesManager
import java.util.concurrent.TimeUnit

class UsageUpdateWorker(
    private val context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        val prefs = PreferencesManager(context)
        if (!prefs.isLoggedIn()) return Result.success()

        prefs.markTodayActive()

        coroutineScope {
            val animJob = launch { rotateRefreshIcon() }
            try {
                val data = UsageRepository(prefs).fetchUsage()
                // Preserve last-good data so a failed/partial fetch never blanks the widget.
                prefs.saveUsageData(data.mergedWith(prefs.getUsageData()))
                prefs.recordHistory(data)           // history records only fresh readings
                Notifier.checkAndNotify(context, prefs, data)
            } catch (_: Exception) {}
            animJob.cancel()
            animJob.join()
        }

        pushWidgetUpdate()
        return Result.success()
    }

    private suspend fun rotateRefreshIcon() {
        val manager = AppWidgetManager.getInstance(context)
        val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java))
        val startMs = System.currentTimeMillis()
        val msPerRotation = 800L // one full rotation every 0.8 seconds

        fun angleAt(now: Long) = ((now - startMs) % msPerRotation) * 360f / msPerRotation

        try {
            while (true) {
                ClaudeUsageWidget.currentRotation = angleAt(System.currentTimeMillis())
                ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }
                delay(16) // aim for ~60fps; IPC speed sets the real ceiling
            }
        } finally {
            // Finish the current rotation cleanly — run until at least one full spin
            withContext(NonCancellable) {
                val minEndMs = startMs + msPerRotation
                while (System.currentTimeMillis() < minEndMs) {
                    ClaudeUsageWidget.currentRotation = angleAt(System.currentTimeMillis())
                    ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }
                    delay(16)
                }
            }
        }
    }

    private fun pushWidgetUpdate() {
        ClaudeUsageWidget.isRefreshing = false
        ClaudeUsageWidget.currentRotation = 0f
        val manager = AppWidgetManager.getInstance(context)
        val ids = manager.getAppWidgetIds(ComponentName(context, ClaudeUsageWidget::class.java))
        ids.forEach { id -> ClaudeUsageWidget.updateWidget(context, manager, id) }
    }

    companion object {
        private const val WORK_ONE_SHOT = "claude_oneshot"
        private const val WORK_PERIODIC = "claude_periodic"
        private const val ALARM_CODE = 1001
        private const val INTERVAL_MS = 5 * 60 * 1000L

        fun schedulePeriodicRefresh(context: Context) {
            // 5-min alarm for fast updates when the device is active/awake
            val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            am.setAndAllowWhileIdle(
                AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + INTERVAL_MS,
                alarmIntent(context)
            )
            // WorkManager periodic as a Doze/background backup (Android 16 reliability).
            // WorkManager uses JobScheduler which survives Doze; AlarmManager may be batched
            // up to 75 min in Doze mode. KEEP = don't reset the 15-min timer on every alarm.
            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
                WORK_PERIODIC,
                ExistingPeriodicWorkPolicy.KEEP,
                PeriodicWorkRequestBuilder<UsageUpdateWorker>(15, TimeUnit.MINUTES)
                    .setConstraints(
                        Constraints.Builder()
                            .setRequiredNetworkType(NetworkType.CONNECTED)
                            .build()
                    )
                    .build()
            )
        }

        fun cancelPeriodicRefresh(context: Context) {
            (context.getSystemService(Context.ALARM_SERVICE) as AlarmManager)
                .cancel(alarmIntent(context))
            WorkManager.getInstance(context).cancelUniqueWork(WORK_PERIODIC)
        }

        fun triggerImmediateRefresh(context: Context) {
            WorkManager.getInstance(context).enqueueUniqueWork(
                WORK_ONE_SHOT,
                ExistingWorkPolicy.REPLACE,
                OneTimeWorkRequestBuilder<UsageUpdateWorker>().build()
            )
        }

        private fun alarmIntent(context: Context) = PendingIntent.getBroadcast(
            context, ALARM_CODE,
            Intent(context, AlarmReceiver::class.java),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    }
}
