package ru.forbion.f7cloud.feature.notes

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
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

/**
 * Состояние экрана Заметок (P1). Список сортируется «избранное вверх», затем по дате
 * изменения; поиск фильтрует по заголовку (карта app-map/notes.md).
 */
data class NotesUiState(
    val loading: Boolean = false,
    val saving: Boolean = false,
    val notes: List<Note> = emptyList(),
    val searchQuery: String = "",
    val openNote: Note? = null,
    val conflict: NoteConflictException? = null,
    val error: String? = null,
    val unauthorized: Boolean = false,
) {
    /** Отфильтрованный по поиску и отсортированный список для отображения. */
    val visibleNotes: List<Note>
        get() = notes
            .filter { searchQuery.isBlank() || it.title.contains(searchQuery, ignoreCase = true) }
            .sortedWith(compareByDescending<Note> { it.favorite }.thenByDescending { it.modified })

    val isEmpty: Boolean get() = !loading && notes.isEmpty()
}

/**
 * ViewModel Заметок (P1): список/открытие/сохранение (с разрешением конфликта версий),
 * избранное, удаление, создание. Пишет через [NotesRepository]; конфликт `If-Match` (412)
 * поднимается в состояние и разрешается ЯВНЫМ выбором пользователя — не молчаливо (mail/071).
 */
class NotesViewModel(
    private val repository: NotesRepository = NotesRepository(),
) : ViewModel() {

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

    fun load(session: AuthSession) {
        _state.update { it.copy(loading = true, error = null) }
        launchGuarded {
            val notes = repository.listNotes(session)
            _state.update { it.copy(loading = false, notes = notes) }
        }
    }

    fun setSearch(query: String) = _state.update { it.copy(searchQuery = query) }

    fun openNote(session: AuthSession, id: Long) {
        launchGuarded {
            val note = repository.getNote(session, id)
            _state.update { it.copy(openNote = note) }
        }
    }

    fun closeNote() = _state.update { it.copy(openNote = null) }

    fun createNote(session: AuthSession) {
        _state.update { it.copy(saving = true, error = null) }
        launchGuarded {
            val note = repository.createNote(session, content = "")
            _state.update { it.copy(saving = false, openNote = note, notes = upsert(it.notes, note)) }
        }
    }

    /** Сохранить содержимое открытой заметки; на 412 — поднять конфликт в состояние. */
    fun saveNote(session: AuthSession, content: String) {
        val current = _state.value.openNote ?: return
        _state.update { it.copy(saving = true, error = null) }
        viewModelScope.launch {
            try {
                val saved = repository.saveNote(session, current.copy(content = content))
                _state.update {
                    it.copy(saving = false, openNote = saved, notes = upsert(it.notes, saved))
                }
            } catch (e: NoteConflictException) {
                _state.update { it.copy(saving = false, conflict = e) }
            } catch (e: UnauthorizedException) {
                _state.update { it.copy(saving = false, unauthorized = true, error = e.message) }
            } catch (e: Exception) {
                _state.update { it.copy(saving = false, error = e.message) }
            }
        }
    }

    /** Разрешение конфликта: перезаписать серверную версию текущим содержимым. */
    fun resolveConflictKeepMine(session: AuthSession) {
        val conflict = _state.value.conflict ?: return
        val mine = _state.value.openNote ?: return
        // берём etag серверной версии, чтобы If-Match прошёл, но содержимое — наше
        val base = conflict.serverNote?.copy(content = mine.content) ?: mine
        _state.update { it.copy(conflict = null, saving = true) }
        launchGuarded {
            val saved = repository.saveNote(session, base)
            _state.update { it.copy(saving = false, openNote = saved, notes = upsert(it.notes, saved)) }
        }
    }

    /** Разрешение конфликта: принять версию сервера (отбросить локальные правки). */
    fun resolveConflictUseServer() {
        val server = _state.value.conflict?.serverNote
        _state.update { st ->
            st.copy(
                conflict = null,
                openNote = server ?: st.openNote,
                notes = if (server != null) upsert(st.notes, server) else st.notes,
            )
        }
    }

    fun toggleFavorite(session: AuthSession, note: Note) {
        launchGuarded {
            val updated = repository.setFavorite(session, note.id, !note.favorite)
            _state.update { it.copy(notes = upsert(it.notes, updated)) }
        }
    }

    fun deleteNote(session: AuthSession, note: Note) {
        launchGuarded {
            repository.deleteNote(session, note.id)
            _state.update {
                it.copy(
                    notes = it.notes.filterNot { n -> n.id == note.id },
                    openNote = if (it.openNote?.id == note.id) null else it.openNote,
                )
            }
        }
    }

    fun clearError() = _state.update { it.copy(error = null) }

    private fun launchGuarded(block: suspend () -> Unit) {
        viewModelScope.launch {
            try {
                block()
            } catch (e: UnauthorizedException) {
                _state.update { it.copy(loading = false, saving = false, unauthorized = true, error = e.message) }
            } catch (e: Exception) {
                _state.update { it.copy(loading = false, saving = false, error = e.message) }
            }
        }
    }

    private fun upsert(notes: List<Note>, note: Note): List<Note> {
        val idx = notes.indexOfFirst { it.id == note.id }
        return if (idx >= 0) notes.toMutableList().also { it[idx] = note } else notes + note
    }

    class Factory(private val context: Context) : ViewModelProvider.Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>): T =
            NotesViewModel(NotesRepository(NotesCache(context.applicationContext))) as T
    }
}
