package com.correx.apps.server.tasks

import com.correx.core.tasks.Commit
import com.correx.core.tasks.GitCommitReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.nio.file.Path
import java.util.concurrent.TimeUnit

private const val RS = "" // ASCII record separator, between commits
private const val US = "" // ASCII unit separator, between sha and body
private const val GIT_TIMEOUT_SEC = 10L

/**
 * Reads recent commits by shelling `git log` in the repo. Best-effort: any failure (git missing,
 * not a repo, timeout, non-zero exit) yields an empty list so the sync endpoint degrades to a no-op
 * rather than erroring. Parsing is split into the testable top-level [parseGitLog].
 */
class GitCommandCommitReader(private val repoRoot: Path) : GitCommitReader {

    override suspend fun recent(limit: Int): List<Commit> = withContext(Dispatchers.IO) {
        runCatching { runGitLog(limit) }.getOrDefault(emptyList())
    }

    private fun runGitLog(limit: Int): List<Commit> {
        val process = ProcessBuilder(
            "git", "-C", repoRoot.toString(), "log", "-n", limit.toString(), "--format=%H$US%B$RS",
        ).redirectErrorStream(false).start()
        val output = process.inputStream.bufferedReader().use { it.readText() }
        val finished = process.waitFor(GIT_TIMEOUT_SEC, TimeUnit.SECONDS)
        return if (finished && process.exitValue() == 0) {
            parseGitLog(output)
        } else {
            if (!finished) process.destroy()
            emptyList()
        }
    }
}

/**
 * Parses `git log --format=%H<US>%B<RS>` output into commits: records split on the record
 * separator, then each split once on the unit separator into sha + message. Top-level + internal
 * so it is unit-testable without invoking git.
 */
internal fun parseGitLog(raw: String): List<Commit> =
    raw.split(RS)
        .map { it.trim() }
        .filter { it.isNotEmpty() }
        .mapNotNull { record ->
            val idx = record.indexOf(US)
            if (idx < 0) {
                null
            } else {
                Commit(sha = record.substring(0, idx).trim(), message = record.substring(idx + 1).trim())
            }
        }
