package com.correx.core.tasks

/**
 * Exact/substring task search. A task matches when every whitespace-separated term in the query
 * appears (case-insensitive) in some searchable field; results are ranked by the strongest field a
 * term hits (title > key > goal > acceptance criteria > notes), ties broken by id for stability.
 * Pure and store-free so it can be reused over [TaskService.list]/[TaskService.listAll] and unit
 * tested directly.
 */
object TaskSearch {

    fun search(tasks: List<Task>, query: String): List<Task> {
        val terms = query.trim().lowercase().split(WHITESPACE).filter { it.isNotBlank() }
        if (terms.isEmpty()) return tasks
        return tasks
            .mapNotNull { task -> score(task, terms)?.let { task to it } }
            .sortedWith(compareByDescending<Pair<Task, Int>> { it.second }.thenBy { it.first.taskId.value })
            .map { it.first }
    }

    /** Sum of the best per-term field weight, or null when any term matches no field. */
    private fun score(task: Task, terms: List<String>): Int? {
        val s = task.state
        val fields = listOf(
            (s.title ?: "") to TITLE_WEIGHT,
            (s.key ?: "") to KEY_WEIGHT,
            (s.goal ?: "") to GOAL_WEIGHT,
            s.acceptanceCriteria.joinToString(" ") to CRITERIA_WEIGHT,
            s.notes.joinToString(" ") { it.body } to NOTES_WEIGHT,
        ).map { (text, weight) -> text.lowercase() to weight }

        var total = 0
        for (term in terms) {
            val best = fields.filter { it.first.contains(term) }.maxOfOrNull { it.second } ?: return null
            total += best
        }
        return total
    }

    private val WHITESPACE = Regex("\\s+")
    private const val TITLE_WEIGHT = 5
    private const val KEY_WEIGHT = 4
    private const val GOAL_WEIGHT = 3
    private const val CRITERIA_WEIGHT = 2
    private const val NOTES_WEIGHT = 1
}
