package com.correx.core.tasks

import com.correx.core.events.types.TaskId
import com.correx.core.events.types.TaskNoteAuthor
import kotlinx.serialization.Serializable

/** A single commit's identity + message — the unit [GitTaskSync] consumes. */
data class Commit(val sha: String, val message: String) {
    fun shortSha(): String = if (sha.length <= SHORT_SHA) sha else sha.substring(0, SHORT_SHA)

    private companion object {
        const val SHORT_SHA = 7
    }
}

/**
 * Port for reading recent commits from the repo. Implemented in a higher layer (apps/server shells
 * `git log`); core stays decoupled from process execution.
 */
interface GitCommitReader {
    suspend fun recent(limit: Int): List<Commit>
}

@Serializable
data class TaskStatusChange(val key: String, val sha: String, val from: String, val to: String)

@Serializable
data class GitSyncReport(val changes: List<TaskStatusChange>)

/**
 * Drives task status forward from git signals: a plain mention advances a task to IN_PROGRESS, a
 * closing keyword advances it all the way to DONE (walking the legal claim → review → done path).
 * It only ever advances — a task already at/past the target, or BLOCKED/CANCELLED, is left alone —
 * so re-running over the same commits is idempotent. Each advance leaves a provenance note naming
 * the commit.
 */
class GitTaskSync(private val service: TaskService) {

    suspend fun sync(commits: List<Commit>): GitSyncReport {
        val changes = mutableListOf<TaskStatusChange>()
        for (commit in commits) {
            for (ref in CommitTaskParser.parse(commit.message)) {
                applyRef(ref, commit)?.let { changes += it }
            }
        }
        return GitSyncReport(changes)
    }

    private suspend fun applyRef(ref: CommitTaskRef, commit: Commit): TaskStatusChange? {
        val taskId = TaskId(ref.key)
        val from = service.getTask(taskId)?.state?.status ?: return null
        val target = if (ref.closing) TaskStatus.DONE else TaskStatus.IN_PROGRESS
        if (from !in ADVANCEABLE || rank(from) >= rank(target)) return null

        var current = from
        while (current in ADVANCEABLE && rank(current) < rank(target)) {
            advanceOne(taskId, current)
            current = service.getTask(taskId)?.state?.status ?: break
        }
        if (current == from) return null
        service.addNote(taskId, TaskNoteAuthor.AGENT, "[git ${commit.shortSha()}] ${from.name} → ${current.name}")
        return TaskStatusChange(ref.key, commit.shortSha(), from.name, current.name)
    }

    private suspend fun advanceOne(taskId: TaskId, current: TaskStatus) {
        when (current) {
            TaskStatus.TODO -> service.claim(taskId, GIT_CLAIMANT)
            TaskStatus.IN_PROGRESS -> service.submitForReview(taskId)
            TaskStatus.IN_REVIEW -> service.complete(taskId)
            else -> Unit
        }
    }

    private fun rank(status: TaskStatus): Int = when (status) {
        TaskStatus.TODO -> 0
        TaskStatus.IN_PROGRESS -> 1
        TaskStatus.IN_REVIEW -> 2
        TaskStatus.DONE -> 3
        else -> -1 // BLOCKED / CANCELLED sit off the forward path
    }

    private companion object {
        const val GIT_CLAIMANT = "git"
        val ADVANCEABLE = setOf(TaskStatus.TODO, TaskStatus.IN_PROGRESS, TaskStatus.IN_REVIEW)
    }
}
