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.subcommands
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.request.post
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive

class SessionCommand : CliktCommand(name = "session") {
    override fun run() = Unit

    init {
        subcommands(
            SessionListCommand(),
            SessionResumeCommand(),
            SessionCancelCommand(),
            SessionEventsCommand(),
        )
    }
}

private fun parentJsonFlag(command: CliktCommand): Boolean =
    (command.currentContext.findRoot().command as? CorrexCli)?.json ?: false

private val sessionJson = Json { ignoreUnknownKeys = true }

class SessionListCommand : CliktCommand(name = "list") {
    private val limit by option("--limit", help = "Max sessions to return").default("20")
    private val host by option("--host").default("localhost")
    private val port by option("--port").default("$DEFAULT_PORT")

    override fun run(): Unit = runBlocking {
        val portInt = port.toIntOrNull() ?: DEFAULT_PORT
        val outputJson = parentJsonFlag(this@SessionListCommand)

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

        runCatching {
            val resp = client.get("http://$host:$portInt/sessions?limit=$limit")
            val body = resp.body<JsonArray>()
            if (outputJson) {
                println(body.toString())
            } else {
                body.forEach { el ->
                    val obj = el as? JsonObject ?: return@forEach
                    val sid = obj["sessionId"]?.jsonPrimitive?.content ?: ""
                    val wf = obj["workflowId"]?.jsonPrimitive?.content ?: ""
                    val status = obj["status"]?.jsonPrimitive?.content ?: ""
                    println("$sid  workflow=$wf  status=$status")
                }
            }
        }.getOrElse { e ->
            System.err.println("Error listing sessions: ${e.message}")
        }

        client.close()
    }
}

class SessionResumeCommand : CliktCommand(name = "resume") {
    private val id by argument("SESSION_ID")
    private val host by option("--host").default("localhost")
    private val port by option("--port").default("$DEFAULT_PORT")

    override fun run(): Unit = runBlocking {
        val portInt = port.toIntOrNull() ?: DEFAULT_PORT
        val outputJson = parentJsonFlag(this@SessionResumeCommand)

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

        runCatching {
            client.post("http://$host:$portInt/sessions/$id/resume")
            if (outputJson) {
                println("{\"sessionId\": \"$id\", \"action\": \"resumed\"}")
            } else {
                println("Session $id resumed.")
            }
        }.getOrElse { e ->
            System.err.println("Error resuming session: ${e.message}")
        }

        client.close()
    }
}

class SessionCancelCommand : CliktCommand(name = "cancel") {
    private val id by argument("SESSION_ID")
    private val host by option("--host").default("localhost")
    private val port by option("--port").default("$DEFAULT_PORT")

    override fun run(): Unit = runBlocking {
        val portInt = port.toIntOrNull() ?: DEFAULT_PORT
        val outputJson = parentJsonFlag(this@SessionCancelCommand)

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

        runCatching {
            client.post("http://$host:$portInt/sessions/$id/cancel")
            if (outputJson) {
                println("{\"sessionId\": \"$id\", \"action\": \"cancelled\"}")
            } else {
                println("Session $id cancelled.")
            }
        }.getOrElse { e ->
            System.err.println("Error cancelling session: ${e.message}")
        }

        client.close()
    }
}

class SessionEventsCommand : CliktCommand(name = "events") {
    private val id by argument("SESSION_ID")
    private val from by option("--from", help = "Start from event ID")
    private val host by option("--host").default("localhost")
    private val port by option("--port").default("$DEFAULT_PORT")

    override fun run(): Unit = runBlocking {
        val portInt = port.toIntOrNull() ?: DEFAULT_PORT
        val outputJson = parentJsonFlag(this@SessionEventsCommand)

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

        val query = from?.let { "?from=$it" } ?: ""

        runCatching {
            val resp = client.get("http://$host:$portInt/sessions/$id/events$query")
            val body = resp.body<JsonArray>()
            if (outputJson) {
                println(body.toString())
            } else {
                body.forEachIndexed { idx, el ->
                    println("[$idx] $el")
                }
            }
        }.getOrElse { e ->
            System.err.println("Error fetching events: ${e.message}")
        }

        client.close()
    }
}
