package de.ladkau.deckinadash.notify

import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import de.ladkau.deckinadash.MainActivity
import de.ladkau.deckinadash.R
import de.ladkau.deckinadash.data.ConfigRepository
import de.ladkau.deckinadash.data.ReadingDto
import de.ladkau.deckinadash.data.ReadingRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import java.time.Instant

private const val CHANNEL_ID = "daily_reading"
private const val NOTIFICATION_ID = 1

/**
 * Fires once (scheduled by NotificationScheduler.reschedule()), computes
 * today's reading via the same ReadingRepository the UI uses (no
 * duplicated reading logic - see that class's own comment), posts a
 * "quick digest" notification from the day's significance + top
 * transit (the same lead-in watch/src/c/ui_report_window.c's own
 * comment describes the watch's wakeup-vibrate leading into), then
 * reschedules itself for tomorrow - WorkManager has no "repeat forever
 * at an exact wall-clock time" primitive, so this self-chaining step is
 * how notify_reschedule()'s own daily-repeat contract is reproduced.
 */
class ReadingNotificationWorker(
    appContext: Context,
    params: WorkerParameters,
) : CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        val configRepository = ConfigRepository(applicationContext)
        val config = configRepository.config.first()

        // Mirrors notify_reschedule()'s own "if disabled, only cancel" branch -
        // a defensive no-op, since reschedule() should already have cancelled
        // this work item when notifications were turned off.
        if (!config.notifyEnabled || !config.configured) {
            return Result.success()
        }

        val reading = try {
            withContext(Dispatchers.Default) {
                ReadingRepository(applicationContext).compute(config, Instant.now().epochSecond)
            }
        } catch (t: Throwable) {
            NotificationScheduler.reschedule(applicationContext, config)
            return Result.failure()
        }

        postNotification(reading)
        NotificationScheduler.reschedule(applicationContext, config)
        return Result.success()
    }

    private fun postNotification(reading: ReadingDto) {
        ensureChannel()

        val body = reading.transits.firstOrNull()?.let { "${it.title}. ${it.narrative}" }
            ?: reading.guidance

        val openAppIntent = PendingIntent.getActivity(
            applicationContext,
            0,
            Intent(applicationContext, MainActivity::class.java),
            PendingIntent.FLAG_IMMUTABLE,
        )

        val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(
                applicationContext.getString(R.string.notification_title, reading.daySignificance.levelLabel),
            )
            .setContentText(body)
            .setStyle(NotificationCompat.BigTextStyle().bigText(body))
            .setAutoCancel(true)
            .setContentIntent(openAppIntent)
            .build()

        if (ActivityCompat.checkSelfPermission(
                applicationContext,
                Manifest.permission.POST_NOTIFICATIONS,
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        NotificationManagerCompat.from(applicationContext).notify(NOTIFICATION_ID, notification)
    }

    private fun ensureChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
        val manager = applicationContext.getSystemService(NotificationManager::class.java)
        val channel = NotificationChannel(
            CHANNEL_ID,
            applicationContext.getString(R.string.notification_channel_name),
            NotificationManager.IMPORTANCE_DEFAULT,
        ).apply {
            description = applicationContext.getString(R.string.notification_channel_description)
        }
        manager.createNotificationChannel(channel)
    }
}
