package com.correx.core.sessions

import com.correx.core.events.events.AgentInstructionsBoundEvent
import com.correx.core.events.events.OperatorProfileBoundEvent
import com.correx.core.events.events.ProjectProfileBoundEvent
import com.correx.core.events.events.SessionWorkspaceBoundEvent
import com.correx.core.events.events.StageCompletedEvent
import com.correx.core.events.events.StageFailedEvent
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.TransitionExecutedEvent
import com.correx.core.events.events.WorkflowCompletedEvent
import com.correx.core.events.events.WorkflowFailedEvent

// The freestyle two-phase planning workflow. Its completion hands off to the execution workflow
// rather than ending the session, so it must not terminalize (see reduce()).
private const val PLANNING_WORKFLOW_ID = "freestyle_planning"

class DefaultSessionReducer : SessionReducer {

    override fun reduce(
        state: SessionState,
        event: StoredEvent
    ): SessionState {

        val payload = event.payload

        val newStatus = when (payload) {
            is StageFailedEvent ->
                SessionStatus.FAILED

            // A failed workflow is terminal for the session: no current workflow recovers after
            // one fails.
            is WorkflowFailedEvent ->
                SessionStatus.FAILED

            // A completed workflow is session-terminal EXCEPT the freestyle planning workflow, which
            // completes mid-session and hands off to the execution workflow (the handoff fires on
            // planning's WorkflowCompleted — see ServerModule). Terminalizing on it would falsely mark
            // the session done during the planning→execution gap; leaving it ACTIVE lets the real
            // (execution) completion terminalize. Without this, a normal single-workflow session — and
            // the execution phase — stayed ACTIVE forever, so clients/approval loops never saw done.
            is WorkflowCompletedEvent ->
                if (payload.workflowId == PLANNING_WORKFLOW_ID) state.status else SessionStatus.COMPLETED

            is StageCompletedEvent,
            is TransitionExecutedEvent ->
                SessionStatus.ACTIVE

            else ->
                state.status
        }

        val createdAt = state.createdAt
            ?: event.metadata.timestamp

        val boundWorkspace = when (payload) {
            is SessionWorkspaceBoundEvent ->
                BoundWorkspace(
                    workspaceRoot = payload.workspaceRoot,
                    allowedPaths = payload.allowedPaths,
                )
            else -> state.boundWorkspace
        }

        val boundProfile = when (payload) {
            is OperatorProfileBoundEvent ->
                BoundProfile(
                    about = payload.about,
                    approvalMode = payload.approvalMode,
                    preferredModels = payload.preferredModels,
                    conventions = payload.conventions,
                )
            else -> state.boundProfile
        }

        val boundProjectProfile = when (payload) {
            is ProjectProfileBoundEvent -> BoundProjectProfile(
                about = payload.about,
                conventions = payload.conventions,
                commands = payload.commands,
            )
            else -> state.boundProjectProfile
        }

        val boundAgentInstructions = when (payload) {
            is AgentInstructionsBoundEvent -> BoundAgentInstructions(
                sources = payload.sources,
                content = payload.content,
            )
            else -> state.boundAgentInstructions
        }

        return state.copy(
            status = newStatus,
            createdAt = createdAt,
            updatedAt = event.metadata.timestamp,
            boundWorkspace = boundWorkspace,
            boundProfile = boundProfile,
            boundProjectProfile = boundProjectProfile,
            boundAgentInstructions = boundAgentInstructions,
        )
    }
}
