package solutions.tretter.githugandroid

import java.io.File

internal class GitHelperCommands(
    private val processRunner: GitProcessRunner,
) {
    fun executeExecutableShortcut(
        sandboxRoot: File,
        workingDir: File,
        currentRepo: RepoState,
        tokens: List<String>,
    ): Pair<RepoState, List<String>>? {
        val executable = tokens.firstOrNull() ?: return null
        if (!executable.startsWith("./") || executable.length <= 2) return null
        return executeShellScript(sandboxRoot, workingDir, currentRepo, listOf("sh", executable) + tokens.drop(1))
    }

    fun execute(
        sandboxRoot: File,
        workingDir: File,
        currentRepo: RepoState,
        tokens: List<String>,
    ): Pair<RepoState, List<String>> {
        return when (tokens.first()) {
            "ls", "dir" -> currentRepo to workingDir.listFiles()
                ?.sortedBy { it.name }
                ?.map { it.name }
                .orEmpty()
            "pwd" -> currentRepo to listOf(
                "/sandbox" + if (workingDir == sandboxRoot) "" else "/${workingDir.relativeTo(sandboxRoot).path}"
            )
            "cat" -> {
                val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: cat <file>")
                val file = File(workingDir, target)
                if (!file.exists() || file.isDirectory) currentRepo to listOf("cat: $target: No such file")
                else currentRepo to file.readLines().ifEmpty { listOf("") }
            }
            "sh" -> executeShellScript(sandboxRoot, workingDir, currentRepo, tokens)
            "touch" -> {
                val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: touch <file>")
                val file = File(workingDir, target)
                if (file.exists()) {
                    currentRepo to listOf("$target already exists")
                } else {
                    file.parentFile?.mkdirs()
                    file.writeText("")
                    currentRepo to emptyList()
                }
            }
            "mkdir", "md" -> {
                val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: mkdir <dir>")
                val dir = File(workingDir, target)
                if (dir.exists()) currentRepo to listOf("mkdir: $target: File exists") else {
                    dir.mkdirs()
                    currentRepo to emptyList()
                }
            }
            "cd", "cd.." -> {
                val target = if (tokens.first() == "cd..") ".." else tokens.getOrNull(1)
                    ?: return currentRepo to listOf("usage: cd <dir>")
                val dir = File(workingDir, target).canonicalFile
                when {
                    !dir.exists() -> currentRepo to listOf("cd: $target: does not exist")
                    !dir.isDirectory -> currentRepo to listOf("cd: $target: Not a directory")
                    !dir.path.startsWith(sandboxRoot.path) -> currentRepo to listOf("cd: $target: Permission denied")
                    else -> {
                        val relativeDir = sandboxRoot.toPath().relativize(dir.toPath()).toString().ifEmpty { "." }
                        currentRepo.copy(currentDir = relativeDir) to emptyList()
                    }
                }
            }
            "rm", "del" -> {
                val targets = tokens.drop(1)
                if (targets.isEmpty()) return currentRepo to listOf("usage: rm <path>")
                val errors = targets.mapNotNull { target ->
                    val file = File(workingDir, target)
                    if (!file.exists()) {
                        "${tokens.first()}: $target: No such file or directory"
                    } else {
                        file.deleteRecursively()
                        null
                    }
                }
                currentRepo to errors
            }
            "echo" -> executeEcho(workingDir, currentRepo, tokens)
            else -> currentRepo to listOf("Command not supported in prototype runtime. Try a git command or helper command.")
        }
    }

    private fun executeShellScript(
        sandboxRoot: File,
        workingDir: File,
        currentRepo: RepoState,
        tokens: List<String>,
    ): Pair<RepoState, List<String>> {
        val script = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: sh <script>")
        val scriptFile = File(workingDir, script).canonicalFile
        if (!scriptFile.path.startsWith(sandboxRoot.path)) {
            return currentRepo to listOf("sh: $script: Permission denied")
        }
        if (!scriptFile.isFile) {
            return currentRepo to listOf("sh: $script: No such file")
        }
        val result = processRunner.runShellProcess(
            File(processRunner.shellExecutable()),
            workingDir,
            tokens.drop(1),
        )
        return currentRepo to result.outputLines
    }

    private fun executeEcho(workingDir: File, currentRepo: RepoState, tokens: List<String>): Pair<RepoState, List<String>> {
        val redirectIndex = tokens.indexOfFirst { it == ">" || it == ">>" }
        if (redirectIndex == -1 || redirectIndex == tokens.lastIndex) {
            return currentRepo to listOf(tokens.drop(1).joinToString(" "))
        }

        val file = File(workingDir, tokens[redirectIndex + 1])
        file.parentFile?.mkdirs()
        val content = tokens.subList(1, redirectIndex).joinToString(" ")
        if (tokens[redirectIndex] == ">>") {
            if (file.exists() && file.length() > 0L) {
                file.appendText("\n$content")
            } else {
                file.writeText(content)
            }
        } else {
            file.writeText(content)
        }
        return currentRepo to emptyList()
    }
}
