package com.correx.apps.server.undo

import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.events.FileWrittenEvent
import com.correx.core.events.events.SessionWorkspaceBoundEvent
import com.correx.core.events.stores.EventStore
import com.correx.core.events.types.SessionId
import com.correx.infrastructure.tools.filesystem.FileMutationReverser
import com.correx.infrastructure.tools.filesystem.RevertResult
import kotlinx.serialization.Serializable
import java.nio.file.Path

@Serializable
data class UndoSummary(
    val sessionId: String,
    val reverted: Int,
    val deleted: Int,
    val rejected: Int,
    val failed: Int,
    val details: List<String> = emptyList(),
)

/**
 * Reverses every file mutation a session performed, newest-first, using only the
 * event log + CAS (the reverser). Newest-first so a path written multiple times in
 * the session ends at its pre-session content.
 *
 * Jail roots for each undo = [bootRoots] ∪ the workspace recorded in the session's
 * [SessionWorkspaceBoundEvent] (if present). When no workspace event exists the
 * effective roots are identical to [bootRoots], preserving pre-existing behaviour.
 */
class SessionUndoService(
    private val eventStore: EventStore,
    private val artifactStore: ArtifactStore,
    private val bootRoots: Set<Path>,
) {
    suspend fun undo(sessionId: SessionId): UndoSummary {
        val events = eventStore.read(sessionId)

        val sessionRoots: Set<Path> = events
            .mapNotNull { it.payload as? SessionWorkspaceBoundEvent }
            .lastOrNull()
            ?.let { bound ->
                buildSet {
                    add(Path.of(bound.workspaceRoot))
                    bound.allowedPaths.forEach { add(Path.of(it)) }
                }
            }
            ?: emptySet()

        val effectiveRoots = bootRoots + sessionRoots
        val reverser = FileMutationReverser(artifactStore, effectiveRoots)

        val mutations = events
            .mapNotNull { it.payload as? FileWrittenEvent }
            .asReversed()

        var reverted = 0
        var deleted = 0
        var rejected = 0
        var failed = 0
        val details = mutableListOf<String>()

        for (event in mutations) {
            when (val result = reverser.revert(event)) {
                RevertResult.Restored -> { reverted++; details += "restored ${event.path}" }
                RevertResult.Deleted -> { deleted++; details += "deleted ${event.path}" }
                is RevertResult.Rejected -> { rejected++; details += "rejected ${event.path}: ${result.reason}" }
                is RevertResult.Failed -> { failed++; details += "failed ${event.path}: ${result.reason}" }
            }
        }

        return UndoSummary(sessionId.value, reverted, deleted, rejected, failed, details)
    }
}
