InMemoryBattleSessionGateway.kt

package io.github.lishangbu.avalon.game.battle.engine.adapter.memory.session

import io.github.lishangbu.avalon.game.battle.engine.adapter.memory.store.InMemoryBattleSessionStore
import io.github.lishangbu.avalon.game.battle.engine.application.gateway.BattleSessionGateway
import io.github.lishangbu.avalon.game.battle.engine.core.model.BattleType
import io.github.lishangbu.avalon.game.battle.engine.core.model.UnitState
import io.github.lishangbu.avalon.game.battle.engine.core.session.BattleSession
import io.github.lishangbu.avalon.game.battle.engine.core.session.BattleSessionChoice
import io.github.lishangbu.avalon.game.battle.engine.core.session.BattleSessionFactory
import io.github.lishangbu.avalon.game.battle.engine.core.session.BattleSessionQuery
import io.github.lishangbu.avalon.game.battle.engine.core.session.BattleSessionTurnResult
import io.github.lishangbu.avalon.game.battle.engine.spi.store.BattleSessionStore

/**
 * BattleSessionGateway 的内存版实现。
 *
 * 设计意图:
 * - 为当前阶段提供最小可用的会话网关实现。
 * - 适用于本地测试、单进程运行和 application 层通过 gateway 驱动 session 的场景。
 */
class InMemoryBattleSessionGateway(
    private val sessionFactory: BattleSessionFactory,
    private val sessionStore: BattleSessionStore = InMemoryBattleSessionStore(),
) : BattleSessionGateway {
    override fun createSession(
        sessionId: String,
        formatId: String,
    ): BattleSessionQuery {
        require(sessionStore.find(sessionId) == null) { "Session '$sessionId' already exists." }
        val session = sessionFactory.create(sessionId, formatId)
        sessionStore.save(sessionId, session)
        return session.query()
    }

    override fun configureSession(
        sessionId: String,
        battleKind: BattleType,
        capturableSideId: String?,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.configureBattle(battleKind, capturableSideId)
        return session.query()
    }

    override fun startSession(sessionId: String): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.start()
        return session.query()
    }

    override fun registerSide(
        sessionId: String,
        sideId: String,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.registerSide(sideId)
        return session.query()
    }

    override fun registerUnit(
        sessionId: String,
        sideId: String,
        unit: UnitState,
        active: Boolean,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.registerUnit(sideId, unit, active)
        return session.query()
    }

    override fun submitChoice(
        sessionId: String,
        choice: BattleSessionChoice,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.submitChoice(choice)
        return session.query()
    }

    override fun submitChoices(
        sessionId: String,
        choices: List<BattleSessionChoice>,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.submitChoices(choices)
        return session.query()
    }

    override fun submitReplacementChoice(
        sessionId: String,
        sideId: String,
        incomingUnitId: String,
    ): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.submitReplacementChoice(sideId, incomingUnitId)
        return session.query()
    }

    override fun markSessionSettled(sessionId: String): BattleSessionQuery {
        val session = requireSession(sessionId)
        session.markSettled()
        return session.query()
    }

    override fun resolveTurn(sessionId: String): BattleSessionTurnResult {
        val session = requireSession(sessionId)
        return session.resolveTurn()
    }

    override fun querySession(sessionId: String): BattleSessionQuery = requireSession(sessionId).query()

    private fun requireSession(sessionId: String): BattleSession =
        requireNotNull(sessionStore.find(sessionId)) {
            "Session '$sessionId' was not found."
        }
}