package com.applemand.zmanim.feature.calendar

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.applemand.zmanim.core.datetime.DateRanges
import com.applemand.zmanim.core.jewcal.DafYomiCalculator
import com.applemand.zmanim.core.jewcal.HolidayCatalog
import com.applemand.zmanim.core.jewcal.OmerCalculator
import com.applemand.zmanim.core.jewcal.ParashaCalculator
import com.applemand.zmanim.core.jewcal.toHebrewDate
import com.applemand.zmanim.core.settings.SettingsRepository
import com.applemand.zmanim.core.settings.proto.UserSettings
import com.applemand.zmanim.core.zmanim.ZmanimCalculator
import com.applemand.zmanim.core.zmanim.ZmanimLocation
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import javax.inject.Inject

@HiltViewModel
class CalendarViewModel @Inject constructor(
    private val savedState: SavedStateHandle,
    private val settings: SettingsRepository,
    private val zmanimCalculator: ZmanimCalculator,
) : ViewModel() {

    private val today: LocalDate
        get() = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date

    private val _state = MutableStateFlow(
        CalendarUiState(
            view = CalendarView.MONTH,
            currentDate = savedState.get<Long>(KEY_DATE)?.let { LocalDate.fromEpochDays(it.toInt()) }
                ?: today,
            today = today,
        )
    )
    val state: StateFlow<CalendarUiState> = _state.asStateFlow()

    fun setView(view: CalendarView) {
        _state.value = _state.value.copy(view = view)
        refresh()
    }

    fun setDate(date: LocalDate) {
        savedState[KEY_DATE] = date.toEpochDays().toLong()
        _state.value = _state.value.copy(currentDate = date)
        refresh()
    }

    fun goToday() = setDate(today)

    init { refresh() }

    private fun refresh() {
        viewModelScope.launch {
            _state.value = _state.value.copy(loading = true)
            val us: UserSettings = settings.settings.first()
            val loc = us.currentLocation
            val zLoc = ZmanimLocation(
                lat = loc.lat, lng = loc.lng, tzid = loc.tzid,
                elevationMeters = loc.elevationM, name = loc.nameEn,
                useElevation = us.useElevationForZmanim,
            )
            val range = visibleDateRange(_state.value.view, _state.value.currentDate)
            val holidayCatalog = HolidayCatalog(inIsrael = us.inIsrael)
            val parashaCalc = ParashaCalculator(inIsrael = us.inIsrael)
            val dafCalc = DafYomiCalculator()
            val omerCalc = OmerCalculator()

            val summaries = withContext(Dispatchers.Default) {
                range.associateWith { d ->
                    DaySummary(
                        date = d,
                        hebrewDate = d.toHebrewDate(),
                        zmanim = runCatching {
                            zmanimCalculator.compute(d, zLoc, us.candleLightingMinutes)
                        }.getOrNull(),
                        holidays = holidayCatalog.filter(
                            holidays = holidayCatalog.holidaysFor(d),
                            yomTov = us.holidayFilters.yomTov,
                            fast = us.holidayFilters.fast,
                            minor = us.holidayFilters.minor,
                            roshChodesh = us.holidayFilters.roshChodesh,
                        ),
                        parasha = if (d.dayOfWeek.ordinal == 5) parashaCalc.forWeekOf(d) else null,
                        daf = if (us.showDafYomi) dafCalc.bavli(d) else null,
                        omer = omerCalc.forDate(d),
                    )
                }
            }
            _state.value = _state.value.copy(
                daySummaries = summaries,
                loading = false,
            )
        }
    }

    private fun visibleDateRange(view: CalendarView, anchor: LocalDate): List<LocalDate> =
        when (view) {
            CalendarView.DAY -> listOf(anchor)
            CalendarView.WEEK -> DateRanges.week(anchor)
            CalendarView.MONTH -> DateRanges.monthGrid(anchor.year, anchor.monthNumber)
            CalendarView.YEAR -> emptyList() // year view reuses monthGrids lazily
        }

    companion object {
        private const val KEY_DATE = "currentDate"
    }
}
