package ru.forbion.f7cloud.feature.mail

import android.content.Context
import android.content.Intent
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
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
import ru.forbion.f7cloud.feature.contacts.ContactItem
import ru.forbion.f7cloud.feature.contacts.ContactRecipientHelper
import ru.forbion.f7cloud.feature.contacts.ContactsRepository
import ru.forbion.f7cloud.feature.files.FilesApiRepository
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId

enum class MailRecipientField {
    TO,
    CC,
    BCC,
}

data class MailComposeUiState(
    val to: String = "",
    val cc: String = "",
    val bcc: String = "",
    val subject: String = "",
    val showCcBcc: Boolean = false,
    val requestMdn: Boolean = false,
    val toolbarVisible: Boolean = false,
    val moreMenuOpen: Boolean = false,
    val sendLaterMenuOpen: Boolean = false,
    val selectedSendPreset: MailSendLaterPreset = MailSendLaterPreset.NOW,
    val customSendAt: LocalDateTime = LocalDateTime.now().withSecond(0).withNano(0),
    val attachments: List<MailComposeAttachment> = emptyList(),
    val uploadingAttachments: Boolean = false,
    val attachmentMenuOpen: Boolean = false,
    val sending: Boolean = false,
    val error: String? = null,
    val finished: Boolean = false,
    val activeRecipientField: MailRecipientField? = null,
    val recipientSuggestions: List<ContactItem> = emptyList(),
)

