package com.tomhempel.rbarometer.utils

/**
 * Strips patterns commonly used in SQL injection attempts from free-text answers.
 * Server applies the same rules via prepared statements + [sanitize_free_text].
 */
object TextInputSanitizer {

    const val DEFAULT_MAX_LENGTH = 2000

    private val blockedPatterns = listOf(
        Regex("""(?i)\bunion\s+select\b"""),
        Regex("""(?i)\bselect\s+.+\s+from\b"""),
        Regex("""(?i)\binsert\s+into\b"""),
        Regex("""(?i)\bupdate\s+.+\s+set\b"""),
        Regex("""(?i)\bdelete\s+from\b"""),
        Regex("""(?i)\bdrop\s+(table|database)\b"""),
        Regex("""(?i)\bexec(\s|\()"""),
        Regex("""(?i)\bxp_\w+"""),
        Regex("""(?i)\bor\s+1\s*=\s*1\b"""),
        Regex("""(?i)\band\s+1\s*=\s*1\b"""),
        Regex("""(?i)'\s*or\s+'"""),
        Regex("""(?i)"\s*or\s+""""),
        Regex("""(?i)\bchar\s*\("""),
        Regex("""(?i)\bconcat\s*\("""),
        Regex("""(?i)\bsleep\s*\("""),
        Regex("""(?i)\bbenchmark\s*\("""),
    )

    fun sanitize(raw: String, maxLength: Int = DEFAULT_MAX_LENGTH): String {
        val limit = maxLength.coerceIn(1, 10_000)
        var s = raw.trim().take(limit)

        s = s.replace(Regex("""[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]"""), "")
        s = s.replace("\u0000", "")
            .replace("--", "")
            .replace("/*", "")
            .replace("*/", "")
            .replace(";", "")

        blockedPatterns.forEach { pattern ->
            s = s.replace(pattern, "")
        }

        return s.trim().replace(Regex("""\s{2,}"""), " ")
    }

    fun isUsableAfterSanitize(raw: String, maxLength: Int = DEFAULT_MAX_LENGTH): Boolean {
        return sanitize(raw, maxLength).isNotBlank()
    }
}
