package de.arxsilex.maptools.converter.desktop

internal data class WorkerProgressUpdate(
    val message: String,
    val progress: Float?,
    val phase: String? = null,
)

internal object WorkerProgressFormatter {
    private val objectProgress = Regex("(?:Progress: )?(Ways|Relations) ([0-9.,]+) / ([0-9.,]+)")
    private val unboundedObjectProgress = Regex("Progress: (Nodes|Ways|Relations) ([0-9.,]+)\\s*$")
    private val writeProgress = Regex("written ([0-9]+)%")

    fun formatOrNull(
        line: String,
        strings: AppStrings = LocalizationCatalog.strings(AppLanguage.GERMAN),
    ): String? = progressOrNull(line, strings)?.message

    fun progressOrNull(
        line: String,
        strings: AppStrings = LocalizationCatalog.strings(AppLanguage.GERMAN),
    ): WorkerProgressUpdate? {
        if (!line.startsWith("MTC-WORKER")) {
            writeProgress.find(line)?.let {
                val percent = it.groupValues[1].toInt().coerceIn(0, 100)
                return WorkerProgressUpdate(
                    "${strings["progress.writing_mapsforge"]} · $percent %",
                    percent / 100f,
                )
            }
            objectProgress.find(line)?.let { match ->
                val current = match.groupValues[2].normalizedNumber().toLongOrNull() ?: return null
                val total = match.groupValues[3].normalizedNumber().toLongOrNull() ?: return null
                if (total <= 0) return null
                val percent = (current * 100 / total).coerceIn(0, 100)
                val type = strings[if (match.groupValues[1] == "Ways") "progress.ways" else "progress.relations"]
                return WorkerProgressUpdate(
                    "${strings("progress.processing", type)} · ${formatCount(current, strings)} / " +
                        "${formatCount(total, strings)} ($percent %)",
                    percent / 100f,
                )
            }
            unboundedObjectProgress.find(line)?.let { match ->
                val current = match.groupValues[2].normalizedNumber().toLongOrNull() ?: return null
                val type = when (match.groupValues[1]) {
                    "Nodes" -> strings["progress.nodes"]
                    "Ways" -> strings["progress.ways"]
                    else -> strings["progress.relations"]
                }
                return WorkerProgressUpdate(
                    "${strings("progress.processing", type)} · ${formatCount(current, strings)}",
                    null,
                )
            }
            return null
        }
        val values = line.substringAfter("MTC-WORKER").trim().split(' ')
            .mapNotNull { token ->
                val separator = token.indexOf('=')
                if (separator <= 0) null else token.substring(0, separator) to token.substring(separator + 1)
            }.toMap()
        val phase = when (values["phase"]) {
            "crop" -> strings["phase.crop"]
            "combine-crop" -> strings["phase.combine_crop"]
            "combine-merge" -> strings["phase.combine_merge"]
            "mtci-index" -> strings["phase.mtci_index"]
            "mtci-build" -> strings["phase.mtci_build"]
            "mtci-select" -> strings["phase.mtci_select"]
            "mtci-materialize" -> strings["phase.mtci_materialize"]
            "mtci-pbf" -> strings["phase.mtci_pbf"]
            "mtci-materialize-nodes" -> strings["phase.materialize_nodes"]
            "mtci-materialize-ways" -> strings["phase.materialize_ways"]
            "mtci-materialize-relations" -> strings["phase.materialize_relations"]
            "mtci-pbf-nodes" -> strings["phase.pbf_nodes"]
            "mtci-pbf-ways" -> strings["phase.pbf_ways"]
            "mtci-pbf-relations" -> strings["phase.pbf_relations"]
            "mapsforge-map" -> strings["phase.mapsforge_map"]
            "mapsforge-poi" -> strings["phase.mapsforge_poi"]
            "mapsforge-poi-schema-v3" -> strings["phase.mapsforge_poi"]
            "mapsforge-source" -> strings["phase.mapsforge_source"]
            "poi-convert" -> strings["phase.poi_convert"]
            "poi-filter" -> strings["phase.poi_filter"]
            "poi-merge" -> strings["phase.poi_merge"]
            else -> strings["phase.conversion"]
        }
        return when (values["status"]) {
            "started" -> WorkerProgressUpdate("$phase …", null, values["phase"])
            "running" -> {
                val current = values["current"]?.toLongOrNull()
                val total = values["total"]?.toLongOrNull()
                val progress = if (current != null && total != null && total > 0L) {
                    (current.toDouble() / total).toFloat().coerceIn(0f, 1f)
                } else null
                val message = when {
                    progress != null -> buildString {
                        val percent = (progress * 100).toInt()
                        append("$phase · ${formatCount(requireNotNull(current), strings)} / ")
                        append("${formatCount(requireNotNull(total), strings)} ($percent %)")
                        values["selected"]?.toLongOrNull()?.let {
                            append(" · ${strings("progress.selected", formatCount(it, strings))}")
                        }
                    }
                    values["selected"] != null -> "$phase · ${strings("progress.selected", values["selected"])}"
                    else -> values["elapsedSeconds"]?.toLongOrNull()
                        ?.let { "$phase · ${formatDuration(it)}" } ?: phase
                }
                WorkerProgressUpdate(message, progress, values["phase"])
            }
            "completed" -> WorkerProgressUpdate("$phase · ${strings["progress.completed"]}", 1f, values["phase"])
            "cached" -> WorkerProgressUpdate("$phase · ${strings["progress.cached"]}", 1f, values["phase"])
            "fallback" -> WorkerProgressUpdate(strings["progress.fallback"], null, values["phase"])
            "failed" -> WorkerProgressUpdate(strings["progress.failed"], null, values["phase"])
            else -> WorkerProgressUpdate(phase, null, values["phase"])
        }
    }

    private fun formatDuration(seconds: Long): String =
        "%02d:%02d".format(seconds / 60, seconds % 60)

    private fun formatCount(value: Long, strings: AppStrings): String =
        String.format(strings.language.locale, "%,d", value)

    private fun String.normalizedNumber(): String = replace(".", "").replace(",", "")
}
