package com.correx.infrastructure.tools.task

import com.correx.core.approvals.Tier
import com.correx.core.events.events.ToolRequest
import com.correx.core.events.types.ProjectId
import com.correx.core.tasks.TaskService
import com.correx.core.tools.contract.Tool
import com.correx.core.tools.contract.ToolCapability
import com.correx.core.tools.contract.ToolExecutor
import com.correx.core.tools.contract.ToolResult
import com.correx.core.tools.contract.ValidationResult
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject

private const val DEFAULT_LIMIT = 20

/**
 * Agent-facing tool: find tasks by text across projects (matches id/title/goal/criteria/notes),
 * ranked by relevance. Read-only, so it sits at the lowest tier (like [TaskContextTool]) — agents
 * use it to discover task ids before fetching a context bundle.
 */
class TaskSearchTool(private val service: TaskService) : Tool, ToolExecutor {

    override val name: String = "task_search"
    override val description: String =
        "Search tasks before opening a new one (avoid duplicates) and to find related or " +
            "blocking work, or to look up a task id by text. Matches id/title/goal/acceptance " +
            "criteria/notes, ranked by relevance; returns 'id [STATUS] title' lines. Pair with " +
            "task_context to load a hit."
    override val parametersSchema: JsonObject = buildJsonObject {
        put("type", "object")
        putJsonObject("properties") {
            putJsonObject("query") {
                put("type", "string")
                put("description", "Free-text query; all terms must match.")
            }
            putJsonObject("project") {
                put("type", "string")
                put("description", "Limit to a project (e.g. 'auth'). Omit to search every project.")
            }
            putJsonObject("limit") {
                put("type", "integer")
                put("description", "Max results (default $DEFAULT_LIMIT).")
            }
        }
        put("required", buildJsonArray { add(JsonPrimitive("query")) })
    }
    override val tier: Tier = Tier.T1
    override val requiredCapabilities: Set<ToolCapability> = emptySet()

    override fun validateRequest(request: ToolRequest): ValidationResult {
        request.stringParam("query") ?: return ValidationResult.Invalid("Missing 'query' (string).")
        return ValidationResult.Valid
    }

    override suspend fun execute(request: ToolRequest): ToolResult {
        val query = request.stringParam("query")
            ?: return ToolResult.Failure(request.invocationId, "Missing 'query' (string).", recoverable = false)
        val project = request.stringParam("project")?.let { ProjectId(it) }
        val limit = (request.parameters["limit"] as? Number)?.toInt()?.takeIf { it > 0 } ?: DEFAULT_LIMIT
        val hits = service.search(query, project).take(limit)
        val output = if (hits.isEmpty()) {
            "no tasks match \"$query\""
        } else {
            hits.joinToString("\n") {
                "${it.taskId.value} [${it.state.status.name}] ${it.state.title.orEmpty()}".trimEnd()
            }
        }
        return ToolResult.Success(
            invocationId = request.invocationId,
            output = output,
            metadata = mapOf("query" to query, "count" to hits.size.toString()),
        )
    }
}
