package com.correx.apps.server.routes

import com.correx.apps.server.ServerModule
import com.correx.apps.server.protocol.SessionConfigDto
import com.correx.apps.server.ws.SessionStreamHandler
import com.correx.core.events.types.SessionId
import com.correx.core.utils.TypeId
import io.ktor.http.HttpStatusCode
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.route
import io.ktor.server.websocket.webSocket
import kotlinx.serialization.Serializable
import org.slf4j.LoggerFactory
import java.util.*

@Serializable
data class StartSessionRequest(val workflowId: String, val config: SessionConfigDto? = null)

@Serializable
data class SessionSummaryResponse(
    val sessionId: String,
    val status: String,
    val intent: String? = null,
    val workflowId: String? = null,
    val stageCount: Int = 0,
    val createdAt: String? = null,
    val lastActivityAt: String? = null,
)

@Serializable
data class SessionStateResponse(val sessionId: String, val status: String, val createdAt: String?)

@Serializable
data class EventResponse(val eventId: String, val sequence: Long, val sessionId: String)

@Serializable
data class StartSessionResponse(val sessionId: String)

private val log = LoggerFactory.getLogger("com.correx.apps.server.routes.SessionRoutes")

fun Route.sessionRoutes(module: ServerModule) {
    val streamHandler = SessionStreamHandler(module)
    route("/sessions") {
        get {
            val summaries = module.listSessionSummaries().map { s ->
                SessionSummaryResponse(
                    sessionId = s.sessionId.value,
                    status = s.status,
                    intent = s.intent,
                    workflowId = s.workflowId,
                    stageCount = s.stageCount,
                    createdAt = s.createdAt?.toString(),
                    lastActivityAt = s.lastActivityAt?.toString(),
                )
            }
            call.respond(summaries)
        }
        startSessionRoute(module)
        route("/{id}") {
            getSessionRoute(module)
            cancelSessionRoute(module)
            undoSessionRoute(module)
            resumeSessionRoute(module)
            getEventsRoute(module)
            webSocket("/stream") {
                val id = call.parameters["id"] ?: return@webSocket
                val lastEventId = call.request.queryParameters["lastEventId"]?.toLongOrNull()
                streamHandler.handle(this, TypeId(id), lastEventId)
            }
        }
    }
}

private fun Route.startSessionRoute(module: ServerModule) {
    post {
        val body = call.receive<StartSessionRequest>()
        val graph = module.workflowRegistry.find(body.workflowId)
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Unknown workflowId: ${body.workflowId}")
        val sessionId: SessionId = TypeId(UUID.randomUUID().toString())
        module.launchSessionRun(sessionId, graph)
        call.respond(HttpStatusCode.Accepted, StartSessionResponse(sessionId.value))
    }
}

private fun Route.getSessionRoute(module: ServerModule) {
    get {
        val id = call.parameters["id"]
            ?: return@get call.respond(HttpStatusCode.BadRequest, "Missing session id")
        val session = runCatching { module.sessionRepository.getSession(TypeId(id)) }.getOrNull()
            ?: return@get call.respond(HttpStatusCode.NotFound, "Session not found")
        call.respond(
            SessionStateResponse(
                sessionId = session.sessionId.value,
                status = session.state.status.name,
                createdAt = session.state.createdAt?.toString(),
            )
        )
    }
}

private fun Route.cancelSessionRoute(module: ServerModule) {
    post("/cancel") {
        val id = call.parameters["id"]
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Missing session id")
        module.orchestrator.cancel(TypeId(id))
        call.respond(HttpStatusCode.OK)
    }
}

private fun Route.undoSessionRoute(module: ServerModule) {
    post("/undo") {
        val id = call.parameters["id"]
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Missing session id")
        val summary = module.sessionUndoService.undo(TypeId(id))
        call.respond(HttpStatusCode.OK, summary)
    }
}

private fun Route.resumeSessionRoute(module: ServerModule) {
    post("/resume") {
        val id = call.parameters["id"]
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Missing session id")
        val orchState = runCatching { module.orchestrationRepository.getState(TypeId(id)) }.getOrNull()
            ?: return@post call.respond(HttpStatusCode.NotFound, "Session not found")
        val workflowId = orchState.workflowId.takeIf { it.isNotBlank() }
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Session has no workflowId")
        val graph = module.workflowRegistry.find(workflowId)
            ?: return@post call.respond(HttpStatusCode.BadRequest, "Unknown workflowId: $workflowId")
        module.launchSessionResumeWithRehydrate(TypeId(id), graph)
        call.respond(HttpStatusCode.Accepted)
    }
}

private fun Route.getEventsRoute(module: ServerModule) {
    get("/events") {
        val id = call.parameters["id"]
            ?: return@get call.respond(HttpStatusCode.BadRequest, "Missing session id")
        val from = call.request.queryParameters["from"]?.toLongOrNull() ?: 0L
        val events = module.eventStore.readFrom(TypeId(id), from)
        call.respond(
            events.map { e ->
                EventResponse(
                    eventId = e.metadata.eventId.value,
                    sequence = e.sequence,
                    sessionId = e.metadata.sessionId.value,
                )
            }
        )
    }
}
