package com.correx.core.context.compression

/**
 * Deterministic, regex-only extraction of load-bearing spans that no lossy stage (token
 * pruning, summarization, ToMe merge) may alter: numbers, IDs, hashes, IPs, file paths,
 * config keys and anything inside code fences or backticks. Runs FIRST, before any lossy
 * stage — this is what keeps compression from becoming a hallucination risk (pipeline §2).
 *
 * [extraEntities] are caller-supplied domain terms (service/model/domain names) protected
 * verbatim in addition to the structural patterns.
 */
class ProtectedSpanTagger(private val extraEntities: List<String> = emptyList()) {

    fun protectedSpans(content: String): List<String> {
        val spans = LinkedHashSet<String>()
        // Code fences first (greedy, multi-line) so their inner content is protected whole,
        // then inline backticks, then the structural token patterns.
        for (pattern in STRUCTURAL_PATTERNS) {
            pattern.findAll(content).forEach { spans += it.value }
        }
        for (entity in extraEntities) {
            if (entity.isNotBlank() && content.contains(entity)) spans += entity
        }
        return spans.toList()
    }

    private companion object {
        val STRUCTURAL_PATTERNS: List<Regex> = listOf(
            Regex("```.*?```", setOf(RegexOption.DOT_MATCHES_ALL)), // fenced code
            Regex("`[^`]+`"),                                       // inline code
            Regex("""\b\d{1,3}(?:\.\d{1,3}){3}\b"""),               // IPv4
            Regex("""\b0x[0-9a-fA-F]+\b"""),                        // hex literal
            Regex("""\b[0-9a-fA-F]{7,40}\b"""),                     // hashes / long hex
            Regex("""(?:[\w.-]+)?/[\w./-]+"""),                     // file paths
            Regex("""\b\w+(?:[._-]\w+)*\s*[:=]"""),                 // config keys (key: / key =)
            Regex("""-?\d+(?:\.\d+)?"""),                           // bare numbers
        )
    }
}
