package org.zaine.management.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.zaine.management.data.BackendRepository
import org.zaine.management.data.NetworkResult
import org.zaine.management.data.api.CalendarEventDto
import java.time.LocalDate
import java.time.YearMonth
import java.time.temporal.TemporalAdjusters
import javax.inject.Inject

enum class CalendarViewMode { Day, Week, Month }

data class CalendarUiState(
    val mode: CalendarViewMode = CalendarViewMode.Month,
    val selectedDate: LocalDate = LocalDate.now(),
    val selectedMonth: YearMonth = YearMonth.now(),
    val loading: Boolean = false,
    val events: List<CalendarEventDto> = emptyList(),
    val error: String? = null
)

data class EventDetailsUiState(
    val loading: Boolean = false,
    val event: CalendarEventDto? = null,
    val error: String? = null
)

@HiltViewModel
class CalendarViewModel @Inject constructor(
    private val repository: BackendRepository
) : ViewModel() {
    private val _state = MutableStateFlow(CalendarUiState(loading = true))
    val state: StateFlow<CalendarUiState> = _state.asStateFlow()

    init {
        refresh()
    }

    fun previousMonth() {
        _state.value = _state.value.copy(selectedMonth = _state.value.selectedMonth.minusMonths(1))
        refresh()
    }

    fun nextMonth() {
        _state.value = _state.value.copy(selectedMonth = _state.value.selectedMonth.plusMonths(1))
        refresh()
    }

    fun previousDay() {
        _state.value = _state.value.copy(selectedDate = _state.value.selectedDate.minusDays(1))
        refresh()
    }

    fun nextDay() {
        _state.value = _state.value.copy(selectedDate = _state.value.selectedDate.plusDays(1))
        refresh()
    }

    fun previousWeek() {
        _state.value = _state.value.copy(selectedDate = _state.value.selectedDate.minusWeeks(1))
        refresh()
    }

    fun nextWeek() {
        _state.value = _state.value.copy(selectedDate = _state.value.selectedDate.plusWeeks(1))
        refresh()
    }

    fun setMode(mode: CalendarViewMode) {
        _state.value = _state.value.copy(mode = mode)
        refresh()
    }

    fun refresh() {
        val current = _state.value
        viewModelScope.launch {
            _state.value = _state.value.copy(loading = true, error = null)
            val result = when (current.mode) {
                CalendarViewMode.Day -> repository.calendarRange(
                    current.selectedDate.toString(),
                    current.selectedDate.plusDays(1).toString()
                )
                CalendarViewMode.Week -> {
                    val weekStart = current.selectedDate.with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY))
                    repository.calendarRange(weekStart.toString(), weekStart.plusWeeks(1).toString())
                }
                CalendarViewMode.Month -> repository.month(current.selectedMonth.year, current.selectedMonth.monthValue)
            }
            when (result) {
                is NetworkResult.Success -> _state.value = _state.value.copy(loading = false, events = result.data)
                is NetworkResult.Error -> _state.value = _state.value.copy(loading = false, error = result.message)
                NetworkResult.Loading -> Unit
            }
        }
    }
}

@HiltViewModel
class EventDetailsViewModel @Inject constructor(
    private val repository: BackendRepository
) : ViewModel() {
    private val _state = MutableStateFlow(EventDetailsUiState(loading = true))
    val state: StateFlow<EventDetailsUiState> = _state.asStateFlow()

    fun load(eventId: Long) {
        viewModelScope.launch {
            _state.value = EventDetailsUiState(loading = true)
            when (val result = repository.event(eventId)) {
                is NetworkResult.Success -> _state.value = EventDetailsUiState(event = result.data)
                is NetworkResult.Error -> _state.value = EventDetailsUiState(error = result.message)
                NetworkResult.Loading -> Unit
            }
        }
    }
}
