package ru.forbion.f7cloud.feature.f7support

import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
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.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 SupportUiState(
    val loading: Boolean = false,
    val config: SupportConfig? = null,
    val tickets: List<SupportTicket> = emptyList(),
    val selectedTicket: SupportTicket? = null,
    val messages: List<SupportMessage> = emptyList(),
    val sending: Boolean = false,
    val showCreate: Boolean = false,
    val showComplaint: Boolean = false,
    val complaintSending: Boolean = false,
    val createPendingFiles: List<SupportPendingFile> = emptyList(),
    val chatPendingFiles: List<SupportPendingFile> = emptyList(),
    val downloadingAttachmentId: Long? = null,
    val snackbar: String? = null,
    val error: String? = null,
    val unauthorized: Boolean = false,
) {
    val ticketsByBucket: Map<SupportStatusBucket, List<SupportTicket>>
        get() = SupportStatusBucket.entries.associateWith { bucket ->
            tickets
                .filter { it.statusBucket() == bucket }
                .sortedByDescending { it.activityAt }
        }

    val hasTickets: Boolean get() = tickets.isNotEmpty()
}

class SupportViewModel(
    private val repository: SupportRepository = SupportRepository(),
) : ViewModel() {
    private val _state = MutableStateFlow(SupportUiState())
    val state: StateFlow<SupportUiState> = _state.asStateFlow()
    private var cachedConfig: SupportConfig? = null
    private var pollJob: Job? = null

    fun load(session: AuthSession) {
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(loading = true, error = null)
            runCatching {
                val config = repository.loadConfig(session)
                cachedConfig = config
                val tickets = repository.listTickets(config)
                config to tickets
            }.onSuccess { (config, tickets) ->
                _state.value = _state.value.copy(
                    loading = false,
                    config = config,
                    tickets = tickets,
                    error = null,
                )
                restartPolling(session)
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    loading = false,
                    error = t.message,
                    unauthorized = t is UnauthorizedException,
                )
            }
        }
    }

    fun refreshTickets(session: AuthSession) {
        val config = cachedConfig ?: return
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { repository.listTickets(config) }
                .onSuccess { tickets ->
                    val selected = _state.value.selectedTicket
                    val updatedSelected = selected?.let { sel ->
                        tickets.find { it.ticketNumber == sel.ticketNumber } ?: sel
                    }
                    _state.value = _state.value.copy(
                        tickets = tickets,
                        selectedTicket = updatedSelected,
                    )
                }
        }
    }

    fun openTicket(session: AuthSession, ticketNumber: String) {
        val config = cachedConfig ?: return
        val ticket = _state.value.tickets.find { it.ticketNumber == ticketNumber }
            ?: SupportTicket(
                ticketNumber = ticketNumber,
                subject = "—",
                status = "",
                preview = "",
                hasUnread = false,
                activityAt = "",
                createdAt = "",
            )
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(
                loading = true,
                selectedTicket = ticket,
                messages = emptyList(),
                error = null,
            )
            runCatching {
                repository.loadMessages(config, ticketNumber)
            }.onSuccess { messages ->
                repository.markRead(config, ticketNumber)
                _state.value = _state.value.copy(
                    loading = false,
                    messages = messages,
                    tickets = _state.value.tickets.map {
                        if (it.ticketNumber == ticketNumber) it.copy(hasUnread = false) else it
                    },
                )
                restartPolling(session)
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    loading = false,
                    error = t.message,
                    unauthorized = t is UnauthorizedException,
                )
            }
        }
    }

    fun closeTicket(session: AuthSession) {
        _state.value = _state.value.copy(
            selectedTicket = null,
            messages = emptyList(),
            showComplaint = false,
            chatPendingFiles = emptyList(),
        )
        refreshTickets(session)
        restartPolling(session)
    }

    fun sendMessage(session: AuthSession, text: String) {
        val config = cachedConfig ?: return
        val ticket = _state.value.selectedTicket ?: return
        val trimmed = text.trim()
        val files = _state.value.chatPendingFiles
        if (trimmed.isEmpty() && files.isEmpty()) return
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(sending = true, error = null)
            runCatching {
                repository.sendMessageWithAttachments(config, ticket.ticketNumber, trimmed, files)
            }.onSuccess {
                val messages = repository.loadMessages(config, ticket.ticketNumber)
                _state.value = _state.value.copy(
                    sending = false,
                    messages = messages,
                    chatPendingFiles = emptyList(),
                )
                refreshTickets(session)
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    sending = false,
                    error = t.message,
                    unauthorized = t is UnauthorizedException,
                )
            }
        }
    }

    fun setShowCreate(show: Boolean) {
        _state.value = _state.value.copy(
            showCreate = show,
            error = null,
            createPendingFiles = if (show) _state.value.createPendingFiles else emptyList(),
        )
    }

    fun setShowComplaint(show: Boolean) {
        _state.value = _state.value.copy(showComplaint = show, error = null)
    }

    fun createTicket(session: AuthSession, subject: String, body: String) {
        val config = cachedConfig ?: return
        val files = _state.value.createPendingFiles
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(loading = true, error = null)
            runCatching {
                repository.createTicket(config, subject.trim(), body.trim(), files)
            }.onSuccess { ticketNumber ->
                val tickets = repository.listTickets(config)
                _state.value = _state.value.copy(
                    loading = false,
                    showCreate = false,
                    createPendingFiles = emptyList(),
                    tickets = tickets,
                )
                openTicket(session, ticketNumber)
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    loading = false,
                    error = t.message,
                    unauthorized = t is UnauthorizedException,
                )
            }
        }
    }

    fun addCreateFiles(context: Context, uris: List<Uri>) {
        if (uris.isEmpty()) return
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { SupportFileIO.readUris(context, uris) }
                .onSuccess { files ->
                    if (files.isEmpty()) return@onSuccess
                    _state.value = _state.value.copy(
                        createPendingFiles = _state.value.createPendingFiles + files,
                    )
                }
                .onFailure { t ->
                    _state.value = _state.value.copy(error = t.message)
                }
        }
    }

    fun removeCreateFile(localId: String) {
        _state.value = _state.value.copy(
            createPendingFiles = _state.value.createPendingFiles.filterNot { it.localId == localId },
        )
    }

    fun addChatFiles(context: Context, uris: List<Uri>) {
        if (uris.isEmpty()) return
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { SupportFileIO.readUris(context, uris) }
                .onSuccess { files ->
                    if (files.isEmpty()) return@onSuccess
                    _state.value = _state.value.copy(
                        chatPendingFiles = _state.value.chatPendingFiles + files,
                    )
                }
                .onFailure { t ->
                    _state.value = _state.value.copy(error = t.message)
                }
        }
    }

    fun removeChatFile(localId: String) {
        _state.value = _state.value.copy(
            chatPendingFiles = _state.value.chatPendingFiles.filterNot { it.localId == localId },
        )
    }

    fun downloadAttachment(
        context: Context,
        attachment: SupportAttachment,
    ) {
        val config = cachedConfig ?: return
        val ticket = _state.value.selectedTicket ?: return
        if (_state.value.downloadingAttachmentId == attachment.id) return
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(downloadingAttachmentId = attachment.id)
            runCatching {
                repository.downloadAttachment(config, ticket.ticketNumber, attachment.id)
            }.onSuccess { (bytes, mime) ->
                SupportFileIO.openBytes(
                    context = context,
                    fileName = attachment.filename,
                    bytes = bytes,
                    mimeType = mime.ifBlank { attachment.mimeType },
                )
                _state.value = _state.value.copy(downloadingAttachmentId = null)
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    downloadingAttachmentId = null,
                    error = t.message,
                )
            }
        }
    }

    fun submitComplaint(text: String) {
        val config = cachedConfig ?: return
        val ticket = _state.value.selectedTicket ?: return
        val trimmed = text.trim()
        if (trimmed.isEmpty()) return
        viewModelScope.launch(Dispatchers.IO) {
            _state.value = _state.value.copy(complaintSending = true, error = null)
            runCatching {
                repository.submitComplaint(
                    config = config,
                    ticketNumber = ticket.ticketNumber,
                    ticketSubject = ticket.subject,
                    text = trimmed,
                )
            }.onSuccess {
                _state.value = _state.value.copy(
                    complaintSending = false,
                    showComplaint = false,
                    snackbar = "Жалоба отправлена",
                )
            }.onFailure { t ->
                _state.value = _state.value.copy(
                    complaintSending = false,
                    error = t.message,
                    unauthorized = t is UnauthorizedException,
                )
            }
        }
    }

    fun clearSnackbar() {
        _state.value = _state.value.copy(snackbar = null)
    }

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

    fun openTicketFromPush(session: AuthSession, ticketNumber: String) {
        if (cachedConfig == null) {
            viewModelScope.launch(Dispatchers.IO) {
                runCatching { repository.loadConfig(session) }
                    .onSuccess { config ->
                        cachedConfig = config
                        _state.value = _state.value.copy(config = config)
                        openTicket(session, ticketNumber)
                    }
            }
        } else {
            openTicket(session, ticketNumber)
        }
    }

    private fun restartPolling(session: AuthSession) {
        pollJob?.cancel()
        pollJob = viewModelScope.launch(Dispatchers.IO) {
            while (isActive) {
                delay(pollIntervalMs())
                if (!AppForegroundTracker.isForeground) continue
                val config = cachedConfig ?: continue
                val selected = _state.value.selectedTicket
                runCatching {
                    if (selected != null) {
                        val messages = repository.loadMessages(config, selected.ticketNumber)
                        val tickets = repository.listTickets(config)
                        Pair(messages, tickets)
                    } else {
                        Pair(null, repository.listTickets(config))
                    }
                }.onSuccess { (messages, tickets) ->
                    if (selected != null && messages != null) {
                        val updatedSelected = tickets.find { it.ticketNumber == selected.ticketNumber }
                        _state.value = _state.value.copy(
                            messages = messages,
                            tickets = tickets,
                            selectedTicket = updatedSelected ?: selected,
                        )
                    } else {
                        _state.value = _state.value.copy(tickets = tickets)
                    }
                }
            }
        }
    }

    private fun pollIntervalMs(): Long =
        if (_state.value.selectedTicket != null) POLL_TICKET_MS else POLL_LIST_MS

    override fun onCleared() {
        pollJob?.cancel()
        super.onCleared()
    }

    companion object {
        private const val POLL_LIST_MS = 15_000L
        private const val POLL_TICKET_MS = 8_000L
    }
}
