import { type GameDefinition } from './gameDefinition'
import { type GameEvent } from './gameEvent'
import { type GameResult } from './gameResult'
import { type GameSessionState } from './gameSessionState'
import { EMPTY_GAME_PRESENTATION_STATE, type GamePresentationState } from '../presentation/presentationState'
import { EMPTY_HUD_PRESENTATION_STATE, type HudPresentationState } from '../presentation/hudPresentationState'
import { EMPTY_MAP_PRESENTATION_STATE, type MapPresentationState } from '../presentation/mapPresentationState'
import { ClassicSequentialRule } from '../rules/classicSequentialRule'
import { ScoreORule } from '../rules/scoreORule'
import { type RulePlugin } from '../rules/rulePlugin'

export class GameRuntime {
  definition: GameDefinition | null
  plugin: RulePlugin | null
  state: GameSessionState | null
  presentation: GamePresentationState
  mapPresentation: MapPresentationState
  hudPresentation: HudPresentationState
  lastResult: GameResult | null

  constructor() {
    this.definition = null
    this.plugin = null
    this.state = null
    this.presentation = EMPTY_GAME_PRESENTATION_STATE
    this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE
    this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE
    this.lastResult = null
  }

  clear(): void {
    this.definition = null
    this.plugin = null
    this.state = null
    this.presentation = EMPTY_GAME_PRESENTATION_STATE
    this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE
    this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE
    this.lastResult = null
  }

  loadDefinition(definition: GameDefinition): GameResult {
    this.definition = definition
    this.plugin = this.resolvePlugin(definition)
    this.state = this.plugin.initialize(definition)
    const result: GameResult = {
      nextState: this.state,
      presentation: this.plugin.buildPresentation(definition, this.state),
      effects: [],
    }
    this.presentation = result.presentation
    this.mapPresentation = result.presentation.map
    this.hudPresentation = result.presentation.hud
    this.lastResult = result
    return result
  }

  restoreDefinition(definition: GameDefinition, state: GameSessionState): GameResult {
    this.definition = definition
    this.plugin = this.resolvePlugin(definition)
    this.state = {
      status: state.status,
      endReason: state.endReason,
      startedAt: state.startedAt,
      endedAt: state.endedAt,
      completedControlIds: state.completedControlIds.slice(),
      skippedControlIds: state.skippedControlIds.slice(),
      currentTargetControlId: state.currentTargetControlId,
      inRangeControlId: state.inRangeControlId,
      score: state.score,
      guidanceState: state.guidanceState,
      modeState: state.modeState
        ? JSON.parse(JSON.stringify(state.modeState)) as Record<string, unknown>
        : null,
    }
    const result: GameResult = {
      nextState: this.state,
      presentation: this.plugin.buildPresentation(definition, this.state),
      effects: [],
    }
    this.presentation = result.presentation
    this.mapPresentation = result.presentation.map
    this.hudPresentation = result.presentation.hud
    this.lastResult = result
    return result
  }

  startSession(startAt = Date.now()): GameResult {
    return this.dispatch({ type: 'session_started', at: startAt })
  }

  dispatch(event: GameEvent): GameResult {
    if (!this.definition || !this.plugin || !this.state) {
      const emptyState: GameSessionState = {
        status: 'idle',
        endReason: null,
        startedAt: null,
        endedAt: null,
        completedControlIds: [],
        skippedControlIds: [],
        currentTargetControlId: null,
        inRangeControlId: null,
        score: 0,
        guidanceState: 'searching',
        modeState: null,
      }
      const result: GameResult = {
        nextState: emptyState,
        presentation: EMPTY_GAME_PRESENTATION_STATE,
        effects: [],
      }
      this.lastResult = result
      this.presentation = result.presentation
      this.mapPresentation = result.presentation.map
      this.hudPresentation = result.presentation.hud
      return result
    }

    const result = this.plugin.reduce(this.definition, this.state, event)
    this.state = result.nextState
    this.presentation = result.presentation
    this.mapPresentation = result.presentation.map
    this.hudPresentation = result.presentation.hud
    this.lastResult = result
    return result
  }

  getPresentation(): GamePresentationState {
    return this.presentation
  }

  getMapPresentation(): MapPresentationState {
    return this.mapPresentation
  }

  getHudPresentation(): HudPresentationState {
    return this.hudPresentation
  }

  resolvePlugin(definition: GameDefinition): RulePlugin {
    if (definition.mode === 'classic-sequential') {
      return new ClassicSequentialRule()
    }

    if (definition.mode === 'score-o') {
      return new ScoreORule()
    }

    throw new Error(`未支持的玩法模式: ${definition.mode}`)
  }
}
