package com.seven.ikar.kotlin.feature.share

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.seven.ikar.kotlin.app.AppContainer
import com.seven.ikar.kotlin.core.contacts.DiscoverUserItem
import com.seven.ikar.kotlin.core.contacts.PhoneNumberNormalizer
import com.seven.ikar.kotlin.core.model.ChatSummaryDto
import com.seven.ikar.kotlin.core.model.ChatType
import com.seven.ikar.kotlin.ui.chatPreview
import com.seven.ikar.kotlin.ui.formatLastActivity
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.launch

data class ShareChatTargetItemUiState(
    val chat: ChatSummaryDto,
    val resolvedTitle: String,
    val lastActivityLabel: String,
    val preview: String
)

data class ShareTargetUiState(
    val payload: PendingExternalShare,
    val query: String = "",
    val chats: List<ShareChatTargetItemUiState> = emptyList(),
    val contacts: List<DiscoverUserItem> = emptyList(),
    val isLoading: Boolean = false,
    val isSending: Boolean = false,
    val errorMessage: String? = null,
    val pendingChatId: String? = null
)

class ShareTargetViewModel(
    private val container: AppContainer,
    private val pendingShare: PendingExternalShare
) : ViewModel() {
    private val _uiState = MutableStateFlow(ShareTargetUiState(payload = pendingShare))
    private val _allChats = mutableListOf<ChatSummaryDto>()
    private var contactNamesByNormalizedPhone: Map<String, String> = emptyMap()
    private var currentUserId: String? = container.sessionStore.read()?.user?.id
    private var searchJob: Job? = null

    val uiState: StateFlow<ShareTargetUiState> = _uiState.asStateFlow()

    init {
        refresh()
    }

    fun updateQuery(value: String) {
        _uiState.value = _uiState.value.copy(query = value)
        searchJob?.cancel()
        searchJob = viewModelScope.launch {
            delay(250)
            refresh()
        }
    }

    fun refresh() {
        viewModelScope.launch {
            val query = _uiState.value.query.trim()
            _uiState.value = _uiState.value.copy(isLoading = true, errorMessage = null)
            runCatching {
                contactNamesByNormalizedPhone = loadDeviceContactNames()
                val chats = container.executeAuthorized { session ->
                    currentUserId = session.user.id
                    container.apiClient.getChats(session.accessToken)
                }
                _allChats.clear()
                _allChats.addAll(chats)
                val contacts = loadContactCandidates(query)
                chats to contacts
            }.onSuccess { (chats, contacts) ->
                _uiState.value = _uiState.value.copy(
                    chats = filterChats(chats, query),
                    contacts = contacts,
                    isLoading = false,
                    errorMessage = null
                )
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isLoading = false, errorMessage = error.message)
            }
        }
    }

    fun sendToChat(chat: ShareChatTargetItemUiState) {
        sendToChatId(chat.chat.id)
    }

    fun sendToUser(user: DiscoverUserItem) {
        if (_uiState.value.isSending) {
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSending = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    val existingChatId = findExistingDirectChatId(user.id)
                    val targetChatId = existingChatId ?: container.apiClient.createDirectChat(session.accessToken, user.id).id
                    sendPendingShare(session.accessToken, targetChatId)
                    targetChatId
                }
            }.onSuccess { chatId ->
                _uiState.value = _uiState.value.copy(isSending = false, pendingChatId = chatId)
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSending = false, errorMessage = error.message)
            }
        }
    }

    fun consumePendingChatNavigation() {
        _uiState.value = _uiState.value.copy(pendingChatId = null)
    }

    private fun sendToChatId(chatId: String) {
        if (_uiState.value.isSending) {
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSending = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    sendPendingShare(session.accessToken, chatId)
                    chatId
                }
            }.onSuccess { targetChatId ->
                _uiState.value = _uiState.value.copy(isSending = false, pendingChatId = targetChatId)
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSending = false, errorMessage = error.message)
            }
        }
    }

    private suspend fun sendPendingShare(accessToken: String, chatId: String) {
        val sharedText = pendingShare.text?.trim().orEmpty()
        if (pendingShare.attachments.isEmpty()) {
            check(sharedText.isNotBlank()) { "Нечего отправлять." }
            container.apiClient.sendMessage(chatId, sharedText, accessToken)
            return
        }

        container.transferQueueRepository.enqueueUpload(
            chatId = chatId,
            text = sharedText.takeIf { it.isNotBlank() },
            replyToMessageId = null,
            files = pendingShare.attachments
        )
    }

    private fun filterChats(chats: List<ChatSummaryDto>, query: String): List<ShareChatTargetItemUiState> {
        val items = chats
            .filter { it.canSendMessages }
            .map { chat ->
                ShareChatTargetItemUiState(
                    chat = chat,
                    resolvedTitle = resolveChatTitle(chat),
                    lastActivityLabel = formatLastActivity(chat.lastActivityAt),
                    preview = chatPreview(chat)
                )
            }

        if (query.isBlank()) {
            return items
        }

        return items.filter { item ->
            item.resolvedTitle.contains(query, ignoreCase = true) ||
                item.chat.title.contains(query, ignoreCase = true) ||
                item.chat.secondaryText.orEmpty().contains(query, ignoreCase = true) ||
                item.chat.lastMessagePreview.orEmpty().contains(query, ignoreCase = true)
        }
    }

    private suspend fun loadContactCandidates(query: String): List<DiscoverUserItem> {
        val candidates = when {
            hasContactsPermission() -> container.resolvedContactsRepository.loadMergedCandidates(query)
            query.isBlank() -> emptyList()
            else -> container.executeAuthorized { session ->
                container.apiClient.searchUsers(session.accessToken, query).map { user ->
                    DiscoverUserItem(
                        user = user,
                        displayName = user.displayName,
                        normalizedPhoneNumber = PhoneNumberNormalizer.normalize(user.phoneNumber) ?: ""
                    )
                }
            }
        }

        val directParticipantIds = _allChats
            .asSequence()
            .filter { it.type == ChatType.Direct }
            .flatMap { chat -> chat.participants.asSequence().map { participant -> participant.id } }
            .toSet()

        return candidates.filterNot { directParticipantIds.contains(it.id) }
    }

    private fun resolveChatTitle(chat: ChatSummaryDto): String {
        if (chat.type != ChatType.Direct) {
            return chat.title
        }

        val peer = chat.participants.firstOrNull { it.id != currentUserId } ?: chat.participants.firstOrNull()
        val normalizedPhone = PhoneNumberNormalizer.normalize(peer?.phoneNumber)
        val contactName = normalizedPhone?.let(contactNamesByNormalizedPhone::get)
        return contactName?.takeIf { it.isNotBlank() }
            ?: peer?.displayName?.takeIf { it.isNotBlank() }
            ?: chat.title
    }

    private fun findExistingDirectChatId(userId: String): String? =
        _allChats.firstOrNull { chat ->
            chat.type == ChatType.Direct && chat.participants.any { participant -> participant.id == userId }
        }?.id

    private fun loadDeviceContactNames(): Map<String, String> {
        if (!hasContactsPermission()) {
            return emptyMap()
        }

        return container.contactDiscoveryService.getContacts().associate { contact ->
            contact.normalizedPhoneNumber to contact.displayName
        }
    }

    private fun hasContactsPermission(): Boolean =
        ContextCompat.checkSelfPermission(
            container.appContext,
            Manifest.permission.READ_CONTACTS
        ) == PackageManager.PERMISSION_GRANTED
}
