package com.atridad.ascently.service

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.atridad.ascently.MainActivity
import com.atridad.ascently.R
import com.atridad.ascently.data.database.AscentlyDatabase
import com.atridad.ascently.data.repository.ClimbRepository
import com.atridad.ascently.utils.AppLogger
import com.atridad.ascently.utils.DateFormatUtils
import com.atridad.ascently.widget.ClimbStatsWidgetProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.temporal.ChronoUnit

class SessionTrackingService : Service() {

    private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    private var notificationJob: Job? = null
    private var monitoringJob: Job? = null

    private lateinit var repository: ClimbRepository
    private lateinit var notificationManager: NotificationManager

    companion object {
        private const val LOG_TAG = "SessionTrackingService"
        const val NOTIFICATION_ID = 1001
        const val CHANNEL_ID = "session_tracking_channel"
        const val ACTION_START_SESSION = "start_session"
        const val ACTION_STOP_SESSION = "stop_session"
        const val EXTRA_SESSION_ID = "session_id"

        fun createStartIntent(context: Context, sessionId: String): Intent {
            return Intent(context, SessionTrackingService::class.java).apply {
                action = ACTION_START_SESSION
                putExtra(EXTRA_SESSION_ID, sessionId)
            }
        }

        fun createStopIntent(context: Context, sessionId: String): Intent {
            return Intent(context, SessionTrackingService::class.java).apply {
                action = ACTION_STOP_SESSION
                putExtra(EXTRA_SESSION_ID, sessionId)
            }
        }
    }

    override fun onCreate() {
        super.onCreate()

        val database = AscentlyDatabase.getDatabase(this)
        repository = ClimbRepository(database, this)
        notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            ACTION_START_SESSION -> {
                val sessionId = intent.getStringExtra(EXTRA_SESSION_ID)
                if (sessionId != null) {
                    startSessionTracking(sessionId)
                }
            }

            ACTION_STOP_SESSION -> {
                val sessionId = intent.getStringExtra(EXTRA_SESSION_ID)
                serviceScope.launch {
                    try {
                        val targetSession =
                            when {
                                sessionId != null -> repository.getSessionById(sessionId)
                                else -> repository.getActiveSession()
                            }
                        if (targetSession != null &&
                            targetSession.status ==
                            com.atridad.ascently.data.model.SessionStatus.ACTIVE
                        ) {
                            val completed =
                                with(com.atridad.ascently.data.model.ClimbSession) {
                                    targetSession.complete()
                                }
                            repository.updateSession(completed)
                        }
                    } finally {
                        stopSessionTracking()
                    }
                }
            }
        }

