package app.git

import app.git.graph.GraphCommitList
import app.git.graph.GraphWalk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ResetCommand
import org.eclipse.jgit.lib.Constants
import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject


class LogManager @Inject constructor() {
    suspend fun loadLog(git: Git, currentBranch: Ref?, hasUncommitedChanges: Boolean) = withContext(Dispatchers.IO) {
        val commitList = GraphCommitList()
        val repositoryState = git.repository.repositoryState
        println("Repository state ${repositoryState.description}")
        if (currentBranch != null || repositoryState.isRebasing) { // Current branch is null when there is no log (new repo) or rebasing
            val logList = git.log().setMaxCount(2).call().toList()

            val walk = GraphWalk(git.repository)

            walk.use {
                walk.markStartAllRefs(Constants.R_HEADS)
                walk.markStartAllRefs(Constants.R_REMOTES)
                walk.markStartAllRefs(Constants.R_TAGS)

                if (hasUncommitedChanges)
                    commitList.addUncommitedChangesGraphCommit(logList.first())

                commitList.source(walk)
                commitList.fillTo(1000) // TODO: Limited commits to show to 1000, add a setting to let the user adjust this
            }

            ensureActive()

        }

        return@withContext commitList
    }

    suspend fun checkoutCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
        git
            .checkout()
            .setName(revCommit.name)
            .call()
    }

    suspend fun revertCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
        git
            .revert()
            .include(revCommit)
            .call()
    }

    suspend fun resetToCommit(git: Git, revCommit: RevCommit, resetType: ResetType) = withContext(Dispatchers.IO) {
        val reset = when (resetType) {
            ResetType.SOFT -> ResetCommand.ResetType.SOFT
            ResetType.MIXED -> ResetCommand.ResetType.MIXED
            ResetType.HARD -> ResetCommand.ResetType.HARD
        }
        git
            .reset()
            .setMode(reset)
            .setRef(revCommit.name)
            .call()
    }

    suspend fun latestMessage(git: Git): String = withContext(Dispatchers.IO) {
        try {
            val log = git.log().setMaxCount(1).call()
            val latestCommitNode = log.firstOrNull()

            return@withContext if (latestCommitNode == null)
                ""
            else
                latestCommitNode.fullMessage

        } catch (ex: Exception) {
            ex.printStackTrace()
            return@withContext ""
        }

    }

    suspend fun hasPreviousCommits(git: Git): Boolean = withContext(Dispatchers.IO) {
        try {
            val log = git.log().setMaxCount(1).call()
            val latestCommitNode = log.firstOrNull()

            return@withContext latestCommitNode != null

        } catch (ex: Exception) {
            ex.printStackTrace()
            return@withContext false
        }
    }
}

enum class ResetType {
    SOFT,
    MIXED,
    HARD,
}