package solutions.tretter.githugandroid

fun allGithugLevels(): List<Level> = listOf(
    initLevel(),
    configLevel(),
    addLevel(),
    commitLevel(),
    cloneLevel(),
    cloneToFolderLevel(),
    ignoreLevel(),
    includeLevel(),
    statusLevel(),
    numberOfFilesCommittedLevel(),
    rmLevel(),
    rmCachedLevel(),
    stashLevel(),
    renameLevel(),
    restructureLevel(),
    logLevel(),
    tagLevel(),
    pushTagsLevel(),
    commitAmendLevel(),
    commitInFutureLevel(),
    resetLevel(),
    resetSoftLevel(),
    checkoutFileLevel(),
    remoteLevel(),
    remoteUrlLevel(),
    pullLevel(),
    remoteAddLevel(),
    pushLevel(),
    diffLevel(),
    blameLevel(),
    branchLevel(),
    checkoutLevel(),
    checkoutTagLevel(),
    checkoutTagOverBranchLevel(),
    branchAtLevel(),
    deleteBranchLevel(),
    pushBranchLevel(),
    mergeLevel(),
    fetchLevel(),
    rebaseLevel(),
    rebaseOntoLevel(),
    repackLevel(),
    cherryPickLevel(),
    grepLevel(),
    renameCommitLevel(),
    squashLevel(),
    mergeSquashLevel(),
    reorderLevel(),
    bisectLevel(),
    stageLinesLevel(),
    findOldBranchLevel(),
    revertLevel(),
    restoreLevel(),
    conflictLevel(),
    submoduleLevel(),
    contributeLevel(),
)

internal fun level(
    id: String,
    title: String = id.replace('-', ' ').replace('_', ' ').replaceFirstChar { it.uppercase() },
    description: String,
    hints: List<String>,
    commandSuggestions: List<String> = listOf("git status", "git log", "git help"),
    setup: () -> RepoState,
    validator: (RepoState, String) -> Boolean,
    testCases: List<LevelTestCase> = emptyList(),
    negativeTestCases: List<LevelTestCase> = emptyList(),
    setupChecks: List<LevelSetupCheck> = emptyList(),
    nativeSetup: (NativeLevelSetup.() -> Boolean)? = null,
): Level = Level(
    id = id,
    title = title,
    description = description,
    hints = hints,
    commandSuggestions = commandSuggestions,
    validator = loggingValidator(id, title, validator),
    setup = setup,
    nativeSetup = nativeSetup,
    testCases = testCases,
    negativeTestCases = negativeTestCases,
    setupChecks = setupChecks,
)

internal fun levelTestCase(name: String, vararg commands: String): LevelTestCase = LevelTestCase(
    name = name,
    commands = commands.toList(),
)

internal fun levelSetupCheck(
    name: String,
    failureMessage: String,
    predicate: (RepoState) -> Boolean,
): LevelSetupCheck = LevelSetupCheck(
    name = name,
    failureMessage = failureMessage,
    predicate = predicate,
)

internal fun commandAnswer(vararg answers: String): (RepoState, String) -> Boolean = { _, command ->
    val normalized = command.trim()
    val matched = answers.firstOrNull { it.equals(normalized, ignoreCase = true) }
    AppLog.d(
        "Validation",
        "commandAnswer normalized='$normalized' expected=${answers.toList()} matched=${matched != null}",
    )
    matched != null
}

internal fun commitHashAnswer(fallbackHash: String): (RepoState, String) -> Boolean = { repo, command ->
    val normalized = command.trim()
    val latestHash = repo.commits.firstOrNull()?.id ?: fallbackHash
    val matched = normalized.equals(fallbackHash, ignoreCase = true) ||
        normalized.startsWith(latestHash, ignoreCase = true) ||
        latestHash.startsWith(normalized, ignoreCase = true)
    AppLog.d(
        "Validation",
        "commitHashAnswer normalized='$normalized' fallback=$fallbackHash latest=$latestHash matched=$matched",
    )
    normalized.isNotBlank() && matched
}

internal fun repoPredicate(block: (RepoState) -> Boolean): (RepoState, String) -> Boolean = { repo, _ ->
    block(repo)
}

private fun loggingValidator(
    levelId: String,
    levelTitle: String,
    validator: (RepoState, String) -> Boolean,
): (RepoState, String) -> Boolean = { repo, command ->
    AppLog.d(
        "Validation",
        buildString {
            append("Evaluating level=")
            append(levelId)
            append(" title=")
            append(levelTitle)
            append(" command='")
            append(command)
            append("' repo=")
            append(repo.validationSnapshot())
        },
    )
    val result = validator(repo, command)
    AppLog.d("Validation", "Result level=$levelId passed=$result")
    result
}

private fun RepoState.validationSnapshot(): String = buildString {
    append("initialized=")
    append(initialized)
    append(", headBranch=")
    append(headBranch)
    append(", branches=")
    append(branches.keys.sorted())
    append(", tags=")
    append(tags.sorted())
    append(", remotes=")
    append(remotes.toSortedMap())
    append(", fetchedBranches=")
    append(fetchedBranches.sorted())
    append(", fetchHeadCount=")
    append(fetchHeadCount)
    append(", pushedBranches=")
    append(pushedBranches.sorted())
    append(", pushedTags=")
    append(pushedTags.sorted())
    append(", stashes=")
    append(stashes)
    append(", submodules=")
    append(submodules.toSortedMap())
    append(", maintenanceActions=")
    append(maintenanceActions.sorted())
    append(", config=")
    append(config.toSortedMap())
    append(", files=")
    append(files.map { file -> "${file.name}(staged=${file.staged},tracked=${file.tracked})" }.sorted())
    append(", commits=")
    append(commits.map { it.id to it.message })
}
