package com.correx.core.tasks

import kotlinx.serialization.Serializable

/**
 * Token-optimized context bundle for a task — the single payload an agent needs to start work:
 * the task itself, its acceptance criteria, resolved dependency tasks, the related (non-task)
 * links it should fetch, the relevant files, and notes. Assembled by [TaskContextAssembler].
 */
@Serializable
data class TaskContextBundle(
    val id: String,
    val key: String?,
    val status: String,
    val title: String?,
    val goal: String?,
    val acceptanceCriteria: List<String>,
    val relevantFiles: List<String>,
    // Dependency-derived readiness (distinct from an explicit BLOCKED status): blockedBy holds the
    // unfinished tasks this one is waiting on, so an agent calling task_context knows to wait.
    val blocked: Boolean = false,
    val blockedBy: List<RelatedTask> = emptyList(),
    val dependencies: List<RelatedTask>,
    val documents: List<TaskDocument>,
    val artifacts: List<TaskArtifact> = emptyList(),
    val sessions: List<RelatedSession> = emptyList(),
    val relatedLinks: List<RelatedLink>,
    val notes: List<NoteEntry>,
    val relevantKnowledge: List<KnowledgeHit> = emptyList(),
) {
    /** Compact, label-per-line rendering for tool output (cheaper than JSON for the model). */
    fun render(): String = buildString {
        appendLine("task $id [$status] ${title.orEmpty()}".trimEnd())
        goal?.let { appendLine("goal: $it") }
        if (blocked) appendLine("BLOCKED — waiting on ${blockedBy.size} unfinished dependency(ies):")
        appendList("blocked_by", blockedBy) {
            "  - ${it.id} [${it.status}] ${it.title.orEmpty()} (${it.link})".trimEnd()
        }
        appendList("acceptance_criteria", acceptanceCriteria) { "  - $it" }
        if (relevantFiles.isNotEmpty()) appendLine("relevant_files: ${relevantFiles.joinToString(", ")}")
        appendList("dependencies", dependencies) {
            "  - ${it.id} [${it.status}] ${it.title.orEmpty()} (${it.link})".trimEnd()
        }
        appendList("documents", documents) { "  - ${it.targetId} (${it.type}) ${it.title} [${it.path}]: ${it.excerpt}" }
        appendList("artifacts", artifacts) {
            val stageSuffix = it.stage?.let { s -> " @$s" }.orEmpty()
            "  - ${it.targetId} (${it.type})$stageSuffix: ${it.excerpt.orEmpty()}".trimEnd()
        }
        appendList("sessions", sessions) {
            "  - ${it.targetId} (${it.type}) [${it.status}] ${it.intent.orEmpty()}".trimEnd()
        }
        appendList("related", relatedLinks) { "  - ${it.targetId} (${it.type})" }
        appendList("relevant_knowledge", relevantKnowledge) { "  - ${it.source}: ${it.text}" }
        appendList("notes", notes) { "  - [${it.author}] ${it.body}" }
    }.trimEnd()

    private fun <T> StringBuilder.appendList(label: String, items: List<T>, line: (T) -> String) {
        if (items.isEmpty()) return
        appendLine("$label:")
        items.forEach { appendLine(line(it)) }
    }
}

/** A linked task resolved against the board: enough for the agent to judge readiness. */
@Serializable
data class RelatedTask(
    val id: String,
    val status: String,
    val title: String?,
    val link: String,
)

/** A link whose target resolved to a document (ADR/doc): title + excerpt inlined for the agent. */
@Serializable
data class TaskDocument(
    val targetId: String,
    val type: String,
    val title: String,
    val path: String,
    val excerpt: String,
)

/** A link whose target resolved to a produced artifact: producing stage/session + content excerpt. */
@Serializable
data class TaskArtifact(
    val targetId: String,
    val type: String,
    val stage: String?,
    val sessionId: String?,
    val excerpt: String?,
)

/** A link whose target resolved to an agent run: status + intent so the agent has the run's gist. */
@Serializable
data class RelatedSession(
    val targetId: String,
    val type: String,
    val status: String,
    val intent: String?,
    val workflowId: String?,
)

/** A link whose target is neither a task nor a resolvable doc — left for the agent to fetch. */
@Serializable
data class RelatedLink(
    val targetId: String,
    val type: String,
)

@Serializable
data class NoteEntry(
    val author: String,
    val body: String,
)

/** A semantically-retrieved snippet relevant to the task's goal (e.g. a repo file or doc). */
@Serializable
data class KnowledgeHit(
    val source: String,
    val text: String,
    val score: Double,
)
