/**
 * A collection of utilities for working with text.
 */

package theorycrafter.utils

import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration


/**
 * Trims any newlines from the end of an [AnnotatedString].
 */
fun AnnotatedString.trimNewlinesFromEnd(): AnnotatedString {
    val lastNonNewlineIndex = indexOfLast { it != '\n' }
    return subSequence(0, lastNonNewlineIndex + 1)
}


/**
 * The [SpanStyle] for italic text.
 */
private val ITALIC_STYLE = SpanStyle(fontStyle = FontStyle.Italic)


/**
 * The [SpanStyle] for bold text.
 */
private val BOLD_STYLE = SpanStyle(fontWeight = FontWeight.Bold)


/**
 * The [SpanStyle] for underline text.
 */
private val UNDERLINE_STYLE = SpanStyle(textDecoration = TextDecoration.Underline)


/**
 * The regular expression for an "<a>" tag and its contents.
 */
private val TAG_REGEX by lazy { "<(?!/?i\\b|/?b\\b|/?u\\b)[^>]*>".toRegex(RegexOption.IGNORE_CASE) }


/**
 * Converts HTML-styled text into an [AnnotatedString].
 * Supported tags:
 * - Italic: `<i>`
 * - Bold: `<b>`
 * - Underline: `<u>`
 * - Other tags (e.g. `<a>`) are stripped, leaving only their content.
 */
fun String.simpleStyleTagsToAnnotatedString(): AnnotatedString {
    val italicStack = ArrayList<Int>(1)
    val boldStack = ArrayList<Int>(1)
    val underlineStack = ArrayList<Int>(1)
    val spanStyles = mutableListOf<AnnotatedString.Range<SpanStyle>>()
    val linksStripped = this.replace(TAG_REGEX, "")
    val plainText = buildString {
        linksStripped.splitToSequence('<', '>').forEach {
            when (it) {
                "i", "I" -> italicStack.add(this.length)
                "b", "B" -> boldStack.add(this.length)
                "u", "U" -> underlineStack.add(this.length)
                "/i", "/I" -> spanStyles.add(
                    AnnotatedString.Range(ITALIC_STYLE, start = italicStack.removeLast(), this.length))
                "/b", "/B" -> spanStyles.add(
                    AnnotatedString.Range(BOLD_STYLE, start = boldStack.removeLast(), this.length))
                "/u", "/U" -> spanStyles.add(
                    AnnotatedString.Range(UNDERLINE_STYLE, start = underlineStack.removeLast(), this.length))
                else -> append(it)
            }
        }
    }

    return AnnotatedString(plainText, spanStyles = spanStyles)
}


/**
 * Returns the name that should be given to a copy of an item with the given name, in light of all the item names.
 * It selects the next available `$name copy $index`.
 */
fun copyName(name: String, allNames: Sequence<String>): String {
    val baseName =
        if (name.substringAfterLast(" copy ").toIntOrNull() != null)
            name.substringBeforeLast(" copy ")
        else
            name
    val maxCopyNumber = allNames.maxOfOrNull {
        it.substringAfter("$baseName copy ", missingDelimiterValue = "0").toIntOrNull() ?: 0
    } ?: 0
    return "$baseName copy ${maxCopyNumber+1}"
}


/**
 * If the given string has an integer as a substring, returns it; otherwise returns `null`.
 */
fun String.substringIntOrNull(): Int? {
    val firstDigitIndex = indexOfFirst { it.isDigit() }
    if (firstDigitIndex == -1)
        return null

    val endIndex = (firstDigitIndex + 1..lastIndex).firstOrNull { !get(it).isDigit() } ?: length
    return substring(firstDigitIndex, endIndex).toIntOrNull()
}