package com.applemand.zmanim.feature.ics

import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.applemand.zmanim.core.calsync.IcsWriter
import com.applemand.zmanim.core.calsync.ZmanimIcsBuilder
import com.applemand.zmanim.core.jewcal.HolidayCatalog
import com.applemand.zmanim.core.settings.SettingsRepository
import com.applemand.zmanim.core.zmanim.ZmanKey
import com.applemand.zmanim.core.zmanim.ZmanimCalculator
import com.applemand.zmanim.core.zmanim.ZmanimLocation
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
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.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime
import java.io.OutputStreamWriter
import javax.inject.Inject

data class IcsExportState(
    val days: Int = 30,
    val busy: Boolean = false,
    val writtenTo: Uri? = null,
    val error: String? = null,
)

@HiltViewModel
class IcsExportViewModel @Inject constructor(
    @ApplicationContext private val context: Context,
    private val settings: SettingsRepository,
    private val zmanimCalc: ZmanimCalculator,
) : ViewModel() {

    private val _state = MutableStateFlow(IcsExportState())
    val state: StateFlow<IcsExportState> = _state.asStateFlow()

    fun setDays(d: Int) { _state.value = _state.value.copy(days = d) }

    fun export(target: Uri) {
        _state.value = _state.value.copy(busy = true, error = null, writtenTo = null)
        viewModelScope.launch {
            runCatching {
                val s = settings.settings.first()
                val loc = s.currentLocation
                val zLoc = ZmanimLocation(
                    lat = loc.lat, lng = loc.lng, tzid = loc.tzid,
                    elevationMeters = loc.elevationM, name = loc.nameEn,
                    useElevation = s.useElevationForZmanim,
                )
                val start = Clock.System.now()
                    .toLocalDateTime(TimeZone.currentSystemDefault()).date
                val range: List<LocalDate> =
                    (0 until _state.value.days).map { start.plus(it, DateTimeUnit.DAY) }

                val snapshots = withContext(Dispatchers.Default) {
                    zmanimCalc.computeRange(range, zLoc, s.candleLightingMinutes)
                }
                val holidayCatalog = HolidayCatalog(inIsrael = s.inIsrael)
                val holidays = range.associateWith { d ->
                    holidayCatalog.filter(
                        holidays = holidayCatalog.holidaysFor(d),
                        yomTov = s.holidayFilters.yomTov,
                        fast = s.holidayFilters.fast,
                        minor = s.holidayFilters.minor,
                        roshChodesh = s.holidayFilters.roshChodesh,
                    )
                }
                val visible = ZmanKey.entries.filter { s.zmanimVisibleMap[it.keyId] != false }.toSet()
                val events = ZmanimIcsBuilder().build(snapshots, holidays, visible)

                context.contentResolver.openOutputStream(target, "w")?.use { out ->
                    OutputStreamWriter(out, Charsets.UTF_8).use { w ->
                        IcsWriter(w).writeCalendar(tzid = loc.tzid, events = events)
                    }
                } ?: error("Cannot open output stream")
            }.onSuccess {
                _state.value = _state.value.copy(busy = false, writtenTo = target)
            }.onFailure {
                _state.value = _state.value.copy(busy = false, error = it.message ?: "Export failed")
            }
        }
    }
}
