/*
 * Episteme Reader - A native Android document reader.
 * Copyright (C) 2026 Episteme
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * mail: epistemereader@gmail.com
 */
package com.aryan.reader.paginatedreader

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.text.ParagraphStyle
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.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.isSpecified
import kotlin.math.roundToInt

private const val IMPORTANT_SPECIFICITY_BOOST = 10_000

private object ReaderCssLog {
    fun d(@Suppress("UNUSED_PARAMETER") message: String) = Unit
    fun w(@Suppress("UNUSED_PARAMETER") message: String) = Unit
    fun w(@Suppress("UNUSED_PARAMETER") throwable: Throwable, @Suppress("UNUSED_PARAMETER") message: String) = Unit
    fun e(@Suppress("UNUSED_PARAMETER") throwable: Throwable, @Suppress("UNUSED_PARAMETER") message: String) = Unit
}

private fun Color.luminance(): Float {
    if (!this.isSpecified) return 0f
    return (0.299f * red + 0.587f * green + 0.114f * blue)
}

private fun resolveCssRelativePath(cssPath: String, rawSrc: String): String {
    if (rawSrc.startsWith("/") || rawSrc.contains("://")) return rawSrc
    val normalizedBase = cssPath.replace('\\', '/').substringBeforeLast('/', "")
    val parts = ArrayDeque<String>()
    (if (normalizedBase.isBlank()) rawSrc else "$normalizedBase/$rawSrc")
        .replace('\\', '/')
        .split('/')
        .forEach { part ->
            when (part) {
                "", "." -> Unit
                ".." -> if (parts.isNotEmpty()) parts.removeLast()
                else -> parts.addLast(part)
            }
        }
    return parts.joinToString("/")
}

object CssParser {
    private val FONT_FACE_REGEX = "@font-face\\s*\\{([^}]+)\\}".toRegex(RegexOption.DOT_MATCHES_ALL)
    private val URL_REGEX = "url\\((['\"]?)(.*?)\\1\\)".toRegex()
    private val ID_SELECTOR_REGEX = Regex("#[^\\s,]+")
    private val CLASS_ATTRIBUTE_SELECTOR_REGEX = Regex("\\.[^\\s,]+|\\[[^]]+]|:(?!:)[^\\s,]+")
    private val TYPE_PSEUDO_ELEMENT_SELECTOR_REGEX = Regex("(?<![.#\\[])\\b[a-zA-Z-]+|::[a-zA-Z-]+")
    private data class FontSource(val url: String, val format: String?)

    // Regex to identify simple, single-part selectors for fast categorization
    private val SIMPLE_TAG_SELECTOR = Regex("^[a-zA-Z0-9]+$")
    private val SIMPLE_CLASS_SELECTOR = Regex("^\\.[a-zA-Z0-9_-]+$")
    private val SIMPLE_ID_SELECTOR = Regex("^#[a-zA-Z0-9_-]+$")

    private val BORDER_WIDTH_KEYWORDS = mapOf(
        "thin" to 1.dp,
        "medium" to 3.dp,
        "thick" to 5.dp
    )

    fun adaptColorForTheme(
        color: Color,
        isDarkTheme: Boolean,
        isBackground: Boolean,
        themeBackground: Color = Color.Unspecified,
        themeText: Color = Color.Unspecified
    ): Color {
        if (!color.isSpecified) return color
        if (color.alpha < 0.9f && color != Color.Transparent) return color
        if (color == Color.Transparent) return color

        if (!themeBackground.isSpecified || !themeText.isSpecified) {
            val luminance = color.luminance()
            return if (isDarkTheme) {
                if (isBackground) {
                    if (luminance > 0.9) Color.Transparent else color
                } else {
                    if (luminance < 0.2) Color.White.copy(alpha = 0.87f) else color
                }
            } else {
                if (isBackground) {
                    if (luminance < 0.1) Color.Transparent else color
                } else {
                    if (luminance > 0.8) Color.Black.copy(alpha = 0.87f) else color
                }
            }
        }

        val bgLuminance = themeBackground.luminance()
        val colorLuminance = color.luminance()

        val l1 = maxOf(bgLuminance, colorLuminance)
        val l2 = minOf(bgLuminance, colorLuminance)
        val contrast = (l1 + 0.05f) / (l2 + 0.05f)

        if (isBackground) {
            return if (isDarkTheme && colorLuminance > 0.5f) {
                Color.Transparent
            } else if (!isDarkTheme && colorLuminance < 0.2f) {
                Color.Transparent
            } else {
                color
            }
        } else {
            if (contrast >= 4.5f) {
                return color
            }

            return themeText.takeIf { it.isSpecified } ?: color
        }
    }

    private fun splitDeclarations(declarations: String): List<String> {
        val parts = declarations.split(';').toMutableList()
        if (parts.size <= 1) return parts

        val result = mutableListOf<String>()
        val iterator = parts.listIterator()
        while(iterator.hasNext()) {
            var current = iterator.next()
            val originalCurrent = current
            var reassembled = false
            while (current.count { it == '(' } > current.count { it == ')' }) {
                if (!iterator.hasNext()) break
                val nextPart = iterator.next()
                current += ";$nextPart"
                reassembled = true
            }
            if (reassembled) {
                ReaderCssLog.d("Reassembled declaration. Original: '$originalCurrent'. Final: '$current'")
            }
            result.add(current)
        }
        return result
    }

    private fun calculateSpecificity(selector: String): Int {
        val ids = ID_SELECTOR_REGEX.findAll(selector).count()
        val classesAndAttributes = CLASS_ATTRIBUTE_SELECTOR_REGEX.findAll(selector).count()
        val elementsAndPseudos = TYPE_PSEUDO_ELEMENT_SELECTOR_REGEX.findAll(selector).count()
        val specificity = ids * 100 + classesAndAttributes * 10 + elementsAndPseudos
        return specificity
    }

