package com.correx.apps.cli.commands

import com.correx.apps.cli.CorrexCli
import com.correx.apps.cli.DEFAULT_PORT
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.ProgramResult
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class TimelineEntryDto(
    val sequence: Long,
    val type: String,
    val stageId: String?,
    val summary: String,
)

@Serializable
data class ReplayReportDto(
    val sessionId: String,
    val eventCount: Long,
    val timeline: List<TimelineEntryDto>,
    val digest: String,
    val deterministic: Boolean,
)

private const val SEPARATOR_WIDTH = 110

private fun renderReport(report: ReplayReportDto): String {
    val lines = mutableListOf<String>()
    lines += "Session:       ${report.sessionId}"
    lines += "Events:        ${report.eventCount}"
    lines += "Digest:        ${report.digest}"
    lines += "Deterministic: ${report.deterministic}"
    lines += ""
    lines += "%-6s  %-35s  %-20s  %s".format("seq", "type", "stageId", "summary")
    lines += "-".repeat(SEPARATOR_WIDTH)
    for (entry in report.timeline) {
        lines += "%-6d  %-35s  %-20s  %s".format(
            entry.sequence,
            entry.type,
            entry.stageId ?: "-",
            entry.summary,
        )
    }
    return lines.joinToString("\n")
}

class ReplayCommand : CliktCommand(name = "replay") {
    private val sessionId by argument("sessionId")
    private val host by option("--host").default("localhost")
    private val port by option("--port").default("$DEFAULT_PORT")

    private val replayJson = Json { ignoreUnknownKeys = true }

    override fun run(): Unit = runBlocking {
        val portInt = port.toIntOrNull() ?: DEFAULT_PORT
        val outputJson = (currentContext.findRoot().command as? CorrexCli)?.json ?: false
        val url = "http://$host:$portInt/sessions/$sessionId/replay"

        val client = HttpClient(CIO) {
            install(ContentNegotiation) { json(replayJson) }
        }

        val nonDeterministic = runCatching {
            if (outputJson) {
                val body = client.get(url).bodyAsText()
                println(body)
                val parsed = replayJson.decodeFromString<ReplayReportDto>(body)
                !parsed.deterministic
            } else {
                val report = client.get(url).body<ReplayReportDto>()
                println(renderReport(report))
                !report.deterministic
            }
        }.getOrElse { e ->
            System.err.println("Error fetching replay: ${e.message}")
            false
        }

        client.close()
        if (nonDeterministic) throw ProgramResult(1)
    }
}
