from datetime import datetime from typing import Optional from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.orm import GameState from app.models.schemas import GamePhase, GameStateResponse class GameStateManager: PHASE_ORDER = GamePhase.ordered_phases() PHASE_COUNT = len(PHASE_ORDER) async def get_state(self, session: AsyncSession) -> Optional[GameState]: result = await session.execute( select(GameState).order_by(GameState.created_at.desc()).limit(1) ) return result.scalars().first() async def get_or_create_state(self, session: AsyncSession) -> GameState: state = await self.get_state(session) if state is None: state = GameState( current_phase=GamePhase.INTRO.value, is_running=False, is_paused=False, ) session.add(state) await session.commit() await session.refresh(state) return state async def next_phase(self, session: AsyncSession) -> GameState: state = await self.get_or_create_state(session) current = state.current_phase try: idx = self.PHASE_ORDER.index(current) if idx < self.PHASE_COUNT - 1: state.current_phase = self.PHASE_ORDER[idx + 1] except ValueError: pass state.phase_started_at = datetime.utcnow() state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state async def prev_phase(self, session: AsyncSession) -> GameState: state = await self.get_or_create_state(session) current = state.current_phase try: idx = self.PHASE_ORDER.index(current) if idx > 0: state.current_phase = self.PHASE_ORDER[idx - 1] except ValueError: pass state.phase_started_at = datetime.utcnow() state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state def get_current_phase(self, state: GameState) -> str: return state.current_phase def get_progress_percent(self, state: GameState) -> float: try: idx = self.PHASE_ORDER.index(state.current_phase) return (idx / max(self.PHASE_COUNT - 1, 1)) * 100 except ValueError: return 0.0 async def start_game(self, session: AsyncSession, script_id: str) -> GameState: state = await self.get_or_create_state(session) state.active_script_id = script_id state.current_phase = GamePhase.INTRO.value state.is_running = True state.is_paused = False state.phase_started_at = datetime.utcnow() state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state async def pause_game(self, session: AsyncSession) -> GameState: state = await self.get_or_create_state(session) state.is_paused = True state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state async def resume_game(self, session: AsyncSession) -> GameState: state = await self.get_or_create_state(session) state.is_paused = False state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state async def reset(self, session: AsyncSession) -> GameState: state = await self.get_or_create_state(session) state.current_phase = GamePhase.INTRO.value state.current_speaker_id = None state.speaker_order = [] state.active_script_id = None state.is_running = False state.is_paused = False state.phase_started_at = None state.config = {} state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state async def set_speaker(self, session: AsyncSession, character_id: str) -> GameState: state = await self.get_or_create_state(session) state.current_speaker_id = character_id state.updated_at = datetime.utcnow() await session.commit() await session.refresh(state) return state def to_response(self, state: GameState) -> GameStateResponse: return GameStateResponse( id=state.id, current_phase=GamePhase(state.current_phase), current_speaker_id=state.current_speaker_id, speaker_order=state.speaker_order or [], active_script_id=state.active_script_id, is_running=state.is_running, is_paused=state.is_paused, phase_started_at=state.phase_started_at, config=state.config or {}, progress_percent=self.get_progress_percent(state), ) game_state_manager = GameStateManager()