package com.seven.ikar.kotlin.feature.chatinfo

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.seven.ikar.kotlin.app.AppContainer
import com.seven.ikar.kotlin.core.model.ChatStateRequest
import com.seven.ikar.kotlin.core.model.ChatInfoDto
import com.seven.ikar.kotlin.core.model.ChatInviteLinkDto
import com.seven.ikar.kotlin.core.model.ChatSummaryDto
import com.seven.ikar.kotlin.core.model.ChatType
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
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter

data class ChatInfoUiState(
    val info: ChatInfoDto? = null,
    val discussionGroups: List<ChatSummaryDto> = emptyList(),
    val inviteTitle: String = "",
    val inviteCreatesJoinRequest: Boolean = false,
    val inviteExpiresInHours: String = "",
    val inviteMaxUses: String = "",
    val inviteMemberLimit: String = "",
    val profileTitle: String = "",
    val profileDescription: String = "",
    val profilePublicUsername: String = "",
    val topicTitle: String = "",
    val isLoading: Boolean = false,
    val isSaving: Boolean = false,
    val errorMessage: String? = null
)

class ChatInfoViewModel(
    private val container: AppContainer,
    private val chatId: String
) : ViewModel() {
    private val _uiState = MutableStateFlow(ChatInfoUiState())
    val uiState: StateFlow<ChatInfoUiState> = _uiState.asStateFlow()
    private var profileAutosaveJob: Job? = null

    init {
        refresh()
    }

    fun refresh() {
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isLoading = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    val info = container.apiClient.getChatInfo(session.accessToken, chatId)
                    val discussionGroups = if (info.chat.type == ChatType.Channel && info.canManageChannel) {
                        container.apiClient.getChats(session.accessToken)
                            .filter { chat -> chat.type == ChatType.Group }
                            .sortedBy { chat -> chat.title.lowercase() }
                    } else {
                        emptyList()
                    }
                    info to discussionGroups
                }
            }.onSuccess { (info, discussionGroups) ->
                _uiState.value = _uiState.value.copy(
                    info = info,
                    discussionGroups = discussionGroups,
                    profileTitle = info.chat.title,
                    profileDescription = info.description ?: info.chat.description.orEmpty(),
                    profilePublicUsername = info.publicUsername ?: info.chat.publicUsername.orEmpty(),
                    isLoading = false
                )
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isLoading = false, errorMessage = error.message)
            }
        }
    }

    fun updateInviteTitle(value: String) {
        _uiState.value = _uiState.value.copy(inviteTitle = value.take(64))
    }

    fun updateInviteCreatesJoinRequest(value: Boolean) {
        _uiState.value = _uiState.value.copy(inviteCreatesJoinRequest = value)
    }

    fun updateInviteExpiresInHours(value: String) {
        _uiState.value = _uiState.value.copy(inviteExpiresInHours = value.digitsOnly(maxLength = 5))
    }

    fun updateInviteMaxUses(value: String) {
        _uiState.value = _uiState.value.copy(inviteMaxUses = value.digitsOnly(maxLength = 6))
    }

    fun updateInviteMemberLimit(value: String) {
        _uiState.value = _uiState.value.copy(inviteMemberLimit = value.digitsOnly(maxLength = 6))
    }

    fun updateTopicTitle(value: String) {
        _uiState.value = _uiState.value.copy(topicTitle = value.take(80))
    }

    fun updateProfileTitle(value: String) {
        _uiState.value = _uiState.value.copy(profileTitle = value.take(120), errorMessage = null)
        scheduleProfileAutosave()
    }

    fun updateProfileDescription(value: String) {
        _uiState.value = _uiState.value.copy(profileDescription = value.take(255), errorMessage = null)
        scheduleProfileAutosave()
    }

    fun updateProfilePublicUsername(value: String) {
        _uiState.value = _uiState.value.copy(
            profilePublicUsername = value
                .trim()
                .trimStart('@')
                .filter { it.isLetterOrDigit() || it == '_' }
                .take(32)
                .lowercase(),
            errorMessage = null
        )
        scheduleProfileAutosave()
    }

    private fun scheduleProfileAutosave() {
        profileAutosaveJob?.cancel()
        profileAutosaveJob = viewModelScope.launch {
            delay(ProfileAutosaveDelayMillis)
            profileAutosaveJob = null
            saveProfileFields()
        }
    }

    private fun saveProfileFields() {
        val state = _uiState.value
        val info = state.info ?: return
        if (!info.canEditProfile()) return
        if (state.isSaving) {
            scheduleProfileAutosave()
            return
        }

        val title = state.profileTitle.trim()
        if (title.isBlank()) {
            _uiState.value = state.copy(errorMessage = "Название не может быть пустым.")
            return
        }

        val description = state.profileDescription.trim()
        val publicUsername = state.profilePublicUsername.trim().trimStart('@')
        if (profileMatchesInfo(info, title, description, publicUsername)) {
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSaving = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    container.apiClient.updateChatState(
                        accessToken = session.accessToken,
                        chatId = chatId,
                        request = ChatStateRequest(
                            title = title,
                            description = description.takeIf { it.isNotBlank() },
                            publicUsername = publicUsername.takeIf { info.chat.type == ChatType.Channel && it.isNotBlank() },
                            clearDescription = description.isBlank(),
                            clearPublicUsername = info.chat.type == ChatType.Channel && publicUsername.isBlank()
                        )
                    )
                }
            }.onSuccess {
                val current = _uiState.value
                val changedAgain = current.profileTitle.trim() != title ||
                    current.profileDescription.trim() != description ||
                    current.profilePublicUsername.trim().trimStart('@') != publicUsername
                _uiState.value = current.copy(isSaving = false)
                if (changedAgain) {
                    scheduleProfileAutosave()
                } else {
                    refresh()
                }
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSaving = false, errorMessage = error.message)
            }
        }
    }

    fun createInviteLink() {
        val state = _uiState.value
        if (state.isSaving || state.info?.canInviteUsers != true) return
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSaving = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    container.apiClient.createChatInviteLink(
                        accessToken = session.accessToken,
                        chatId = chatId,
                        title = state.inviteTitle,
                        createsJoinRequest = state.inviteCreatesJoinRequest,
                        expiresAt = state.inviteExpiresInHours.toPositiveIntOrNull()?.let { hours ->
                            OffsetDateTime.now(ZoneOffset.UTC)
                                .plusHours(hours.toLong())
                                .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
                        },
                        maxUses = state.inviteMaxUses.toPositiveIntOrNull(),
                        memberLimit = state.inviteMemberLimit.toPositiveIntOrNull()
                    )
                }
            }.onSuccess {
                _uiState.value = _uiState.value.copy(
                    inviteTitle = "",
                    inviteCreatesJoinRequest = false,
                    inviteExpiresInHours = "",
                    inviteMaxUses = "",
                    inviteMemberLimit = "",
                    isSaving = false
                )
                refresh()
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSaving = false, errorMessage = error.message)
            }
        }
    }

    fun revokeInviteLink(link: ChatInviteLinkDto) {
        if (_uiState.value.isSaving || _uiState.value.info?.canInviteUsers != true) return
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSaving = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    container.apiClient.revokeChatInviteLink(session.accessToken, chatId, link.id)
                }
            }.onSuccess {
                _uiState.value = _uiState.value.copy(isSaving = false)
                refresh()
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSaving = false, errorMessage = error.message)
            }
        }
    }

    fun createTopic() {
        val state = _uiState.value
        val title = state.topicTitle.trim()
        if (state.isSaving || title.length < 2) return
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSaving = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    container.apiClient.createChatTopic(session.accessToken, chatId, title)
                }
            }.onSuccess {
                _uiState.value = _uiState.value.copy(topicTitle = "", isSaving = false)
                refresh()
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSaving = false, errorMessage = error.message)
            }
        }
    }

    fun setLinkedDiscussion(group: ChatSummaryDto) {
        updateLinkedDiscussion(ChatStateRequest(linkedDiscussionChatId = group.id))
    }

    fun clearLinkedDiscussion() {
        updateLinkedDiscussion(ChatStateRequest(clearLinkedDiscussionChat = true))
    }

    private fun updateLinkedDiscussion(request: ChatStateRequest) {
        val state = _uiState.value
        val info = state.info ?: return
        if (state.isSaving || info.chat.type != ChatType.Channel || !info.canManageChannel) {
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSaving = true, errorMessage = null)
            runCatching {
                container.executeAuthorized { session ->
                    container.apiClient.updateChatState(
                        accessToken = session.accessToken,
                        chatId = chatId,
                        request = request
                    )
                }
            }.onSuccess {
                _uiState.value = _uiState.value.copy(isSaving = false)
                refresh()
            }.onFailure { error ->
                _uiState.value = _uiState.value.copy(isSaving = false, errorMessage = error.message)
            }
        }
    }
}

private const val ProfileAutosaveDelayMillis = 600L

private fun String.digitsOnly(maxLength: Int): String =
    filter { it.isDigit() }.take(maxLength)

private fun String.toPositiveIntOrNull(): Int? =
    toIntOrNull()?.takeIf { it > 0 }

private fun ChatInfoDto.canEditProfile(): Boolean =
    chat.type != ChatType.Direct && (canManageChannel || chat.currentUserPermissions?.canManageMembers == true)

private fun profileMatchesInfo(
    info: ChatInfoDto,
    title: String,
    description: String,
    publicUsername: String
): Boolean {
    val currentDescription = (info.description ?: info.chat.description.orEmpty()).trim()
    val currentPublicUsername = (info.publicUsername ?: info.chat.publicUsername.orEmpty())
        .trim()
        .trimStart('@')
    return info.chat.title.trim() == title &&
        currentDescription == description &&
        (info.chat.type != ChatType.Channel || currentPublicUsername == publicUsername)
}