class MailComposeViewModel(
    private val launch: MailComposeLaunch,
    private val repository: MailRepository = MailRepository(),
    private val filesApiRepository: FilesApiRepository = FilesApiRepository(),
    contactsRepository: ContactsRepository? = null,
) : ViewModel() {
    private val contactsRepository = contactsRepository
    private val _state = MutableStateFlow(MailComposeUiState())
    val state: StateFlow<MailComposeUiState> = _state.asStateFlow()
    private var cachedContacts: List<ContactItem> = emptyList()
    private var nextCloudAttachmentId = -1
    private var pendingFilesPickMode: MailFilesPickMode? = null

    init {
        val customSendAt = launch.initialCustomSendAtEpochSeconds?.let { epoch ->
            LocalDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault())
        } ?: LocalDateTime.now().withSecond(0).withNano(0)
        _state.value = MailComposeUiState(
            to = launch.initialTo,
            cc = launch.initialCc,
            subject = launch.initialSubject,
            showCcBcc = launch.showCcBcc || launch.initialCc.isNotBlank(),
            selectedSendPreset = launch.initialSendPreset,
            customSendAt = customSendAt,
        )
    }

    val sendLaterOptions: List<MailSendLaterOption> = MailComposeSchedule.options()

    val senderLabel: String
        get() {
            val email = launch.accountEmail
            val name = launch.accountName?.takeIf { it.isNotBlank() }
            return if (name != null) "$name <$email>" else email
        }

    fun initContacts(session: AuthSession) {
        val repo = contactsRepository ?: return
        viewModelScope.launch {
            launch(Dispatchers.IO) {
                runCatching { repo.syncContacts(session) }
            }
            repo.observeContacts(session).collect { contacts ->
                cachedContacts = contacts
                val active = _state.value.activeRecipientField
                if (active != null) {
                    val current = currentValue(active)
                    updateSuggestions(active, current)
                }
            }
        }
    }

    fun setTo(value: String) = updateRecipient(MailRecipientField.TO, value)
    fun setCc(value: String) = updateRecipient(MailRecipientField.CC, value)
    fun setBcc(value: String) = updateRecipient(MailRecipientField.BCC, value)

    fun selectSuggestion(field: MailRecipientField, contact: ContactItem) {
        val current = currentValue(field)
        val formatted = ContactRecipientHelper.formatRecipient(contact.displayName, contact.email)
        val updated = ContactRecipientHelper.replaceCurrentToken(current, formatted) + ", "
        _state.update {
            when (field) {
                MailRecipientField.TO -> it.copy(
                    to = updated,
                    activeRecipientField = field,
                    recipientSuggestions = emptyList(),
                )
                MailRecipientField.CC -> it.copy(
                    cc = updated,
                    activeRecipientField = field,
                    recipientSuggestions = emptyList(),
                )
                MailRecipientField.BCC -> it.copy(
                    bcc = updated,
                    activeRecipientField = field,
                    recipientSuggestions = emptyList(),
                )
            }
        }
    }

    fun dismissSuggestions() {
        _state.update { it.copy(activeRecipientField = null, recipientSuggestions = emptyList()) }
    }

    fun setSubject(value: String) = _state.update { it.copy(subject = value) }
    fun toggleCcBcc() = _state.update { it.copy(showCcBcc = !it.showCcBcc) }
    fun toggleToolbar() = _state.update {
        it.copy(
            toolbarVisible = !it.toolbarVisible,
            moreMenuOpen = false,
            sendLaterMenuOpen = false,
            attachmentMenuOpen = false,
        )
    }
    fun setMoreMenuOpen(open: Boolean) = _state.update {
        it.copy(moreMenuOpen = open, sendLaterMenuOpen = false, attachmentMenuOpen = false)
    }
    fun setSendLaterMenuOpen(open: Boolean) = _state.update { it.copy(sendLaterMenuOpen = open, moreMenuOpen = false) }
    fun toggleRequestMdn() = _state.update { it.copy(requestMdn = !it.requestMdn) }

    fun selectSendPreset(preset: MailSendLaterPreset) {
        _state.update {
            it.copy(
                selectedSendPreset = preset,
                sendLaterMenuOpen = preset != MailSendLaterPreset.CUSTOM,
            )
        }
    }

    fun setCustomSendAt(value: LocalDateTime) {
        _state.update {
            it.copy(
                customSendAt = value,
                selectedSendPreset = MailSendLaterPreset.CUSTOM,
            )
        }
    }

    fun setAttachmentMenuOpen(open: Boolean) = _state.update {
        it.copy(attachmentMenuOpen = open, moreMenuOpen = false, sendLaterMenuOpen = false)
    }

    fun prepareFilesPick(mode: MailFilesPickMode) {
        pendingFilesPickMode = mode
        setAttachmentMenuOpen(false)
    }

    fun filesPickIntent(context: Context, session: AuthSession): Intent? {
        val mode = pendingFilesPickMode ?: return null
        val pickMode = when (mode) {
            MailFilesPickMode.ATTACHMENT -> MailFilesPickerActivity.MODE_ATTACHMENT
            MailFilesPickMode.SHARE_LINK -> MailFilesPickerActivity.MODE_SHARE_LINK
        }
        return MailFilesPickerActivity.intent(context, session, pickMode)
    }

    fun cancelFilesPick() {
        pendingFilesPickMode = null
    }

    fun handleFilesPickResult(
        data: Intent,
        session: AuthSession,
        editor: MailRichTextEditorController,
        onUnauthorized: () -> Unit,
    ) {
        val mode = pendingFilesPickMode ?: return
        pendingFilesPickMode = null
        val path = data.getStringExtra(MailFilesPickerActivity.RESULT_PATH).orEmpty()
        if (path.isBlank()) return
        when (mode) {
            MailFilesPickMode.ATTACHMENT -> addCloudAttachment(
                path = path,
                fileName = data.getStringExtra(MailFilesPickerActivity.RESULT_NAME).orEmpty().ifBlank {
                    path.substringAfterLast('/')
                },
                mimeType = data.getStringExtra(MailFilesPickerActivity.RESULT_MIME).orEmpty()
                    .ifBlank { "application/octet-stream" },
                size = data.getLongExtra(MailFilesPickerActivity.RESULT_SIZE, 0L).takeIf { it > 0L },
            )
            MailFilesPickMode.SHARE_LINK -> insertShareLinkFromFile(
                session = session,
                path = path,
                editor = editor,
                onUnauthorized = onUnauthorized,
            )
        }
    }

    fun addAttachmentsFromUris(
        context: Context,
        session: AuthSession,
        uris: List<android.net.Uri>,
        onUnauthorized: () -> Unit,
    ) {
        if (uris.isEmpty() || _state.value.uploadingAttachments) return
        viewModelScope.launch {
            _state.update { it.copy(uploadingAttachments = true, error = null) }
            try {
                val uploaded = kotlinx.coroutines.withContext(Dispatchers.IO) {
                    buildList {
                        uris.forEach { uri ->
                            val (name, bytes, mime) = MailAttachmentIO.readUri(context, uri)
                            add(repository.uploadLocalAttachment(session, name, bytes, mime))
                        }
                    }
                }
                _state.update {
                    it.copy(
                        attachments = it.attachments + uploaded,
                        uploadingAttachments = false,
                    )
                }
            } catch (_: UnauthorizedException) {
                _state.update { it.copy(uploadingAttachments = false) }
                onUnauthorized()
            } catch (e: Exception) {
                _state.update {
                    it.copy(
                        uploadingAttachments = false,
                        error = e.message ?: "Не удалось загрузить вложение",
                    )
                }
            }
        }
    }

    private fun addCloudAttachment(
        path: String,
        fileName: String,
        mimeType: String,
        size: Long?,
    ) {
        val cloudPath = if (path.startsWith("/")) path else "/$path"
        val attachment = MailComposeAttachment(
            id = nextCloudAttachmentId--,
            fileName = fileName,
            mimeType = mimeType,
            type = MailComposeAttachmentType.CLOUD,
            cloudPath = cloudPath,
            size = size,
        )
        _state.update { it.copy(attachments = it.attachments + attachment) }
    }

    fun insertShareLinkFromFile(
        session: AuthSession,
        path: String,
        editor: MailRichTextEditorController,
        onUnauthorized: () -> Unit,
    ) {
        if (_state.value.uploadingAttachments) return
        viewModelScope.launch {
            _state.update { it.copy(uploadingAttachments = true, error = null) }
            try {
                val shareUrl = kotlinx.coroutines.withContext(Dispatchers.IO) {
                    filesApiRepository.createPublicShareLink(session, path)
                }
                val html = """<a href="$shareUrl">$shareUrl</a>"""
                editor.insertHtml(html)
                _state.update { it.copy(uploadingAttachments = false) }
            } catch (_: UnauthorizedException) {
                _state.update { it.copy(uploadingAttachments = false) }
                onUnauthorized()
            } catch (e: Exception) {
                _state.update {
                    it.copy(
                        uploadingAttachments = false,
                        error = e.message ?: "Не удалось создать ссылку",
                    )
                }
            }
        }
    }

    fun removeAttachment(id: Int) {
        _state.update { it.copy(attachments = it.attachments.filterNot { att -> att.id == id }) }
    }

    fun send(
        session: AuthSession,
        editor: MailRichTextEditorController,
        onUnauthorized: () -> Unit,
    ) {
        val current = _state.value
        if (current.sending) return
        val recipients = repository.parseRecipients(current.to)
        if (recipients.isEmpty()) {
            _state.update { it.copy(error = "Укажите получателя") }
            return
        }
        _state.update { it.copy(sending = true, error = null, recipientSuggestions = emptyList()) }
        viewModelScope.launch {
            try {
                val bodyHtml = editor.html()
                val bodyPlain = editor.plainText()
                val sendAt = when (current.selectedSendPreset) {
                    MailSendLaterPreset.NOW -> null
                    MailSendLaterPreset.TOMORROW_MORNING ->
                        sendLaterOptions.first { it.preset == MailSendLaterPreset.TOMORROW_MORNING }.sendAtEpochSeconds?.toInt()
                    MailSendLaterPreset.TOMORROW_AFTERNOON ->
                        sendLaterOptions.first { it.preset == MailSendLaterPreset.TOMORROW_AFTERNOON }.sendAtEpochSeconds?.toInt()
                    MailSendLaterPreset.MONDAY_MORNING ->
                        sendLaterOptions.first { it.preset == MailSendLaterPreset.MONDAY_MORNING }.sendAtEpochSeconds?.toInt()
                    MailSendLaterPreset.CUSTOM ->
                        current.customSendAt.atZone(java.time.ZoneId.systemDefault()).toEpochSecond().toInt()
                }
                repository.createAndSendMessage(
                    session = session,
                    accountId = launch.accountId,
                    to = recipients,
                    cc = repository.parseRecipients(current.cc),
                    bcc = repository.parseRecipients(current.bcc),
                    subject = current.subject,
                    bodyHtml = bodyHtml,
                    bodyPlain = bodyPlain,
                    editorBody = bodyHtml,
                    requestMdn = current.requestMdn,
                    sendAt = sendAt,
                    attachments = current.attachments,
                )
                _state.update { it.copy(sending = false, finished = true) }
            } catch (_: UnauthorizedException) {
                _state.update { it.copy(sending = false) }
                onUnauthorized()
            } catch (e: Exception) {
                _state.update { it.copy(sending = false, error = e.message ?: "Ошибка отправки") }
            }
        }
    }

    private fun updateRecipient(field: MailRecipientField, value: String) {
        _state.update {
            when (field) {
                MailRecipientField.TO -> it.copy(to = value, activeRecipientField = field)
                MailRecipientField.CC -> it.copy(cc = value, activeRecipientField = field)
                MailRecipientField.BCC -> it.copy(bcc = value, activeRecipientField = field)
            }
        }
        updateSuggestions(field, value)
    }

    private fun updateSuggestions(field: MailRecipientField, value: String) {
        val repo = contactsRepository
        val token = ContactRecipientHelper.currentToken(value)
        val suggestions = if (repo != null && token.isNotBlank()) {
            repo.filterSuggestions(cachedContacts, token)
        } else {
            emptyList()
        }
        _state.update {
            it.copy(
                activeRecipientField = field,
                recipientSuggestions = suggestions,
            )
        }
    }

    private fun currentValue(field: MailRecipientField): String = when (field) {
        MailRecipientField.TO -> _state.value.to
        MailRecipientField.CC -> _state.value.cc
        MailRecipientField.BCC -> _state.value.bcc
    }
}
