package de.tsschulz.timeclock.ui.time

import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import de.tsschulz.timeclock.data.api.TimeClockApiClient
import de.tsschulz.timeclock.data.api.TimeEntryDto
import de.tsschulz.timeclock.data.api.TimeStatsDto
import de.tsschulz.timeclock.data.api.WeekOverviewDto
import de.tsschulz.timeclock.data.auth.TokenStore
import de.tsschulz.timeclock.data.offline.OfflineClockQueue
import de.tsschulz.timeclock.data.time.TimeDashboard
import de.tsschulz.timeclock.data.time.TimeRepository
import de.tsschulz.timeclock.ui.model.ButtonVariant
import de.tsschulz.timeclock.ui.model.StatusAction
import de.tsschulz.timeclock.ui.model.StatusRow
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

data class TimeUiState(
    val loading: Boolean = false,
    val clockInProgress: Boolean = false,
    val error: String? = null,
    val dashboard: TimeDashboard? = null,
    val statusRows: List<StatusRow> = listOf(StatusRow("Derzeit gearbeitet", "—")),
    val primaryAction: StatusAction? = null,
    val secondaryAction: StatusAction? = null,
    val weekLoading: Boolean = false,
    val weekOffset: Int = 0,
    val week: WeekOverviewDto? = null,
    val weekError: String? = null,
    val entries: List<TimeEntryDto> = emptyList(),
    val entriesLoading: Boolean = false,
    val entriesError: String? = null,
    val stats: TimeStatsDto? = null,
    val statsLoading: Boolean = false,
    val statsError: String? = null,
)

