package com.radiola.service

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import com.radiola.MainActivity
import com.radiola.data.local.dao.AlarmDao
import com.radiola.data.local.entity.AlarmEntity
import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.Calendar
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AlarmScheduler @Inject constructor(
    @ApplicationContext private val context: Context
) {
    private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

    /**
     * Запланировать следующее срабатывание будильника.
     * Если daysMask == 0 — разовый: берём ближайшее сегодня/завтра совпадение по времени.
     * Если daysMask != 0 — повторяющийся: ищем ближайший день недели из маски.
     */
    fun schedule(alarm: AlarmEntity) {
        val triggerMs = nextTriggerMillis(alarm)
        val operation = buildPendingIntent(alarm.id)
        val showIntent = PendingIntent.getActivity(
            context,
            0,
            Intent(context, MainActivity::class.java),
            PendingIntent.FLAG_IMMUTABLE
        )
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                if (alarmManager.canScheduleExactAlarms()) {
                    alarmManager.setAlarmClock(
                        AlarmManager.AlarmClockInfo(triggerMs, showIntent),
                        operation
                    )
                } else {
                    // Нет разрешения на точные будильники — используем менее точный метод
                    alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerMs, operation)
                }
            } else {
                alarmManager.setAlarmClock(
                    AlarmManager.AlarmClockInfo(triggerMs, showIntent),
                    operation
                )
            }
            Log.d("AlarmScheduler", "Будильник #${alarm.id} запланирован на $triggerMs")
        } catch (e: SecurityException) {
            Log.w("AlarmScheduler", "SecurityException при setAlarmClock — фолбэк на setAndAllowWhileIdle", e)
            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerMs, operation)
        }
    }

    /** Отменить будильник по id. */
    fun cancel(alarmId: Int) {
        alarmManager.cancel(buildPendingIntent(alarmId))
        Log.d("AlarmScheduler", "Будильник #$alarmId отменён")
    }

    /** Пересчитать расписание всех будильников из базы. */
    suspend fun rescheduleAll(alarmDao: AlarmDao) {
        val alarms = alarmDao.getAllOnce()
        alarms.forEach { alarm ->
            cancel(alarm.id)
            if (alarm.enabled) schedule(alarm)
        }
        Log.d("AlarmScheduler", "Перепланировано ${alarms.size} будильников")
    }

    // ──────────────────────────────────────────────

    private fun buildPendingIntent(alarmId: Int): PendingIntent {
        val intent = Intent(context, AlarmReceiver::class.java).apply {
            putExtra("alarm_id", alarmId)
        }
        return PendingIntent.getBroadcast(
            context,
            alarmId,
            intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )
    }

    /**
     * Вычислить эпоху (мс) следующего срабатывания будильника.
     * Calendar.MONDAY=2 .. Calendar.SUNDAY=1 — маппим в биты 0..6 (Пн..Вс).
     */
    private fun nextTriggerMillis(alarm: AlarmEntity): Long {
        val cal = Calendar.getInstance().apply {
            set(Calendar.HOUR_OF_DAY, alarm.hour)
            set(Calendar.MINUTE, alarm.minute)
            set(Calendar.SECOND, 0)
            set(Calendar.MILLISECOND, 0)
        }

        return if (alarm.daysMask == 0) {
            // Разовый: если время уже прошло сегодня — сдвигаем на завтра
            if (cal.timeInMillis <= System.currentTimeMillis()) {
                cal.add(Calendar.DAY_OF_YEAR, 1)
            }
            cal.timeInMillis
        } else {
            // Повторяющийся: ищем ближайший день из маски
            var found = false
            repeat(7) { offset ->
                if (!found) {
                    val checkCal = cal.clone() as Calendar
                    if (offset > 0) checkCal.add(Calendar.DAY_OF_YEAR, offset)
                    val bit = calDayToBit(checkCal.get(Calendar.DAY_OF_WEEK))
                    if (alarm.daysMask and (1 shl bit) != 0) {
                        // Тот же день, но время уже прошло → пропускаем
                        if (offset == 0 && checkCal.timeInMillis <= System.currentTimeMillis()) return@repeat
                        cal.timeInMillis = checkCal.timeInMillis
                        found = true
                    }
                }
            }
            if (!found) {
                // Крайний случай: все биты проверены — ждём ещё 7 дней (не должно случиться)
                cal.add(Calendar.DAY_OF_YEAR, 7)
            }
            cal.timeInMillis
        }
    }

    /**
     * Перевод Calendar.DAY_OF_WEEK → бит в daysMask (0=Пн, 6=Вс).
     * Calendar: SUNDAY=1, MONDAY=2, ..., SATURDAY=7
     */
    private fun calDayToBit(calDay: Int): Int = when (calDay) {
        Calendar.MONDAY    -> 0
        Calendar.TUESDAY   -> 1
        Calendar.WEDNESDAY -> 2
        Calendar.THURSDAY  -> 3
        Calendar.FRIDAY    -> 4
        Calendar.SATURDAY  -> 5
        Calendar.SUNDAY    -> 6
        else               -> 0
    }
}
