/**
 * SIM-12: Flussmanagement — Game Controller
 *
 * Rundenbasiertes Spiel. Verwaltet Zustand, Runden, Events,
 * Scoring und die gesamte Spielschleife.
 */

import {
  type Controls, type State, type Conditions, type ScoreBreakdown,
  type LevelDefinition, type LevelEvent,
  simulateRound, computeScore, computeTotalCost, checkWinLose, checkFinalWin,
  LEVELS,
} from './logic'

export type GamePhase = 'level-select' | 'intro' | 'playing' | 'event' | 'round-result' | 'won' | 'lost'

export interface RoundHistory {
  round: number
  state: State
  controls: Controls
  score: ScoreBreakdown
  cost: number
  event?: LevelEvent
}

export class FlussGame {
  // Zustand
  level!: LevelDefinition
  phase: GamePhase = 'level-select'
  round = 0
  state!: State
  controls!: Controls
  budgetRemaining = 0
  score!: ScoreBreakdown
  history: RoundHistory[] = []
  currentEvent: LevelEvent | null = null

  // Callbacks
  private onChange: (() => void) | null = null

  constructor() {
    this.reset()
  }

  subscribe(fn: () => void): void {
    this.onChange = fn
  }

  private notify(): void {
    this.onChange?.()
  }

  /** Level auswaehlen und Spiel starten */
  selectLevel(levelId: string): void {
    const lvl = LEVELS.find(l => l.id === levelId)
    if (!lvl) return
    this.level = lvl
    this.phase = 'intro'
    this.round = 0
    this.state = { ...lvl.initialState }
    this.controls = this.createEmptyControls()
    this.budgetRemaining = lvl.conditions.budget
    this.score = computeScore(this.state, lvl.scoreWeights)
    this.history = []
    this.currentEvent = null
    this.notify()
  }

  /** Intro bestaetigt → Spielphase */
  startPlaying(): void {
    this.phase = 'playing'
    this.notify()
  }

  /** Leere Controls erstellen (nur erlaubte Massnahmen) */
  private createEmptyControls(): Controls {
    return {
      straightening: 0,
      levees: 0,
      dredging: 0,
      floodplainRelease: 0,
      renaturation: 0,
      irrigation: 0,
    }
  }

  /** Massnahme aendern (Slider) */
  setControl(key: keyof Controls, value: number): void {
    if (!this.level.allowedControls.includes(key)) return

    // Limit pruefen
    const limit = this.level.controlLimits?.[key] ?? 100
    value = Math.max(0, Math.min(limit, value))

    // Budget pruefen: Differenz berechnen
    const oldControls = { ...this.controls }
    const testControls = { ...this.controls, [key]: value }
    const oldCost = computeTotalCost(oldControls)
    const newCost = computeTotalCost(testControls)
    const costDiff = newCost - oldCost

    if (costDiff > this.budgetRemaining + computeTotalCost(this.controls)) {
      // Nicht genug Budget — Maximum berechnen
      return
    }

    this.controls[key] = value
    this.notify()
  }

  /** Runde ausfuehren */
  executeRound(): void {
    if (this.phase !== 'playing') return
    if (this.round >= this.level.rounds) return

    this.round++
    const cost = computeTotalCost(this.controls)

    // Budget abziehen
    this.budgetRemaining = Math.max(0, this.budgetRemaining - cost)

    // Event fuer diese Runde?
    const event = this.level.events?.find(e => e.round === this.round) ?? null
    this.currentEvent = event

    // Simulation ausfuehren
    this.state = simulateRound(this.state, this.controls, this.level.conditions, event ?? undefined)
    this.score = computeScore(this.state, this.level.scoreWeights)

    // History speichern
    this.history.push({
      round: this.round,
      state: { ...this.state },
      controls: { ...this.controls },
      score: { ...this.score },
      cost,
      event: event ?? undefined,
    })

    // Win/Lose pruefen
    const result = checkWinLose(this.state, this.score, this.level)
    if (result === 'lose') {
      this.phase = 'lost'
      this.notify()
      return
    }

    // Event anzeigen?
    if (event) {
      this.phase = 'event'
      this.notify()
      return
    }

    // Letzte Runde?
    if (this.round >= this.level.rounds) {
      this.phase = checkFinalWin(this.score, this.level) ? 'won' : 'lost'
      this.notify()
      return
    }

    this.phase = 'round-result'
    this.notify()
  }

  /** Event bestaetigen → weiter spielen */
  acknowledgeEvent(): void {
    if (this.round >= this.level.rounds) {
      this.phase = checkFinalWin(this.score, this.level) ? 'won' : 'lost'
    } else {
      this.phase = 'playing'
    }
    this.currentEvent = null
    this.notify()
  }

  /** Rundenresultat bestaetigen → naechste Runde */
  continueAfterResult(): void {
    this.phase = 'playing'
    this.notify()
  }

  /** Alles zuruecksetzen */
  reset(): void {
    this.phase = 'level-select'
    this.round = 0
    this.history = []
    this.currentEvent = null
  }

  /** Serialisieren fuer Save */
  serialize(): string {
    return JSON.stringify({
      v: 1,
      levelId: this.level?.id,
      phase: this.phase,
      round: this.round,
      state: this.state,
      controls: this.controls,
      budgetRemaining: this.budgetRemaining,
      history: this.history,
    })
  }

  /** Deserialisieren */
  deserialize(json: string): boolean {
    try {
      const d = JSON.parse(json)
      if (d.v !== 1) return false
      const lvl = LEVELS.find(l => l.id === d.levelId)
      if (!lvl) return false
      this.level = lvl
      this.phase = d.phase
      this.round = d.round
      this.state = d.state
      this.controls = d.controls
      this.budgetRemaining = d.budgetRemaining
      this.history = d.history || []
      this.score = computeScore(this.state, lvl.scoreWeights)
      this.notify()
      return true
    } catch {
      return false
    }
  }
}
