package ru.forbion.f7cloud.feature.contacts

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
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.isActive
import kotlinx.coroutines.launch
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.designsystem.AppForegroundTracker
import ru.forbion.f7cloud.core.network.UnauthorizedException

data class ContactsUiState(
    val loading: Boolean = false,
    val syncing: Boolean = false,
    val contacts: List<ContactItem> = emptyList(),
    val searchQuery: String = "",
    val error: String? = null,
    val unauthorized: Boolean = false,
    val addSheetOpen: Boolean = false,
    val savingContact: Boolean = false,
    val addError: String? = null,
    val selectedContact: ContactItem? = null,
) {
    val filteredContacts: List<ContactItem>
        get() {
            val q = searchQuery.trim()
            if (q.isEmpty()) return contacts
            return contacts.filter {
                it.displayName.contains(q, ignoreCase = true) ||
                    it.email.contains(q, ignoreCase = true) ||
                    it.phone.contains(q, ignoreCase = true)
            }
        }
}

class ContactsViewModel(
    private val repository: ContactsRepository,
) : ViewModel() {
    private val _state = MutableStateFlow(ContactsUiState())
    val state: StateFlow<ContactsUiState> = _state.asStateFlow()

    private var periodicSyncJob: Job? = null
    private var activeSession: AuthSession? = null

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

    fun openAddSheet() {
        _state.update { it.copy(addSheetOpen = true, addError = null) }
    }

    fun closeAddSheet() {
        _state.update { it.copy(addSheetOpen = false, addError = null, savingContact = false) }
    }

    fun openContact(contact: ContactItem) {
        _state.update { it.copy(selectedContact = contact) }
    }

    fun closeContactDetail() {
        _state.update { it.copy(selectedContact = null) }
    }

    fun load(session: AuthSession) {
        if (activeSession?.serverUrl == session.serverUrl &&
            activeSession?.username == session.username
        ) {
            return
        }
        activeSession = session
        periodicSyncJob?.cancel()
        viewModelScope.launch {
            repository.observeContacts(session).collect { contacts ->
                _state.update {
                    it.copy(
                        contacts = contacts,
                        loading = false,
                    )
                }
            }
        }
        refresh(session, showLoading = true)
        periodicSyncJob = viewModelScope.launch {
            while (isActive) {
                delay(ContactsRepository.SYNC_INTERVAL_MS)
                if (AppForegroundTracker.isForeground) {
                    refresh(session, showLoading = false)
                }
            }
        }
    }

    fun refresh(session: AuthSession, showLoading: Boolean = false, force: Boolean = false) {
        viewModelScope.launch(Dispatchers.IO) {
            if (showLoading && _state.value.contacts.isEmpty()) {
                _state.update { it.copy(loading = true, error = null) }
            } else {
                _state.update { it.copy(syncing = true, error = null) }
            }
            // force (pull-to-refresh) с CTag-проверкой дешёвый: 1 лёгкий PROPFIND,
            // полная закачка vCard — только если адресные книги реально менялись
            runCatching { repository.syncContacts(session, force = force) }
                .onFailure { t ->
                    _state.update {
                        it.copy(
                            loading = false,
                            syncing = false,
                            error = if (it.contacts.isEmpty()) t.message else null,
                            unauthorized = t is UnauthorizedException,
                        )
                    }
                }
                .onSuccess {
                    _state.update {
                        it.copy(
                            loading = false,
                            syncing = false,
                            error = null,
                        )
                    }
                }
        }
    }

    fun createContact(
        session: AuthSession,
        displayName: String,
        email: String,
        phone: String,
    ) {
        if (_state.value.savingContact) return
        val name = displayName.trim()
        val mail = email.trim()
        if (name.isBlank() && mail.isBlank()) {
            _state.update { it.copy(addError = "Укажите имя или email") }
            return
        }
        viewModelScope.launch(Dispatchers.IO) {
            _state.update { it.copy(savingContact = true, addError = null) }
            runCatching {
                repository.createContact(
                    session = session,
                    displayName = name.ifBlank { mail.substringBefore('@') },
                    email = mail,
                    phone = phone.trim(),
                )
            }.onSuccess {
                _state.update {
                    it.copy(
                        savingContact = false,
                        addSheetOpen = false,
                        addError = null,
                    )
                }
                refresh(session, showLoading = false)
            }.onFailure { t ->
                _state.update {
                    it.copy(
                        savingContact = false,
                        addError = t.message ?: "Не удалось создать контакт",
                        unauthorized = t is UnauthorizedException,
                    )
                }
            }
        }
    }
}

class ContactsViewModelFactory(
    context: Context,
) : ViewModelProvider.Factory {
    private val repository = ContactsRepository(context.applicationContext)

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ContactsViewModel::class.java)) {
            return ContactsViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