    fun parse(
        cssContent: String,
        cssPath: String?,
        baseFontSizeSp: Float,
        density: Float,
        constraints: Constraints,
        isDarkTheme: Boolean,
        themeBackgroundColor: Color = Color.Unspecified,
        themeTextColor: Color = Color.Unspecified,
        adaptThemeColors: Boolean = true
    ): OptimizedCssParseResult {
        val byTag = mutableMapOf<String, MutableList<CssRule>>()
        val byClass = mutableMapOf<String, MutableList<CssRule>>()
        val byId = mutableMapOf<String, MutableList<CssRule>>()
        val otherComplex = mutableListOf<CssRule>()
        val fontFaces = mutableListOf<FontFaceInfo>()

        val blockRegex = "([^{}]+)\\s*\\{([^}]+)\\}".toRegex()

        var cleanedCss = cssContent.replace(Regex("/\\*.*?\\*/", RegexOption.DOT_MATCHES_ALL), "")

        val mediaQueryRegex = Regex("@media[^{]+\\{((?>[^{}]+|\\{[^{}]*\\})*)\\}")
        mediaQueryRegex.findAll(cleanedCss).forEach { match ->
            val condition = match.groups[0]?.value?.trim() ?: ""
            if (adaptThemeColors && isDarkTheme && condition.contains("prefers-color-scheme: dark")) {
                val darkCss = match.groups[1]?.value ?: ""
                cleanedCss += "\n$darkCss"
            }
        }
        cleanedCss = mediaQueryRegex.replace(cleanedCss, "")

        ReaderCssLog.d("CssParser: Checking for @font-face rules...")
        val fontFaceMatches = FONT_FACE_REGEX.findAll(cleanedCss)
        if (!fontFaceMatches.any()) {
            ReaderCssLog.d("CssParser: No @font-face rules found by regex.")
        }
        fontFaceMatches.forEach { match ->
            ReaderCssLog.d("CssParser: Found a @font-face block. Parsing its properties.")
            val properties = match.groupValues[1]
            parseFontFace(properties, cssPath)?.let { fontFaces.add(it) }
        }
        cleanedCss = FONT_FACE_REGEX.replace(cleanedCss, "")

        blockRegex.findAll(cleanedCss).forEach { matchResult ->
            val selectorGroup = matchResult.groups[1]?.value?.trim() ?: ""
            val propertiesGroup = matchResult.groups[2]?.value?.trim() ?: ""

            val selectors = selectorGroup.split(',').map { it.trim() }

            for (originalSelector in selectors) {
                if (originalSelector.isBlank() || originalSelector.startsWith("@")) {
                    continue
                }
                val sanitizedSelector = originalSelector.replace(
                    Regex(":(link|visited|hover|active|focus)\\b|::(first-letter|first-line|marker)\\b", RegexOption.IGNORE_CASE),
                    ""
                ).trim()
                if (sanitizedSelector.isBlank()) {
                    continue
                }
                val specificity = calculateSpecificity(originalSelector)
                val normalStyle = parseProperties(
                    properties = propertiesGroup,
                    baseFontSizeSp = baseFontSizeSp,
                    density = density,
                    constraints = constraints,
                    onlyImportant = false,
                    isDarkTheme = isDarkTheme,
                    themeBackgroundColor = themeBackgroundColor,
                    themeTextColor = themeTextColor,
                    adaptThemeColors = adaptThemeColors
                )
                val importantStyle = parseProperties(
                    properties = propertiesGroup,
                    baseFontSizeSp = baseFontSizeSp,
                    density = density,
                    constraints = constraints,
                    onlyImportant = true,
                    isDarkTheme = isDarkTheme,
                    themeBackgroundColor = themeBackgroundColor,
                    themeTextColor = themeTextColor,
                    adaptThemeColors = adaptThemeColors
                )

                fun addRule(style: CssStyle, spec: Int) {
                    if (style == CssStyle()) return
                    val rule = CssRule(CssSelector(sanitizedSelector, spec), style)
                    when {
                        SIMPLE_ID_SELECTOR.matches(sanitizedSelector) ->
                            byId.getOrPut(sanitizedSelector.substring(1)) { mutableListOf() }.add(rule)
                        SIMPLE_CLASS_SELECTOR.matches(sanitizedSelector) ->
                            byClass.getOrPut(sanitizedSelector.substring(1)) { mutableListOf() }.add(rule)
                        SIMPLE_TAG_SELECTOR.matches(sanitizedSelector) ->
                            byTag.getOrPut(sanitizedSelector) { mutableListOf() }.add(rule)
                        else -> otherComplex.add(rule)
                    }
                }

                addRule(normalStyle, specificity)
                addRule(importantStyle, specificity + IMPORTANT_SPECIFICITY_BOOST)
            }
        }
        val optimizedRules = OptimizedCssRules(byTag, byClass, byId, otherComplex)
        return OptimizedCssParseResult(optimizedRules, fontFaces)
    }

