/**
 * Simulation Base Class
 *
 * Jede Simulation erbt von dieser Klasse.
 * Trennung: Logik (testbar ohne Canvas) ↔ Rendering (Canvas)
 */

export interface SimulationMeta {
  id: string              // z.B. "sim-05"
  name: string            // z.B. "Treibhauseffekt-Simulator"

  /**
   * Unterstützte Schulstufen (internationale Nummerierung, 1–13)
   * z.B. [5, 6] = 1.+2. Klasse Mittelschule in AT, Klasse 5+6 in DE
   * Die länderspezifische Bezeichnung wird über education-levels.ts aufgelöst.
   */
  educationLevels: number[]

  /**
   * Primäre Zielstufe (für Sortierung und Empfehlung)
   * z.B. 5 = 1. Klasse Mittelschule in AT
   */
  primaryLevel: number

  kompetenzbereich: string
  lernziele: string[]
  basiskonzepte: string[]
  dpiMinuten: number      // erwartete Dauer in Minuten
  typ: 'sachsimulation' | 'planspiel' | 'exploration' | 'rollenspiel' | 'abstimmung'
  tier: 1 | 2 | 3

  /**
   * Braucht die Simulation Lesekompetenz?
   * false = auch für Grundstufe 1 nutzbar (rein visuell/auditiv)
   */
  requiresReading: boolean
}

export interface SimulationState {
  phase: 'intro' | 'predict' | 'simulate' | 'observe' | 'reflect' | 'complete'
  startTime: number
  elapsedMs: number
  variables: Record<string, number>
  predictions: Record<string, unknown>
  results: Record<string, unknown>
  reflections: string[]
}

export interface AssessmentData {
  processLog: Array<{
    timestamp: number
    action: string
    variable?: string
    oldValue?: number
    newValue?: number
  }>
  predictions: Record<string, unknown>
  results: Record<string, unknown>
  reflections: string[]
  duration: number
  completedPhases: string[]
}

export abstract class Simulation {
  readonly meta: SimulationMeta
  protected state: SimulationState

  constructor(meta: SimulationMeta) {
    this.meta = meta
    this.state = {
      phase: 'intro',
      startTime: Date.now(),
      elapsedMs: 0,
      variables: {},
      predictions: {},
      results: {},
      reflections: [],
    }
    this._processLog = []  // Sicherstellen, dass das Array existiert, bevor Subklassen Variablen setzen
  }

  /** Setzt eine Variable und loggt die Änderung */
  setVariable(name: string, value: number): void {
    const old = this.state.variables[name]
    this.state.variables[name] = value
    this.logAction('set-variable', name, old, value)
    this.onVariableChange(name, value)
  }

  /** Holt den aktuellen Wert einer Variable */
  getVariable(name: string): number {
    return this.state.variables[name] ?? 0
  }

  /** Speichert eine Vorhersage (Predict-Phase) */
  setPrediction(key: string, value: unknown): void {
    this.state.predictions[key] = value
    this.logAction('predict', key)
  }

  /** Speichert eine Reflexion (Reflect-Phase) */
  addReflection(text: string): void {
    this.state.reflections.push(text)
    this.logAction('reflect')
  }

  /** Wechselt zur nächsten Phase */
  nextPhase(): void {
    const phases: SimulationState['phase'][] = ['intro', 'predict', 'simulate', 'observe', 'reflect', 'complete']
    const idx = phases.indexOf(this.state.phase)
    if (idx < phases.length - 1) {
      this.state.phase = phases[idx + 1]
      this.logAction('phase-change')
    }
  }

  /** Gibt Assessment-Daten für das Lehrkräfte-Dashboard */
  getAssessmentData(): AssessmentData {
    return {
      processLog: [...this._processLog],
      predictions: { ...this.state.predictions },
      results: { ...this.state.results },
      reflections: [...this.state.reflections],
      duration: Date.now() - this.state.startTime,
      completedPhases: this.getCompletedPhases(),
    }
  }

  // --- Abstrakte Methoden — jede Simulation implementiert diese ---

  /** Berechne den aktuellen Zustand basierend auf Variablen */
  abstract compute(): Record<string, number>

  /** Reagiere auf Variablenänderung */
  protected abstract onVariableChange(name: string, value: number): void

  /** Gib die initialen Variablen und ihre Bereiche zurück */
  abstract getVariableRanges(): Record<string, { min: number; max: number; default: number; unit: string; label: string }>

  // --- Internes Logging ---

  private _processLog!: AssessmentData['processLog']

  private logAction(action: string, variable?: string, oldValue?: number, newValue?: number): void {
    this._processLog.push({
      timestamp: Date.now() - this.state.startTime,
      action,
      variable,
      oldValue,
      newValue,
    })
  }

  private getCompletedPhases(): string[] {
    const all: SimulationState['phase'][] = ['intro', 'predict', 'simulate', 'observe', 'reflect', 'complete']
    const idx = all.indexOf(this.state.phase)
    return all.slice(0, idx + 1)
  }
}
