package solutions.tretter.githugandroid

import androidx.compose.runtime.saveable.listSaver

val RepoStateSaver = listSaver<RepoState, Any>(
    save = { state ->
        listOf(
            state.initialized,
            state.headBranch,
            state.files.flatMap { listOf(it.name, it.content, it.staged.toString(), it.tracked.toString(), it.deleted.toString()) },
            state.commits.flatMap {
                listOf(
                    it.id,
                    it.message,
                    it.authorTimestampSeconds?.toString().orEmpty(),
                    it.parentCount.toString(),
                )
            },
            state.branches.flatMap { listOf(it.key, it.value.toString()) },
            state.currentDir,
            state.tags,
            state.remotes.flatMap { listOf(it.key, it.value) },
            state.config.flatMap { listOf(it.key, it.value) },
            state.stashes,
            state.fetchedBranches.toList(),
            state.pushedBranches.toList(),
            state.pushedTags.toList(),
            state.submodules.flatMap { listOf(it.key, it.value) },
            state.maintenanceActions.toList(),
            state.fetchHeadCount,
            state.interactiveAddSession?.let {
                listOf(
                    it.target.orEmpty(),
                    it.awaitingUpdateSelection.toString(),
                    it.selectionPrompt,
                    it.selectionAction,
                )
            }.orEmpty(),
        )
    },
    restore = { saved ->
        val initialized = saved[0] as Boolean
        val headBranch = saved[1] as String
        val fileParts = saved[2] as List<*>
        val commitParts = saved[3] as List<*>
        val branchParts = saved[4] as List<*>
        val tags = saved[6] as List<*>
        val remoteParts = saved[7] as List<*>
        val configParts = saved.getOrNull(8) as? List<*> ?: emptyList<Any>()
        val stashes = saved.getOrNull(9) as? List<*> ?: emptyList<Any>()
        val fetchedBranches = saved.getOrNull(10) as? List<*> ?: emptyList<Any>()
        val pushedBranches = saved.getOrNull(11) as? List<*> ?: emptyList<Any>()
        val pushedTags = saved.getOrNull(12) as? List<*> ?: emptyList<Any>()
        val submoduleParts = saved.getOrNull(13) as? List<*> ?: emptyList<Any>()
        val maintenanceActions = saved.getOrNull(14) as? List<*> ?: emptyList<Any>()
        val fetchHeadCount = saved.getOrNull(15) as? Int ?: 0
        val interactiveAddSessionParts = saved.getOrNull(16) as? List<*> ?: emptyList<Any>()
        RepoState(
            initialized = initialized,
            headBranch = headBranch,
            files = fileParts.chunked(if (fileParts.size % 5 == 0) 5 else 4).map {
                GitFile(
                    name = it[0] as String,
                    content = it[1] as String,
                    staged = (it[2] as String).toBoolean(),
                    tracked = (it[3] as String).toBoolean(),
                    deleted = (it.getOrNull(4) as? String)?.toBoolean() ?: false,
                )
            },
            commits = commitParts.restoreCommitNodes(),
            branches = branchParts.chunked(2).associate { (it[0] as String) to (it[1] as String).toInt() },
            currentDir = saved[5] as String,
            tags = tags.filterIsInstance<String>(),
            remotes = remoteParts.chunked(2).associate { (it[0] as String) to (it[1] as String) },
            config = configParts.chunked(2).associate { (it[0] as String) to (it[1] as String) },
            stashes = stashes.filterIsInstance<String>(),
            fetchedBranches = fetchedBranches.filterIsInstance<String>().toSet(),
            fetchHeadCount = fetchHeadCount,
            pushedBranches = pushedBranches.filterIsInstance<String>().toSet(),
            pushedTags = pushedTags.filterIsInstance<String>().toSet(),
            submodules = submoduleParts.chunked(2).associate { (it[0] as String) to (it[1] as String) },
            maintenanceActions = maintenanceActions.filterIsInstance<String>().toSet(),
            interactiveAddSession = interactiveAddSessionParts.takeIf { it.size >= 2 }?.let {
                InteractiveAddSession(
                    target = (it[0] as String).ifBlank { null },
                    awaitingUpdateSelection = (it[1] as String).toBoolean(),
                    selectionPrompt = it.getOrNull(2) as? String ?: "Update>>",
                    selectionAction = it.getOrNull(3) as? String ?: "update",
                )
            },
        )
    }
)

private fun List<*>.restoreCommitNodes(): List<CommitNode> {
    val hasTimestampAndParentColumns = size % 4 == 0 && chunked(4).all { chunk ->
        val timestamp = chunk.getOrNull(2) as? String
        val parentCount = chunk.getOrNull(3) as? String
        (timestamp.isNullOrEmpty() || timestamp.toLongOrNull()?.let { it >= 100_000_000L } == true) &&
            parentCount?.toIntOrNull() != null
    }
    val hasTimestampColumn = !hasTimestampAndParentColumns && size % 3 == 0 && chunked(3).all { chunk ->
        val timestamp = chunk.getOrNull(2) as? String
        timestamp.isNullOrEmpty() || timestamp.toLongOrNull()?.let { it >= 100_000_000L } == true
    }
    val width = when {
        hasTimestampAndParentColumns -> 4
        hasTimestampColumn -> 3
        else -> 2
    }

    return chunked(width).mapNotNull { chunk ->
        val id = chunk.getOrNull(0) as? String ?: return@mapNotNull null
        val message = chunk.getOrNull(1) as? String ?: ""
        CommitNode(
            id = id,
            message = message,
            authorTimestampSeconds = if (hasTimestampAndParentColumns || hasTimestampColumn) {
                (chunk.getOrNull(2) as? String)?.toLongOrNull()
            } else {
                null
            },
            parentCount = if (hasTimestampAndParentColumns) (chunk.getOrNull(3) as? String)?.toIntOrNull() ?: 0 else 0,
        )
    }
}
