package ru.forbion.f7cloud.feature.deck

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.network.UnauthorizedException

data class DeckUiState(
    val loading: Boolean = false,
    val boards: List<DeckBoard> = emptyList(),
    val selectedBoardId: Int? = null,
    val boardDetail: DeckBoardDetail? = null,
    val openedCard: DeckCard? = null,
    val busy: Boolean = false,
    val error: String? = null,
    val unauthorized: Boolean = false,
)

class DeckViewModel(
    private val repository: DeckRepository = DeckRepository(),
) : ViewModel() {
    private val _state = MutableStateFlow(DeckUiState())
    val state: StateFlow<DeckUiState> = _state.asStateFlow()

    fun load(session: AuthSession) {
        viewModelScope.launch(Dispatchers.IO) {
            _state.update { it.copy(loading = true, error = null) }
            runCatching { repository.loadBoards(session) }
                .onSuccess { boards -> _state.update { it.copy(loading = false, boards = boards, error = null) } }
                .onFailure { t -> fail(t) }
        }
    }

    fun openBoard(session: AuthSession, boardId: Int) {
        viewModelScope.launch(Dispatchers.IO) {
            _state.update { it.copy(loading = true, selectedBoardId = boardId, boardDetail = null, error = null) }
            runCatching { repository.loadBoardDetail(session, boardId) }
                .onSuccess { detail -> _state.update { it.copy(loading = false, boardDetail = detail) } }
                .onFailure { t -> fail(t) }
        }
    }

    private fun reloadBoard(session: AuthSession) {
        val boardId = _state.value.selectedBoardId ?: return
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { repository.loadBoardDetail(session, boardId) }
                .onSuccess { detail ->
                    // если открыта карточка — подставим её свежую версию
                    val openedId = _state.value.openedCard?.id
                    val freshCard = openedId?.let { id ->
                        detail.stacks.flatMap { it.cards }.firstOrNull { it.id == id }
                    }
                    _state.update { it.copy(boardDetail = detail, openedCard = freshCard ?: it.openedCard, busy = false) }
                }
                .onFailure { t -> fail(t) }
        }
    }

    fun closeBoard() {
        _state.update { it.copy(selectedBoardId = null, boardDetail = null, openedCard = null) }
    }

    fun openCard(card: DeckCard) {
        _state.update { it.copy(openedCard = card) }
    }

    fun closeCard() {
        _state.update { it.copy(openedCard = null) }
    }

    fun openCardById(session: AuthSession, cardId: Int) {
        viewModelScope.launch(Dispatchers.IO) {
            _state.update { it.copy(loading = true, error = null) }
            runCatching { repository.loadCard(session, cardId) }
                .onSuccess { card -> openBoard(session, card.boardId) }
                .onFailure { t -> fail(t, "Не удалось открыть карточку") }
        }
    }

    // --- Действия (оптимистично не мутируем, перечитываем доску) ---

    fun createCard(session: AuthSession, stackId: Int, title: String) =
        mutate(session) { repository.createCard(session, stackId, title.trim()) }

    fun toggleDone(session: AuthSession, card: DeckCard) =
        mutate(session) { repository.setCardDone(session, card.id, !card.done) }

    fun updateCard(session: AuthSession, card: DeckCard, title: String, description: String, duedate: String?) =
        mutate(session) { repository.updateCard(session, card, title.trim(), description.trim(), duedate) }

    fun moveCard(session: AuthSession, card: DeckCard, targetStackId: Int) =
        mutate(session) { repository.moveCard(session, card.id, targetStackId) }

    fun archiveCard(session: AuthSession, card: DeckCard) =
        mutate(session, closeCardAfter = true) { repository.archiveCard(session, card.id) }

    fun deleteCard(session: AuthSession, card: DeckCard) =
        mutate(session, closeCardAfter = true) { repository.deleteCard(session, card.id) }

    fun toggleLabel(session: AuthSession, card: DeckCard, label: DeckLabel) = mutate(session) {
        if (card.labels.any { it.id == label.id }) {
            repository.removeLabel(session, card.id, label.id)
        } else {
            repository.assignLabel(session, card.id, label.id)
        }
    }

    fun createStack(session: AuthSession, title: String) {
        val boardId = _state.value.selectedBoardId ?: return
        mutate(session) { repository.createStack(session, boardId, title.trim()) }
    }

    private fun mutate(
        session: AuthSession,
        closeCardAfter: Boolean = false,
        action: () -> Unit,
    ) {
        _state.update { it.copy(busy = true, error = null) }
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { action() }
                .onSuccess {
                    if (closeCardAfter) _state.update { it.copy(openedCard = null) }
                    reloadBoard(session)
                }
                .onFailure { t -> fail(t) }
        }
    }

    private fun fail(t: Throwable, fallback: String? = null) {
        _state.update {
            it.copy(
                loading = false,
                busy = false,
                error = t.message ?: fallback,
                unauthorized = t is UnauthorizedException,
            )
        }
    }
}