class TimeViewModel(
    private val repository: TimeRepository,
) : ViewModel() {

    private val _uiState = MutableStateFlow(TimeUiState())
    val uiState: StateFlow<TimeUiState> = _uiState.asStateFlow()
    private var refreshJob: Job? = null

    fun start() {
        if (refreshJob != null) return
        refreshJob = viewModelScope.launch {
            refreshAll()
            while (true) {
                delay(30_000)
                refreshDashboard()
            }
        }
    }

    fun stop() {
        refreshJob?.cancel()
        refreshJob = null
        _uiState.value = TimeUiState()
    }

    fun refreshAll() {
        refreshDashboard()
        loadWeek(_uiState.value.weekOffset)
    }

    fun refreshDashboard() {
        viewModelScope.launch {
            _uiState.update { it.copy(loading = true, error = null) }
            runCatching { repository.loadDashboard() }
                .onSuccess { dashboard ->
                    _uiState.update {
                        it.copy(
                            loading = false,
                            dashboard = dashboard,
                            statusRows = dashboard.toStatusRows(),
                            primaryAction = dashboard.primaryAction(),
                            secondaryAction = dashboard.secondaryAction(),
                            error = null,
                        )
                    }
                }
                .onFailure { e ->
                    _uiState.update { it.copy(loading = false, error = e.message ?: "Status konnte nicht geladen werden") }
                }
        }
    }

    fun clock(action: String) {
        viewModelScope.launch {
            _uiState.update { it.copy(clockInProgress = true, error = null) }
            runCatching { repository.clock(action) }
                .onSuccess { dashboard ->
                    _uiState.update {
                        it.copy(
                            clockInProgress = false,
                            dashboard = dashboard,
                            statusRows = dashboard.toStatusRows(),
                            primaryAction = dashboard.primaryAction(),
                            secondaryAction = dashboard.secondaryAction(),
                            error = null,
                        )
                    }
                    loadWeek(_uiState.value.weekOffset)
                }
                .onFailure { e ->
                    _uiState.update { it.copy(clockInProgress = false, error = e.message ?: "Stempeln fehlgeschlagen") }
                }
        }
    }

    fun loadWeek(weekOffset: Int) {
        viewModelScope.launch {
            _uiState.update { it.copy(weekLoading = true, weekError = null, weekOffset = weekOffset) }
            runCatching { repository.loadWeek(weekOffset) }
                .onSuccess { week ->
                    _uiState.update { it.copy(weekLoading = false, week = week, weekError = null) }
                }
                .onFailure { e ->
                    _uiState.update {
                        it.copy(weekLoading = false, weekError = e.message ?: "Wochenübersicht konnte nicht geladen werden")
                    }
                }
        }
    }

    fun loadEntries() {
        viewModelScope.launch {
            _uiState.update { it.copy(entriesLoading = true, entriesError = null) }
            runCatching { repository.loadEntries() }
                .onSuccess { entries -> _uiState.update { it.copy(entriesLoading = false, entries = entries) } }
                .onFailure { e -> _uiState.update { it.copy(entriesLoading = false, entriesError = e.message ?: "Einträge konnten nicht geladen werden") } }
        }
    }

    fun deleteEntry(id: String) {
        viewModelScope.launch {
            _uiState.update { it.copy(entriesLoading = true, entriesError = null) }
            runCatching {
                repository.deleteEntry(id)
                repository.loadEntries()
            }
                .onSuccess { entries -> _uiState.update { it.copy(entriesLoading = false, entries = entries) } }
                .onFailure { e -> _uiState.update { it.copy(entriesLoading = false, entriesError = e.message ?: "Eintrag konnte nicht gelöscht werden") } }
        }
    }

    fun loadStats() {
        viewModelScope.launch {
            _uiState.update { it.copy(statsLoading = true, statsError = null) }
            runCatching { repository.loadStats() }
                .onSuccess { stats -> _uiState.update { it.copy(statsLoading = false, stats = stats) } }
                .onFailure { e -> _uiState.update { it.copy(statsLoading = false, statsError = e.message ?: "Statistiken konnten nicht geladen werden") } }
        }
    }

    private fun TimeDashboard.toStatusRows(): List<StatusRow> {
        val stats = stats
        return buildList {
            add(StatusRow("Derzeit gearbeitet", stats.currentlyWorked.toHoursLabel()))
            add(StatusRow("Offen", stats.open.toHoursLabel()))
            add(StatusRow("Normales Arbeitsende", stats.regularEnd.toClockLabel()))
            addOvertimeRow("Woche", stats.overtime)
            addOvertimeRow("Gesamt", stats.totalOvertime)
            add(StatusRow("Wochenarbeitszeit", stats.weekWorktime.toHoursLabel()))
            add(StatusRow("Arbeitsfreie Stunden", stats.nonWorkingHours.toHoursLabel()))
            add(StatusRow("Offen für Woche", stats.openForWeek.toHoursLabel()))
            add(StatusRow("Bereinigtes Arbeitsende (heute)", isHeading = true))
            add(StatusRow("- Generell", stats.adjustedEndTodayGeneral.toClockLabel()))
            add(StatusRow("- Woche", stats.adjustedEndTodayWeek.toClockLabel()))
        }
    }

    private fun TimeDashboard.primaryAction(): StatusAction? =
        when (state) {
            null, "stop work" -> StatusAction("Arbeit beginnen", ButtonVariant.Success, "start work")
            "start work", "stop pause" -> StatusAction("Arbeit beenden", ButtonVariant.Danger, "stop work")
            "start pause" -> null
            else -> StatusAction("Arbeit beginnen", ButtonVariant.Success, "start work")
        }

    private fun TimeDashboard.secondaryAction(): StatusAction? =
        when (state) {
            "start work", "stop pause" -> StatusAction("Pause beginnen", ButtonVariant.Secondary, "start pause")
            "start pause" -> StatusAction("Pause beenden", ButtonVariant.Secondary, "stop pause")
            else -> null
        }

    private fun MutableList<StatusRow>.addOvertimeRow(scope: String, value: String?) {
        val raw = value?.takeIf { it.isNotBlank() && it != "—" } ?: "—"
        val isNegative = raw.startsWith("-")
        val label = if (isNegative) "Fehlzeit ($scope)" else "Überstunden ($scope)"
        val displayValue = if (isNegative) raw.removePrefix("-").toHoursLabel() else raw.toHoursLabel()
        add(StatusRow(label, displayValue))
    }

    private fun String?.toHoursLabel(): String {
        val value = this?.takeIf { it.isNotBlank() } ?: return "—"
        if (value == "—" || value.contains("erreicht")) return value
        return if (value.endsWith(" h")) value else "$value h"
    }

    private fun String?.toClockLabel(): String {
        val value = this?.takeIf { it.isNotBlank() } ?: return "—"
        if (value == "—" || value.contains("erreicht") || value.endsWith(" Uhr")) return value
        return "$value Uhr"
    }

    class Factory(
        private val application: Application,
    ) : ViewModelProvider.Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            val tokenStore = TokenStore(application)
            val api = TimeClockApiClient(tokenStore)
            val repo = TimeRepository(api, OfflineClockQueue(application))
            return TimeViewModel(repo) as T
        }
    }
}
