package com.correx.infrastructure.tools

import com.correx.core.approvals.Tier
import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.events.ToolRequest
import com.correx.core.events.types.ArtifactId
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.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject

/**
 * Retrieves the full text of an earlier tool result that was truncated for model context. When a
 * Success output exceeds the context floor, the kernel spills the full raw output to the artifact
 * store and shows a marker containing its `ref` (the content hash, also recorded on the
 * ToolExecutionCompleted receipt in the event log). This tool resolves that `ref` back to the full
 * content. Read-only (Tier T1, no filesystem capability — it reads the content-addressed store by a
 * hash the model was explicitly handed, never arbitrary paths).
 */
class ToolOutputTool(private val artifactStore: ArtifactStore) : Tool, ToolExecutor {

    override val name: String = "tool_output"
    override val description: String =
        "Retrieve the FULL output of an earlier tool call that was truncated. Pass the ref shown in " +
            "the truncation marker (e.g. tool_output(ref=\"<hash>\"))."
    override val parametersSchema: JsonObject = buildJsonObject {
        put("type", "object")
        putJsonObject("properties") {
            putJsonObject("ref") {
                put("type", "string")
                put("description", "The ref/hash from a '… output truncated …' marker.")
            }
        }
        put("required", buildJsonArray { add(JsonPrimitive("ref")) })
    }
    override val tier: Tier = Tier.T1
    override val requiredCapabilities: Set<ToolCapability> = emptySet()

    override fun validateRequest(request: ToolRequest): ValidationResult =
        if ((request.parameters["ref"] as? String).isNullOrBlank()) {
            ValidationResult.Invalid("tool_output requires a non-empty 'ref'")
        } else {
            ValidationResult.Valid
        }

    override suspend fun execute(request: ToolRequest): ToolResult {
        val ref = (request.parameters["ref"] as? String)?.takeIf { it.isNotBlank() }
            ?: return ToolResult.Failure(
                request.invocationId,
                "tool_output requires a non-empty 'ref'",
                recoverable = true,
            )
        return artifactStore.get(ArtifactId(ref))
            ?.let { ToolResult.Success(request.invocationId, output = it.toString(Charsets.UTF_8)) }
            ?: ToolResult.Failure(
                request.invocationId,
                "No stored output for ref '$ref' — it may have expired or the ref is wrong.",
                recoverable = true,
            )
    }
}