        return START_REDELIVER_INTENT
    }

    override fun onBind(intent: Intent?): IBinder? = null

    private fun startSessionTracking(sessionId: String) {
        notificationJob?.cancel()
        monitoringJob?.cancel()

        try {
            createAndShowNotification(sessionId)
            // Update widget when session tracking starts
            ClimbStatsWidgetProvider.updateAllWidgets(this)
        } catch (e: Exception) {
            AppLogger.e(LOG_TAG, e) { "Failed to initialize session tracking notification" }
        }

        notificationJob =
            serviceScope.launch {
                try {
                    if (!isNotificationActive()) {
                        delay(1000L)
                        createAndShowNotification(sessionId)
                    }

                    while (isActive) {
                        delay(5000L)
                        updateNotification(sessionId)
                    }
                } catch (e: Exception) {
                    AppLogger.e(LOG_TAG, e) { "Notification updater loop crashed" }
                }
            }

        monitoringJob =
            serviceScope.launch {
                try {
                    while (isActive) {
                        delay(10000L)

                        if (!isNotificationActive()) {
                            updateNotification(sessionId)
                        }

                        val session = repository.getSessionById(sessionId)
                        if (session == null ||
                            session.status !=
                            com.atridad.ascently.data.model.SessionStatus
                                .ACTIVE
                        ) {
                            stopSessionTracking()
                            break
                        }
                    }
                } catch (e: Exception) {
                    AppLogger.e(LOG_TAG, e) { "Session monitoring loop crashed" }
                }
            }
    }

    private fun stopSessionTracking() {
        notificationJob?.cancel()
        monitoringJob?.cancel()
        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
        // Update widget when session tracking stops
        ClimbStatsWidgetProvider.updateAllWidgets(this)
    }

    private fun isNotificationActive(): Boolean {
        return try {
            val activeNotifications = notificationManager.activeNotifications
            activeNotifications.any { it.id == NOTIFICATION_ID }
        } catch (_: Exception) {
            false
        }
    }

    private suspend fun updateNotification(sessionId: String) {
        try {
            createAndShowNotification(sessionId)
            // Update widget when notification updates
            ClimbStatsWidgetProvider.updateAllWidgets(this)
        } catch (e: Exception) {
            AppLogger.e(LOG_TAG, e) { "Failed to update notification; retrying in 10s" }

            try {
                delay(10000L)
                createAndShowNotification(sessionId)
            } catch (retryException: Exception) {
                AppLogger.e(LOG_TAG, retryException) { "Retrying notification update failed" }
                stopSessionTracking()
            }
        }
    }

    private fun createAndShowNotification(sessionId: String) {
        try {
            val session = runBlocking { repository.getSessionById(sessionId) }
            if (session == null ||
                session.status != com.atridad.ascently.data.model.SessionStatus.ACTIVE
            ) {
                stopSessionTracking()
                return
            }

            val gym = runBlocking { repository.getGymById(session.gymId) }

            val attempts = runBlocking {
                repository.getAttemptsBySession(sessionId).firstOrNull() ?: emptyList()
            }

            val notificationBuilder =
                NotificationCompat.Builder(this, CHANNEL_ID)
                    .setSmallIcon(R.drawable.ic_mountains)
                    .setOngoing(true)
                    .setAutoCancel(false)
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE)
                    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                    .setContentIntent(createOpenAppIntent())
                    .addAction(
                        R.drawable.ic_mountains,
                        "Open Session",
                        createOpenAppIntent(),
                    )
                    .addAction(
                        android.R.drawable.ic_menu_close_clear_cancel,
                        "End Session",
                        createStopPendingIntent(sessionId),
                    )

            // Use Live Update
            if (Build.VERSION.SDK_INT >= 36) {
                val startTimeMillis =
                    session.startTime?.let { startTime ->
                        try {
                            DateFormatUtils.parseISO8601(startTime)?.toEpochMilli()
                                ?: System.currentTimeMillis()
                        } catch (_: Exception) {
                            System.currentTimeMillis()
                        }
                    }
                        ?: System.currentTimeMillis()

                notificationBuilder
                    .setContentTitle("Climbing Session Active")
                    .setContentText(
                        "${gym?.name ?: "Gym"} • ${attempts.size} attempts",
                    )
                    .setWhen(startTimeMillis)
                    .setUsesChronometer(true)
                    .setShowWhen(true)

                val extras = Bundle()
                extras.putBoolean("android.extra.REQUEST_PROMOTED_ONGOING", true)
                notificationBuilder.setExtras(extras)
            } else {
                // Fallback for older versions
                val duration =
                    session.startTime?.let { startTime ->
                        try {
                            val start = DateFormatUtils.parseISO8601(startTime)
                            val now = java.time.Instant.now()
                            val totalSeconds = if (start != null) ChronoUnit.SECONDS.between(start, now) else 0L
                            val hours = totalSeconds / 3600
                            val minutes = (totalSeconds % 3600) / 60
                            val seconds = totalSeconds % 60

                            when {
                                hours > 0 -> "${hours}h ${minutes}m ${seconds}s"
                                minutes > 0 -> "${minutes}m ${seconds}s"
                                else -> "${totalSeconds}s"
                            }
                        } catch (_: Exception) {
                            "Active"
                        }
                    }
                        ?: "Active"

                notificationBuilder
                    .setContentTitle("Climbing Session Active")
                    .setContentText(
                        "${gym?.name ?: "Gym"} • $duration • ${attempts.size} attempts",
                    )
            }

            val notification = notificationBuilder.build()

            startForeground(NOTIFICATION_ID, notification)

            notificationManager.notify(NOTIFICATION_ID, notification)
        } catch (e: Exception) {
            AppLogger.e(LOG_TAG, e) { "Failed to build session tracking notification" }
            throw e
        }
    }

    private fun createOpenAppIntent(): PendingIntent {
        val intent =
            Intent(this, MainActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                action = "OPEN_SESSION"
            }
        return PendingIntent.getActivity(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        )
    }

    private fun createStopPendingIntent(sessionId: String): PendingIntent {
        val intent = createStopIntent(this, sessionId)
        return PendingIntent.getService(
            this,
            1,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        )
    }

    private fun createNotificationChannel() {
        val channel =
            NotificationChannel(
                CHANNEL_ID,
                "Session Tracking",
                NotificationManager.IMPORTANCE_DEFAULT,
            )
                .apply {
                    description = "Shows active climbing session information"
                    setShowBadge(false)
                    lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
                    enableLights(false)
                    enableVibration(false)
                    setSound(null, null)
                }

        notificationManager.createNotificationChannel(channel)
    }

    override fun onDestroy() {
        super.onDestroy()
        notificationJob?.cancel()
        monitoringJob?.cancel()
        serviceScope.cancel()
    }
}
