package ru.forbion.f7cloud.feature.talk

import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import ru.forbion.f7cloud.core.designsystem.F7Colors
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale

sealed class TalkChatItem {
    data class DateSeparator(val label: String) : TalkChatItem()
    data object UnreadMarker : TalkChatItem()
    data class MessageGroup(
        val messages: List<TalkMessage>,
        val outgoing: Boolean,
        val showAuthor: Boolean,
    ) : TalkChatItem()

    data class SystemLine(val text: String) : TalkChatItem()
}

/**
 * Substitutes Talk system-message placeholders ({actor}, {user1}, …) using API messageParameters.
 */
fun formatSystemMessage(
    message: String,
    parameters: Map<String, TalkMessageParameter>,
    fallback: String = "",
): String {
    if (message.isBlank()) return fallback
    if (parameters.isEmpty()) return message
    var result = message
    for ((key, param) in parameters) {
        val replacement = when (param.type) {
            "user", "guest", "call", "email", "user-group", "circle" -> "@${param.name}"
            "geo-location" -> param.name
            else -> param.name
        }
        if (replacement.isNotBlank()) {
            result = result.replace("{$key}", replacement)
        }
    }
    return result.ifBlank { fallback }
}

fun formatMessageText(raw: String): String {
    if (raw.isBlank()) return ""
    return raw
        .replace(Regex("""!\[[^\]]*]\([^)]*\)"""), "")
        .replace(Regex("""\[[^\]]*]\([^)]*\)""")) { it.value.substringAfter('[').substringBefore(']') }
        .replace(Regex("""^#{1,6}\s+""", RegexOption.MULTILINE), "")
        .replace(Regex("""[*_~`>]"""), "")
        .replace(Regex("""\{mention-[^}]+\}"""), "@")
        .replace(Regex("""\s+"""), " ")
        .trim()
}

fun buildMessageDisplayText(raw: String): AnnotatedString {
    if (raw.isBlank()) return AnnotatedString("")
    val cleaned = raw
        .replace(Regex("""!\[[^\]]*]\([^)]*\)"""), "")
        .replace(Regex("""\{mention-[^}]+\}"""), "@")
    val mentionPattern = Regex("""@"([^"\s]+)|@"([^"]+)"""")
    return buildAnnotatedString {
        var last = 0
        mentionPattern.findAll(cleaned).forEach { match ->
            val start = match.range.first
            if (start > last) {
                append(cleaned.substring(last, start))
            }
            append(match.value)
            addStyle(
                SpanStyle(color = F7Colors.Primary, fontWeight = FontWeight.SemiBold),
                start = length - match.value.length,
                end = length,
            )
            last = match.range.last + 1
        }
        if (last < cleaned.length) {
            append(cleaned.substring(last))
        }
    }
}

fun extractMarkdownImageUrl(raw: String): String? {
    val match = Regex("""!\[[^\]]*]\(([^)]+)\)""").find(raw) ?: return null
    return match.groupValues.getOrNull(1)?.trim()?.takeIf { it.startsWith("http") }
}

fun formatDateSeparator(epochSeconds: Long): String {
    val date = Date(epochSeconds * 1000L)
    val today = Calendar.getInstance()
    val then = Calendar.getInstance().apply { time = date }
    return when {
        today.get(Calendar.YEAR) == then.get(Calendar.YEAR) &&
            today.get(Calendar.DAY_OF_YEAR) == then.get(Calendar.DAY_OF_YEAR) -> "Сегодня"
        today.get(Calendar.YEAR) == then.get(Calendar.YEAR) &&
            today.get(Calendar.DAY_OF_YEAR) - then.get(Calendar.DAY_OF_YEAR) == 1 -> "Вчера"
        else -> SimpleDateFormat("d MMMM yyyy", Locale("ru")).format(date)
    }
}

fun buildChatItems(
    messages: List<TalkMessage>,
    lastReadMessageId: Long,
    selfActorId: String,
    collapseSystemMessages: Boolean,
): List<TalkChatItem> {
    if (messages.isEmpty()) return emptyList()
    val items = mutableListOf<TalkChatItem>()
    var lastDate: String? = null
    var unreadAdded = false
    var pendingSystem = mutableListOf<String>()
    var groupMessages = mutableListOf<TalkMessage>()
    var groupOutgoing = false
    var groupAuthor = ""

    fun isOutgoing(message: TalkMessage): Boolean =
        message.actorDisplayName.equals("Вы", ignoreCase = true) ||
            message.actorDisplayName.equals("You", ignoreCase = true) ||
            (message.actorId.isNotBlank() && message.actorId == selfActorId)

    fun groupShowAuthor(): Boolean = groupMessages.isNotEmpty() && !groupOutgoing

    fun flushSystem() {
        if (pendingSystem.isEmpty()) return
        if (!collapseSystemMessages) {
            pendingSystem.forEach { items += TalkChatItem.SystemLine(it) }
        }
        pendingSystem = mutableListOf()
    }

    fun flushGroup() {
        if (groupMessages.isEmpty()) return
        items += TalkChatItem.MessageGroup(
            messages = groupMessages.toList(),
            outgoing = groupOutgoing,
            showAuthor = groupShowAuthor(),
        )
        groupMessages = mutableListOf()
    }

    for (message in messages) {
        if (message.isSystemMessage) {
            flushGroup()
            val line = message.displayText
            if (line.isNotBlank()) pendingSystem += line
            continue
        }
        flushSystem()
        if (!unreadAdded && lastReadMessageId > 0 && message.id > lastReadMessageId) {
            flushGroup()
            items += TalkChatItem.UnreadMarker
            unreadAdded = true
        }
        if (message.timestamp > 0) {
            val dateKey = formatDateSeparator(message.timestamp)
            if (dateKey != lastDate) {
                flushGroup()
                items += TalkChatItem.DateSeparator(dateKey)
                lastDate = dateKey
            }
        }
        val outgoing = isOutgoing(message)
        val author = message.actorId.ifBlank { message.actorDisplayName }
        if (groupMessages.isEmpty()) {
            groupOutgoing = outgoing
            groupAuthor = author
            groupMessages += message
        } else if (outgoing == groupOutgoing && (outgoing || author == groupAuthor)) {
            groupMessages += message
        } else {
            flushGroup()
            groupOutgoing = outgoing
            groupAuthor = author
            groupMessages += message
        }
    }
    flushGroup()
    flushSystem()
    return items
}