    private fun parseFontFace(properties: String, cssPath: String?): FontFaceInfo? {
        val propsMap = splitDeclarations(properties)
            .map { it.trim().split(':', limit = 2).map { part -> part.trim() } }
            .filter { it.size == 2 && it[0].isNotBlank() }
            .associate { it[0].lowercase() to it[1] }

        val fontFamily = propsMap["font-family"]?.removeSurrounding("\"")?.removeSurrounding("'")?.lowercase()
        val srcString = propsMap["src"]
        ReaderCssLog.d("Parsing font-face for family: $fontFamily. Raw src string: $srcString")

        if (fontFamily == null || srcString == null) {
            ReaderCssLog.w("Incomplete @font-face rule: missing font-family or src.")
            return null
        }

        val urlWithFormatRegex = "url\\((['\"]?)(.*?)\\1\\)\\s*format\\((['\"]?)(.*?)\\3\\)".toRegex()

        val sources = srcString.split(Regex(",(?=\\s*url\\()")).mapNotNull { part ->
            val trimmedPart = part.trim()
            ReaderCssLog.d("Processing src part: '$trimmedPart'")

            urlWithFormatRegex.find(trimmedPart)?.let {
                ReaderCssLog.d("Matched url with format(). URL: ${it.groupValues[2]}, Format: ${it.groupValues[4]}")
                FontSource(url = it.groupValues[2], format = it.groupValues[4].lowercase().removeSurrounding("'"))
            } ?: URL_REGEX.find(trimmedPart)?.let {
                val url = it.groupValues[2]
                ReaderCssLog.d("Matched url() only. URL: '$url'")
                val format = when {
                    url.startsWith("data:", ignoreCase = true) -> {
                        val mediaType = url.substringAfter("data:").substringBefore(';')
                        ReaderCssLog.d("Data URI detected. Media type: '$mediaType'")
                        when {
                            mediaType.contains("opentype") -> "opentype"
                            mediaType.contains("truetype") -> "truetype"
                            mediaType.contains("woff2") -> "woff2"
                            mediaType.contains("woff") -> "woff"
                            else -> {
                                ReaderCssLog.w("Unknown data URI media type: $mediaType")
                                null
                            }
                        }
                    }
                    url.endsWith(".woff2", ignoreCase = true) -> "woff2"
                    url.endsWith(".woff", ignoreCase = true) -> "woff"
                    url.endsWith(".otf", ignoreCase = true) -> "opentype"
                    url.endsWith(".ttf", ignoreCase = true) -> "truetype"
                    else -> {
                        ReaderCssLog.w("Could not determine format from URL: $url")
                        null
                    }
                }
                ReaderCssLog.d("Determined format: '$format'")
                if (format != null) {
                    FontSource(url = url, format = format)
                } else {
                    null
                }
            }
        }

        if (sources.isEmpty()) {
            ReaderCssLog.w("Could not parse any valid source from @font-face src: $srcString")
            return null
        }

        val preferredSource = sources.minByOrNull {
            when (it.format) {
                "opentype", "otf" -> 1
                "truetype", "ttf" -> 2
                "woff2" -> 3
                "woff" -> 4
                else -> 5
            }
        }!!

        val rawSrc = preferredSource.url
        ReaderCssLog.d("Selected font source for '$fontFamily': '${preferredSource.url}' with format '${preferredSource.format}'")

        val finalSrc = if (cssPath != null && !rawSrc.startsWith("data:")) {
            try {
                resolveCssRelativePath(cssPath, rawSrc)
            } catch (e: Exception) {
                ReaderCssLog.e(e, "Could not resolve font path for src '$rawSrc' in css '$cssPath'")
                rawSrc // Fallback to the raw path on error
            }
        } else {
            rawSrc
        }
        val fontWeight = when (propsMap["font-weight"]) {
            "bold" -> FontWeight.Bold
            "700" -> FontWeight.Bold
            "600" -> FontWeight.SemiBold
            "500" -> FontWeight.Medium
            "300" -> FontWeight.Light
            "200" -> FontWeight.ExtraLight
            "100" -> FontWeight.Thin
            else -> FontWeight.Normal
        }

        val fontStyle = when (propsMap["font-style"]) {
            "italic", "oblique" -> FontStyle.Italic
            else -> FontStyle.Normal
        }

        return FontFaceInfo(fontFamily, finalSrc, fontWeight, fontStyle)
    }

