package solutions.tretter.githugandroid

import java.io.File

class NativeLevelSetup internal constructor(
    internal val sandbox: File,
    private val runGit: (File, List<String>, Map<String, String>) -> Int,
) {
    private var commitSequence = 0

    fun resetFiles() {
        sandbox.listFiles()
            ?.filterNot { it.name == ".git" }
            ?.forEach { it.deleteRecursively() }
        git("checkout", "-B", "master")
    }

    fun git(vararg arguments: String): Int = runGit(sandbox, arguments.toList(), emptyMap())

    fun git(directory: File, vararg arguments: String): Int = runGit(directory, arguments.toList(), emptyMap())

    fun initRepo(directory: File) {
        directory.mkdirs()
        val initResult = git(directory, "init", "-b", "master")
        if (initResult != 0) {
            git(directory, "init")
            git(directory, "checkout", "-B", "master")
        }
        git(directory, "config", "receive.denyCurrentBranch", "ignore")
    }

    fun write(path: String, content: String = "") {
        File(sandbox, path).apply {
            parentFile?.mkdirs()
            writeText(content)
        }
    }

    fun append(path: String, content: String) {
        File(sandbox, path).appendText(content)
    }

    fun add(vararg paths: String) {
        git("add", *paths)
    }

    fun commit(message: String, author: String? = null) {
        val commitDate = nextDeterministicCommitDate()
        val environment = mapOf(
            "GIT_AUTHOR_DATE" to commitDate,
            "GIT_COMMITTER_DATE" to commitDate,
        )
        if (author == null) {
            gitWithEnvironment(environment, "commit", "-m", message)
        } else {
            gitWithEnvironment(environment, "commit", "--author", author, "-m", message)
        }
    }

    fun addCommit(message: String, vararg paths: String, author: String? = null) {
        add(*paths)
        commit(message, author)
    }

    fun writeIn(directory: File, path: String, content: String = "") {
        File(directory, path).apply {
            parentFile?.mkdirs()
            writeText(content)
        }
    }

    fun addCommitIn(directory: File, message: String, vararg paths: String) {
        git(directory, "add", *paths)
        git(directory, "commit", "-m", message)
    }

    fun siblingRepo(name: String): File {
        val directory = File(sandbox.parentFile ?: sandbox, "${sandbox.name}-$name")
        directory.deleteRecursively()
        initRepo(directory)
        return directory
    }

    fun checkoutNew(branch: String) {
        git("checkout", "-b", branch)
    }

    fun checkout(branch: String) {
        git("checkout", branch)
    }

    fun tag(name: String) {
        git("tag", "-f", name)
    }

    private fun gitWithEnvironment(environment: Map<String, String>, vararg arguments: String): Int {
        return runGit(sandbox, arguments.toList(), environment)
    }

    private fun nextDeterministicCommitDate(): String {
        commitSequence += 1
        val minute = (commitSequence / 60).toString().padStart(2, '0')
        val second = (commitSequence % 60).toString().padStart(2, '0')
        return "2000-01-01T00:$minute:$second+0000"
    }
}
