package com.correx.infrastructure.tools.filesystem

import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.events.FileWrittenEvent
import com.correx.core.events.types.ArtifactId
import java.nio.file.Files
import java.nio.file.Path

/**
 * Reverses a recorded [FileWrittenEvent] using only the event + CAS (invariants #1/#8):
 * restores the pre-image blob, or deletes the file if it didn't previously exist.
 * The restore goes through the same atomic write seam ([AtomicFileWriter]) and is
 * jailed through [PathJail] (fail-closed) and idempotent. Pure function of recorded
 * state — never re-observes the environment to decide what to do.
 */
class FileMutationReverser(
    private val artifactStore: ArtifactStore,
    private val allowedRoots: Set<Path>,
) {
    @Suppress("ReturnCount")
    suspend fun revert(event: FileWrittenEvent): RevertResult {
        val target = Path.of(event.path)
        if (!PathJail.isContained(target, allowedRoots)) {
            return RevertResult.Rejected("target outside allowed roots: ${event.path}")
        }
        return if (event.preExisted) {
            val hash = event.preImageHash
                ?: return RevertResult.Rejected("preExisted but no pre-image hash: ${event.path}")
            val bytes = artifactStore.get(ArtifactId(hash))
                ?: return RevertResult.Rejected("pre-image blob not found in CAS: $hash")
            runCatching { AtomicFileWriter.write(target, bytes) }
                .fold({ RevertResult.Restored }, { RevertResult.Failed(it.message ?: "io error") })
        } else {
            runCatching { Files.deleteIfExists(target) }
                .fold({ RevertResult.Deleted }, { RevertResult.Failed(it.message ?: "io error") })
        }
    }
}

sealed interface RevertResult {
    /** Pre-image bytes were written back to the target. */
    data object Restored : RevertResult

    /** The (previously non-existent) target was removed. */
    data object Deleted : RevertResult

    /** The event could not be reversed safely (jail/missing blob); no change made. */
    data class Rejected(val reason: String) : RevertResult

    /** An IO error occurred during the restore. */
    data class Failed(val reason: String) : RevertResult
}