    internal fun parseProperties(
        properties: String,
        baseFontSizeSp: Float,
        density: Float,
        constraints: Constraints,
        onlyImportant: Boolean,
        isDarkTheme: Boolean,
        themeBackgroundColor: Color = Color.Unspecified,
        themeTextColor: Color = Color.Unspecified,
        adaptThemeColors: Boolean = true
    ): CssStyle {
        var spanStyle = SpanStyle()
        var paragraphStyle = ParagraphStyle()
        var padding = BoxBorders()
        var width: Dp = Dp.Unspecified
        var maxWidth: Dp = Dp.Unspecified
        var height: Dp = Dp.Unspecified
        var backgroundColor: Color = Color.Unspecified

        // Changed: Track the max width found to prioritize visible borders
        var maxBorderWidthFound: Dp = 0.dp
        var finalBorderColor: Color? = null
        var finalBorderStyle: String? = null

        var fontFamilies: List<String> = emptyList()
        var fontSize: TextUnit = TextUnit.Unspecified
        var pageBreakInsideAvoid = false
        var listStyleType: String? = null
        var listStyleImage: String? = null
        var display: String? = null
        val containerWidthPx = constraints.maxWidth
        var pageBreakAfterAvoid = false
        var textTransform: String? = null
        var boxSizing: String? = null
        var float: String? = null
        var clear: String? = null
        var content: String? = null
        var position: String? = null
        var left: Dp = Dp.Unspecified
        var top: Dp = Dp.Unspecified
        var right: Dp = Dp.Unspecified
        var bottom: Dp = Dp.Unspecified
        var flexDirection: String? = null
        var justifyContent: String? = null
        var alignItems: String? = null
        var filter: String? = null
        var borderCollapse: String? = null
        var borderSpacing: Dp = 0.dp
        var borderRadius: Dp = 0.dp
        var hyphens: String? = null
        var fontVariantNumeric: String? = null
        var textEmphasisStyleString: String? = null
        var textEmphasisColor: Color? = null
        var textEmphasisPositionString: String? = null
        var marginTopStr: String? = null
        var marginRightStr: String? = null
        var marginBottomStr: String? = null
        var marginLeftStr: String? = null

        var wordSpacing: TextUnit = TextUnit.Unspecified
        var textDecorationStyle: String? = null
        var textDecorationColor: Color = Color.Unspecified
        var textUnderlineOffset: Dp = Dp.Unspecified

        var borderTopWidth: Dp? = null
        var borderRightWidth: Dp? = null
        var borderBottomWidth: Dp? = null
        var borderLeftWidth: Dp? = null

        var borderTopStyle: String? = null
        var borderRightStyle: String? = null
        var borderBottomStyle: String? = null
        var borderLeftStyle: String? = null

        var borderTopColor: Color? = null
        var borderRightColor: Color? = null
        var borderBottomColor: Color? = null
        var borderLeftColor: Color? = null

        var borderTopLeftRadius: Dp = 0.dp
        var borderTopRightRadius: Dp = 0.dp
        var borderBottomRightRadius: Dp = 0.dp
        var borderBottomLeftRadius: Dp = 0.dp

        fun maybeAdaptColor(color: Color, isBackground: Boolean): Color {
            return if (adaptThemeColors) {
                this@CssParser.adaptColorForTheme(color, isDarkTheme, isBackground, themeBackgroundColor, themeTextColor)
            } else {
                color
            }
        }

        splitDeclarations(properties).filter { it.isNotBlank() }.forEach { prop ->
            val parts = prop.split(':', limit = 2).map { it.trim() }
            if (parts.size == 2) {
                val key = parts[0].lowercase()
                val valueWithImportant = parts[1]
                val isImportant = valueWithImportant.contains("!important", ignoreCase = true)

                if (isImportant != onlyImportant) {
                    return@forEach
                }
                val value = if (isImportant) {
                    valueWithImportant.replace(Regex("\\s*!important", RegexOption.IGNORE_CASE), "").trim()
                } else {
                    valueWithImportant
                }

                fun updateUnifiedBorder(
                    widthStr: String?,
                    colorStr: String?,
                    styleStr: String?
                ) {
                    val parsedWidth = widthStr?.let { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) } ?: 0.dp
                    val parsedColor = colorStr?.let { parseColor(it) }?.let { maybeAdaptColor(it, isBackground = false) }

                    val isExplicitWidth = widthStr != null

                    if (parsedWidth > maxBorderWidthFound) {
                        maxBorderWidthFound = parsedWidth
                        if (parsedColor != null) finalBorderColor = parsedColor
                        if (styleStr != null) finalBorderStyle = styleStr
                    } else if (parsedWidth == maxBorderWidthFound && maxBorderWidthFound > 0.dp) {
                        if (parsedColor != null) finalBorderColor = parsedColor
                        if (styleStr != null) finalBorderStyle = styleStr
                    } else if (!isExplicitWidth) {
                        if (parsedColor != null) finalBorderColor = parsedColor
                        if (styleStr != null) finalBorderStyle = styleStr
                    }
                }

                when (key) {
                    "font-family" -> {
                        fontFamilies = value.split(',')
                            .map { it.trim().removeSurrounding("\"").removeSurrounding("'").lowercase() }
                    }
                    "font-size" -> {
                        val trimmedValue = value.trim().lowercase()
                        fontSize = if (trimmedValue.endsWith("%")) {
                            val percentage = trimmedValue.removeSuffix("%").toFloatOrNull()
                            if (percentage != null) {
                                (percentage / 100f).em
                            } else {
                                TextUnit.Unspecified
                            }
                        } else {
                            parseCssDimensionToTextUnit(value, containerWidthPx, density)
                        }
                    }
                    "font-weight" -> {
                        spanStyle = spanStyle.copy(fontWeight = when (value) {
                            "bold" -> FontWeight.Bold
                            "700" -> FontWeight.Bold
                            "600" -> FontWeight.SemiBold
                            "500" -> FontWeight.Medium
                            "300" -> FontWeight.Light
                            "200" -> FontWeight.ExtraLight
                            "100" -> FontWeight.Thin
                            "normal" -> FontWeight.Normal
                            "400" -> FontWeight.Normal
                            else -> value.toIntOrNull()?.let { FontWeight(it) } ?: spanStyle.fontWeight
                        })
                    }
                    "font-style" -> {
                        if (value == "italic" || value == "oblique") spanStyle = spanStyle.copy(fontStyle = FontStyle.Italic)
                        else if (value == "normal") spanStyle = spanStyle.copy(fontStyle = FontStyle.Normal)
                    }
                    "color" -> {
                        parseColor(value)?.let {
                            spanStyle = spanStyle.copy(color = maybeAdaptColor(it, isBackground = false))
                        }
                    }
                    "text-align" -> {
                        val align = when (value) {
                            "center" -> TextAlign.Center
                            "right" -> TextAlign.End
                            "justify" -> TextAlign.Justify
                            else -> TextAlign.Start
                        }
                        paragraphStyle = paragraphStyle.copy(textAlign = align)
                    }
                    "line-height" -> {
                        val trimmedValue = value.trim()
                        var lineHeight = when {
                            trimmedValue.endsWith("%") -> {
                                val percentage = trimmedValue.removeSuffix("%").toFloatOrNull()
                                if (percentage != null) {
                                    (percentage / 100f).em
                                } else {
                                    TextUnit.Unspecified
                                }
                            }
                            trimmedValue.toFloatOrNull() != null && trimmedValue.none { it.isLetter() } -> {
                                trimmedValue.toFloatOrNull()?.em ?: TextUnit.Unspecified
                            }
                            else -> parseCssDimensionToTextUnit(trimmedValue, containerWidthPx, density)
                        }
                        if (lineHeight.isEm && lineHeight.value < 1.2f && lineHeight.value > 0) {
                            lineHeight = 2f.em
                        }
                        if (lineHeight != TextUnit.Unspecified) {
                            paragraphStyle = paragraphStyle.copy(lineHeight = lineHeight)
                        }
                    }
                    "text-indent" -> {
                        val indent = parseCssDimensionToTextUnit(value, containerWidthPx, density)
                        if (indent != TextUnit.Unspecified) {
                            paragraphStyle = paragraphStyle.copy(textIndent = TextIndent(firstLine = indent))
                        }
                    }
                    "text-decoration" -> {
                        val parts = value.split(" ")
                        val decos = mutableListOf<TextDecoration>()

                        if (parts.contains("underline")) decos.add(TextDecoration.Underline)
                        if (parts.contains("line-through")) decos.add(TextDecoration.LineThrough)

                        if (parts.contains("none")) {
                            spanStyle = spanStyle.copy(textDecoration = TextDecoration.None)
                        } else if (decos.isNotEmpty()) {
                            spanStyle = spanStyle.copy(textDecoration = TextDecoration.combine(decos))
                        }

                        val styles = listOf("solid", "double", "dotted", "dashed", "wavy")
                        parts.firstOrNull { it in styles }?.let { textDecorationStyle = it }
                        parts.firstNotNullOfOrNull { parseColor(it) }?.let { color ->
                            textDecorationColor = maybeAdaptColor(color, isBackground = false)
                        }
                    }
                    "word-spacing" -> {
                        val trimmedValue = value.trim()
                        wordSpacing = if (trimmedValue.lowercase() == "normal") {
                            TextUnit.Unspecified
                        } else {
                            parseCssDimensionToTextUnit(value, containerWidthPx, density)
                        }
                    }
                    "text-decoration-style" -> {
                        textDecorationStyle = value
                    }
                    "text-decoration-color" -> {
                        parseColor(value)?.let {
                            textDecorationColor = maybeAdaptColor(it, isBackground = false)
                        }
                    }
                    "text-underline-offset" -> {
                        textUnderlineOffset = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    }
                    "letter-spacing" -> {
                        val letterSpacing = parseCssDimensionToTextUnit(value, containerWidthPx, density)
                        if (letterSpacing != TextUnit.Unspecified) {
                            spanStyle = spanStyle.copy(letterSpacing = letterSpacing)
                        }
                    }
                    "text-transform" -> {
                        textTransform = when (value) {
                            "uppercase", "lowercase", "capitalize", "none" -> value
                            else -> null
                        }
                    }
                    "font-variant" -> {
                        if (value.contains("small-caps")) {
                            spanStyle = spanStyle.copy(fontFeatureSettings = "\"smcp\" on")
                        }
                    }
                    "margin" -> {
                        val marginParts = value.split(' ').filter { it.isNotBlank() }
                        when (marginParts.size) {
                            1 -> {
                                marginTopStr = marginParts[0]; marginRightStr = marginParts[0]; marginBottomStr = marginParts[0]; marginLeftStr = marginParts[0]
                            }
                            2 -> {
                                marginTopStr = marginParts[0]; marginBottomStr = marginParts[0]
                                marginRightStr = marginParts[1]; marginLeftStr = marginParts[1]
                            }
                            3 -> {
                                marginTopStr = marginParts[0]
                                marginRightStr = marginParts[1]; marginLeftStr = marginParts[1]
                                marginBottomStr = marginParts[2]
                            }
                            4 -> {
                                marginTopStr = marginParts[0]; marginRightStr = marginParts[1]; marginBottomStr = marginParts[2]; marginLeftStr = marginParts[3]
                            }
                        }
                    }
                    "margin-top" -> marginTopStr = value
                    "margin-bottom" -> marginBottomStr = value
                    "margin-left" -> marginLeftStr = value
                    "margin-right" -> marginRightStr = value

                    "padding" -> padding = parseBoxBorders(value, baseFontSizeSp, density, containerWidthPx)
                    "padding-top" -> padding = padding.copy(top = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx))
                    "padding-bottom" -> padding = padding.copy(bottom = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx))
                    "padding-left" -> padding = padding.copy(left = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx))
                    "padding-right" -> padding = padding.copy(right = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx))

                    "width" -> width = parseCssDimension(value, baseFontSizeSp, density, containerWidthPx)
                    "max-width" -> maxWidth = parseCssDimension(value, baseFontSizeSp, density, containerWidthPx)
                    "height" -> height = parseCssDimension(value, baseFontSizeSp, density, containerWidthPx)

                    "background-color" -> {
                        val originalColor = parseColor(value) ?: Color.Unspecified
                        backgroundColor = maybeAdaptColor(originalColor, isBackground = true)
                    }

                    // Border Properties
                    "border-width" -> {
                        val widths = parseShorthand4(value, baseFontSizeSp, density, containerWidthPx)
                        borderTopWidth = widths[0]; borderRightWidth = widths[1]; borderBottomWidth = widths[2]; borderLeftWidth = widths[3]
                    }
                    "border-style" -> {
                        val styles = parseShorthand4Strings(value)
                        borderTopStyle = styles[0]; borderRightStyle = styles[1]; borderBottomStyle = styles[2]; borderLeftStyle = styles[3]
                    }
                    "border-color" -> {
                        val colors = parseShorthand4Colors(value)
                        borderTopColor = colors[0]; borderRightColor = colors[1]; borderBottomColor = colors[2]; borderLeftColor = colors[3]
                    }

                    "border-top-width" -> borderTopWidth = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-right-width" -> borderRightWidth = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-bottom-width" -> borderBottomWidth = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-left-width" -> borderLeftWidth = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)

                    "border-top-style" -> borderTopStyle = value
                    "border-right-style" -> borderRightStyle = value
                    "border-bottom-style" -> borderBottomStyle = value
                    "border-left-style" -> borderLeftStyle = value

                    "border-top-color" -> borderTopColor = parseColor(value)
                    "border-right-color" -> borderRightColor = parseColor(value)
                    "border-bottom-color" -> borderBottomColor = parseColor(value)
                    "border-left-color" -> borderLeftColor = parseColor(value)

                    "border-top" -> {
                        val (w, s, c) = parseBorderShorthand(value, baseFontSizeSp, density, containerWidthPx)
                        if (w != null) borderTopWidth = w
                        if (s != null) borderTopStyle = s
                        if (c != null) borderTopColor = c
                    }
                    "border-right" -> {
                        val (w, s, c) = parseBorderShorthand(value, baseFontSizeSp, density, containerWidthPx)
                        if (w != null) borderRightWidth = w
                        if (s != null) borderRightStyle = s
                        if (c != null) borderRightColor = c
                    }
                    "border-bottom" -> {
                        val (w, s, c) = parseBorderShorthand(value, baseFontSizeSp, density, containerWidthPx)
                        if (w != null) borderBottomWidth = w
                        if (s != null) borderBottomStyle = s
                        if (c != null) borderBottomColor = c
                    }
                    "border-left" -> {
                        val (w, s, c) = parseBorderShorthand(value, baseFontSizeSp, density, containerWidthPx)
                        if (w != null) borderLeftWidth = w
                        if (s != null) borderLeftStyle = s
                        if (c != null) borderLeftColor = c
                    }

                    "border" -> {
                        val (w, s, c) = parseBorderShorthand(value, baseFontSizeSp, density, containerWidthPx)
                        if (w != null) { borderTopWidth = w; borderRightWidth = w; borderBottomWidth = w; borderLeftWidth = w }
                        if (s != null) { borderTopStyle = s; borderRightStyle = s; borderBottomStyle = s; borderLeftStyle = s }
                        if (c != null) { borderTopColor = c; borderRightColor = c; borderBottomColor = c; borderLeftColor = c }
                    }

                    "border-radius" -> {
                        val radii = parseShorthand4(value, baseFontSizeSp, density, containerWidthPx)
                        borderTopLeftRadius = radii[0]
                        borderTopRightRadius = radii[1]
                        borderBottomRightRadius = radii[2]
                        borderBottomLeftRadius = radii[3]
                    }
                    "border-top-left-radius" -> borderTopLeftRadius = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-top-right-radius" -> borderTopRightRadius = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-bottom-right-radius" -> borderBottomRightRadius = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "border-bottom-left-radius" -> borderBottomLeftRadius = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)

                    "border-collapse" -> {
                        if (value in listOf("collapse", "separate")) {
                            borderCollapse = value
                        }
                    }
                    "border-spacing" -> {
                        borderSpacing = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    }

                    "list-style-type" -> {
                        listStyleType = value
                    }
                    "list-style-image" -> {
                        URL_REGEX.find(value)?.groupValues?.get(2)?.let {
                            listStyleImage = it
                        }
                    }
                    "page-break-inside" -> {
                        if (value == "avoid") {
                            pageBreakInsideAvoid = true
                        }
                    }
                    "page-break-after" -> {
                        if (value == "avoid") {
                            pageBreakAfterAvoid = true
                        }
                    }
                    "display" -> display = value
                    "flex-direction" -> flexDirection = value
                    "justify-content" -> justifyContent = value
                    "align-items" -> alignItems = value
                    "filter" -> filter = value
                    "box-sizing" -> boxSizing = value
                    "content" -> content = value.removeSurrounding("\"").removeSurrounding("'")
                    "position" -> position = value
                    "left" -> left = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "right" -> right = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "top" -> top = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "bottom" -> bottom = parseCssSizeToDp(value, baseFontSizeSp, density, containerWidthPx)
                    "float" -> {
                        if (value in listOf("left", "right", "none")) {
                            float = value
                        }
                    }
                    "hyphens", "-webkit-hyphens", "-moz-hyphens", "-epub-hyphens", "adobe-hyphenate" -> {
                        if (value in listOf("auto", "manual", "none")) {
                            hyphens = value
                        }
                    }
                    "font-variant-numeric" -> {
                        fontVariantNumeric = value
                    }
                    "clear" -> {
                        if (value in listOf("left", "right", "both", "none")) {
                            clear = value
                        }
                    }
                    "text-emphasis", "-epub-text-emphasis" -> {
                        textEmphasisStyleString = value
                    }
                    "text-emphasis-style", "-epub-text-emphasis-style" -> {
                        textEmphasisStyleString = value
                    }
                    "text-emphasis-color", "-epub-text-emphasis-color" -> {
                        textEmphasisColor = parseColor(value)?.let { maybeAdaptColor(it, isBackground = false) }
                    }
                    "text-emphasis-position", "-epub-text-emphasis-position" -> {
                        if (value in listOf("over", "under")) {
                            textEmphasisPositionString = value
                        }
                    }
                }
            }
        }
        val finalHorizontalAlign = if (marginLeftStr == "auto" && marginRightStr == "auto") "center" else null

        val margin = BoxBorders(
            top = marginTopStr?.let { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) } ?: 0.dp,
            bottom = marginBottomStr?.let { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) } ?: 0.dp,
            left = marginLeftStr.takeIf { it != "auto" }?.let { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) } ?: 0.dp,
            right = marginRightStr.takeIf { it != "auto" }?.let { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) } ?: 0.dp
        )

        // ... [TextEmphasis object creation code remains same] ...
        val textEmphasis = if (textEmphasisStyleString != null) {
            val parts = textEmphasisStyleString.split(' ').filter { it.isNotBlank() }
            var fill: String? = null
            var style: String? = null

            parts.forEach { part ->
                when (part) {
                    "filled", "open" -> fill = part
                    "dot", "circle", "double-circle", "triangle", "sesame" -> style = part
                    else -> {
                        style = part.removeSurrounding("'").removeSurrounding("\"")
                    }
                }
            }
            TextEmphasis(
                style = style,
                fill = fill,
                color = textEmphasisColor ?: Color.Unspecified,
                position = textEmphasisPositionString
            )
        } else {
            null
        }

        val finalBorder = if (maxBorderWidthFound > 0.dp && finalBorderStyle != null) {
            val borderColor = finalBorderColor ?: spanStyle.color.takeIf { it.isSpecified } ?: Color.Black
            BorderStyle(
                width = maxBorderWidthFound,
                color = borderColor,
                style = finalBorderStyle
            )
        } else null

        fun makeBorder(width: Dp?, style: String?, color: Color?): BorderStyle? {
            val finalWidth = width ?: if (style != null && style != "none" && style != "hidden") 3.dp else 0.dp
            val finalStyle = style ?: "none"
            val finalColor = color ?: spanStyle.color.takeIf { it.isSpecified } ?: Color.Black

            val adaptedColor = maybeAdaptColor(finalColor, isBackground = false)

            if (finalWidth > 0.dp && finalStyle != "none" && finalStyle != "hidden") {
                return BorderStyle(finalWidth, adaptedColor, finalStyle)
            }
            return null
        }

        val finalBorderTop = makeBorder(borderTopWidth, borderTopStyle, borderTopColor)
        val finalBorderRight = makeBorder(borderRightWidth, borderRightStyle, borderRightColor)
        val finalBorderBottom = makeBorder(borderBottomWidth, borderBottomStyle, borderBottomColor)
        val finalBorderLeft = makeBorder(borderLeftWidth, borderLeftStyle, borderLeftColor)

        val blockStyle = BlockStyle(
            margin = margin, padding = padding, width = width, maxWidth = maxWidth, height = height,
            backgroundColor = backgroundColor,
            borderTop = finalBorderTop,
            borderRight = finalBorderRight,
            borderBottom = finalBorderBottom,
            borderLeft = finalBorderLeft,
            borderTopLeftRadius = borderTopLeftRadius,
            borderTopRightRadius = borderTopRightRadius,
            borderBottomRightRadius = borderBottomRightRadius,
            borderBottomLeftRadius = borderBottomLeftRadius,
            listStyleType = listStyleType,
            listStyleImage = listStyleImage,
            pageBreakInsideAvoid = pageBreakInsideAvoid,
            pageBreakAfterAvoid = pageBreakAfterAvoid,
            boxSizing = boxSizing,
            float = float,
            clear = clear,
            position = position,
            left = left,
            right = right,
            top = top,
            bottom = bottom,
            display = display,
            flexDirection = flexDirection,
            justifyContent = justifyContent,
            alignItems = alignItems,
            horizontalAlign = finalHorizontalAlign,
            filter = filter,
            borderCollapse = borderCollapse,
            borderSpacing = borderSpacing
        )
        return CssStyle(
            spanStyle, paragraphStyle, blockStyle, fontFamilies, display, fontSize, textTransform, boxSizing, content, hyphens, fontVariantNumeric, textEmphasis,
            wordSpacing, textDecorationStyle, textDecorationColor, textUnderlineOffset
        )
    }

    private fun parseShorthand4(value: String, baseFontSize: Float, density: Float, containerWidth: Int): List<Dp> {
        val parts = value.split(' ').filter { it.isNotBlank() }
        val dps = parts.map { parseCssSizeToDp(it, baseFontSize, density, containerWidth) }
        return when (dps.size) {
            1 -> listOf(dps[0], dps[0], dps[0], dps[0])
            2 -> listOf(dps[0], dps[1], dps[0], dps[1]) // Top/Bottom, Left/Right
            3 -> listOf(dps[0], dps[1], dps[2], dps[1]) // Top, Left/Right, Bottom
            4 -> listOf(dps[0], dps[1], dps[2], dps[3]) // Top, Right, Bottom, Left
            else -> listOf(0.dp, 0.dp, 0.dp, 0.dp)
        }
    }

    private fun parseShorthand4Strings(value: String): List<String?> {
        val parts = value.split(' ').filter { it.isNotBlank() }
        return when (parts.size) {
            1 -> listOf(parts[0], parts[0], parts[0], parts[0])
            2 -> listOf(parts[0], parts[1], parts[0], parts[1])
            3 -> listOf(parts[0], parts[1], parts[2], parts[1])
            4 -> listOf(parts[0], parts[1], parts[2], parts[3])
            else -> listOf(null, null, null, null)
        }
    }

    private fun parseShorthand4Colors(value: String): List<Color?> {
        if (value.contains("(") || value.contains(",")) {
            val c = parseColor(value)
            return listOf(c, c, c, c)
        }
        val parts = value.split(' ').filter { it.isNotBlank() }
        val colors = parts.map { parseColor(it) }
        return when (colors.size) {
            1 -> listOf(colors[0], colors[0], colors[0], colors[0])
            2 -> listOf(colors[0], colors[1], colors[0], colors[1])
            3 -> listOf(colors[0], colors[1], colors[2], colors[1])
            4 -> listOf(colors[0], colors[1], colors[2], colors[3])
            else -> listOf(null, null, null, null)
        }
    }

    private fun parseBorderShorthand(value: String, baseFontSize: Float, density: Float, containerWidth: Int): Triple<Dp?, String?, Color?> {
        val parts = value.split(" ").filter { it.isNotBlank() }
        var w: Dp? = null
        var s: String? = null
        var c: Color? = null

        val keywords = mapOf("thin" to 1.dp, "medium" to 3.dp, "thick" to 5.dp)

        parts.forEach { part ->
            val lower = part.lowercase()
            if (keywords.containsKey(lower)) {
                w = keywords[lower]
            } else if (lower.endsWith("px") || lower.endsWith("em") || lower.endsWith("rem") || lower.endsWith("%") || lower.first().isDigit()) {
                val parsed = parseCssSizeToDp(part, baseFontSize, density, containerWidth)
                if (parsed > 0.dp || part == "0") w = parsed
            } else if (lower in listOf("solid", "dashed", "dotted", "double", "none", "hidden", "groove", "ridge", "inset", "outset")) {
                s = lower
            } else {
                val parsedColor = parseColor(part)
                if (parsedColor != null) c = parsedColor
            }
        }
        return Triple(w, s, c)
    }

    internal fun parseCssDimension(
        size: String,
        baseFontSizeSp: Float,
        density: Float,
        containerWidthPx: Int
    ): Dp {
        val trimmed = size.trim().lowercase()
        if (trimmed in listOf("auto", "none", "max-content", "min-content", "fit-content", "inherit", "initial")) {
            return Dp.Unspecified
        }
        if (trimmed == "0" || trimmed == "0px") return 0.dp

        return when {
            trimmed.endsWith("px") -> trimmed.removeSuffix("px").toFloatOrNull()?.let { (it / density).dp } ?: Dp.Unspecified
            trimmed.endsWith("dp") -> trimmed.removeSuffix("dp").toFloatOrNull()?.dp ?: Dp.Unspecified
            trimmed.endsWith("em") -> trimmed.removeSuffix("em").toFloatOrNull()?.let { (it * baseFontSizeSp).dp } ?: Dp.Unspecified
            trimmed.endsWith("rem") -> trimmed.removeSuffix("rem").toFloatOrNull()?.let { (it * baseFontSizeSp).dp } ?: Dp.Unspecified
            trimmed.endsWith("pt") -> trimmed.removeSuffix("pt").toFloatOrNull()?.let { (it * 1.33f).dp } ?: Dp.Unspecified
            trimmed.endsWith("%") -> {
                val percent = trimmed.removeSuffix("%").toFloatOrNull()
                if (percent != null) {
                    ((percent / 100f) * containerWidthPx / density).dp
                } else {
                    Dp.Unspecified
                }
            }
            trimmed.endsWith("vw") -> {
                val percent = trimmed.removeSuffix("vw").toFloatOrNull()
                if (percent != null) {
                    ((percent / 100f) * containerWidthPx / density).dp
                } else {
                    Dp.Unspecified
                }
            }
            trimmed.endsWith("vh") -> Dp.Unspecified
            trimmed.toFloatOrNull() != null -> (trimmed.toFloat() / density).dp
            else -> Dp.Unspecified
        }
    }

    internal fun parseCssSizeToDp(
        size: String,
        baseFontSizeSp: Float,
        density: Float,
        containerWidthPx: Int
    ): Dp {
        val trimmed = size.trim().lowercase()
        BORDER_WIDTH_KEYWORDS[trimmed]?.let { return it }
        val dim = parseCssDimension(size, baseFontSizeSp, density, containerWidthPx)
        return if (dim.isSpecified) dim else 0.dp
    }

    internal fun parseColor(colorString: String): Color? {
        val sanitized = colorString.trim().lowercase()
        return when {
            sanitized.startsWith("#") -> {
                val hex = sanitized.substring(1)
                val colorLong = hex.toLongOrNull(16) ?: return null
                when (hex.length) {
                    3 -> { // #RGB
                        val r = (colorLong and 0xF00) shr 8
                        val g = (colorLong and 0x0F0) shr 4
                        val b = colorLong and 0x00F
                        Color((r * 17).toInt(), (g * 17).toInt(), (b * 17).toInt(), 255)
                    }
                    6 -> Color(
                        ((colorLong shr 16) and 0xFF).toInt(),
                        ((colorLong shr 8) and 0xFF).toInt(),
                        (colorLong and 0xFF).toInt(),
                        255
                    ) // #RRGGBB
                    8 -> Color(
                        ((colorLong shr 16) and 0xFF).toInt(),
                        ((colorLong shr 8) and 0xFF).toInt(),
                        (colorLong and 0xFF).toInt(),
                        ((colorLong shr 24) and 0xFF).toInt()
                    ) // #AARRGGBB
                    else -> null
                }
            }
            sanitized.startsWith("rgb") -> {
                val isRgba = sanitized.startsWith("rgba")
                val valuesString = sanitized.substringAfter('(').substringBefore(')')
                val values = valuesString.split(',').map { it.trim() }

                if (values.size < 3) return null

                val r = values[0].toIntOrNull() ?: 0
                val g = values[1].toIntOrNull() ?: 0
                val b = values[2].toIntOrNull() ?: 0
                val a = if (isRgba && values.size == 4) (values[3].toFloatOrNull() ?: 1f) else 1f

                Color(r, g, b, (a * 255).roundToInt())
            }
            else -> when(sanitized) {
                "black" -> Color.Black
                "white" -> Color.White
                "red" -> Color.Red
                "green" -> Color.Green
                "blue" -> Color.Blue
                "gray", "grey" -> Color.Gray
                "cyan" -> Color.Cyan
                "magenta" -> Color.Magenta
                "yellow" -> Color.Yellow
                "transparent" -> Color.Transparent
                else -> null
            }
        }
    }

    private fun parseBoxBorders(value: String, baseFontSizeSp: Float, density: Float, containerWidthPx: Int): BoxBorders {
        val parts = value.split(' ').map { it.trim() }.filter { it.isNotEmpty() }
        val dps = parts.map { parseCssSizeToDp(it, baseFontSizeSp, density, containerWidthPx) }
        return when (dps.size) {
            1 -> BoxBorders(top = dps[0], right = dps[0], bottom = dps[0], left = dps[0])
            2 -> BoxBorders(top = dps[0], bottom = dps[0], right = dps[1], left = dps[1])
            3 -> BoxBorders(top = dps[0], right = dps[1], left = dps[1], bottom = dps[2])
            4 -> BoxBorders(top = dps[0], right = dps[1], bottom = dps[2], left = dps[3])
            else -> BoxBorders()
        }
    }
}
