/**
 * Phase 6.1 — LongTermMemory
 * - Append snapshots (from Phase 5 via toSnapshot/createSnapshot)
 * - Compute rolling metrics: stability, adaptationRate, evolutionScore
 * - Persist to JSON (storage/phase6_long_term_memory.json)
 *
 * Also acts as the backing store for the alias "MemoryKernel" (see memory_kernel.ts).
 */

import fs from 'node:fs';
import { Snapshot, LongTermState, LongTermMetrics } from '../../types/core';
import { Phase6Config } from '../../config/phase6.config';

/** ---------- load / save ---------- */
function loadState(): LongTermState {
  if (!fs.existsSync(Phase6Config.storagePath)) {
    return { snapshots: [], metrics: null };
  }
  try {
    const parsed = JSON.parse(fs.readFileSync(Phase6Config.storagePath, 'utf-8'));
    return {
      snapshots: Array.isArray(parsed?.snapshots) ? parsed.snapshots : [],
      metrics: parsed?.metrics ?? null,
    };
  } catch {
    return { snapshots: [], metrics: null };
  }
}

function saveState(state: LongTermState) {
  fs.mkdirSync('./storage', { recursive: true });
  fs.writeFileSync(Phase6Config.storagePath, JSON.stringify(state, null, 2));
}

/** ---------- metrics ---------- */
function computeMetrics(snapshots: Snapshot[]): LongTermMetrics {
  const win = Phase6Config.metricsWindow;
  const recent = snapshots.slice(-win);

  // stability = variance of all outcome scores in the recent window (lower is better)
  const scores = recent.flatMap((s) => s.outcomes.map((o) => o.score));
  const mean = avg(scores);
  const variance = avg(scores.map((x) => (x - mean) ** 2));
  const stability = variance || 0;

  // adaptationRate = mean(delta of average snapshot score) across the recent window
  const deltas: number[] = [];
  for (let i = 1; i < recent.length; i++) {
    const prev = snapshotMean(recent[i - 1]);
    const curr = snapshotMean(recent[i]);
    deltas.push(curr - prev);
  }
  const adaptationRate = avg(deltas) || 0;

  // evolutionScore = improvement discounted by instability
  const evolutionScore = Math.max(0, adaptationRate - stability);

  return { stability, adaptationRate, evolutionScore };
}

function snapshotMean(s: Snapshot) {
  const vals = s.outcomes.map((o) => o.score);
  return avg(vals);
}
const avg = (arr: number[]) => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0);

/** ---------- public API ---------- */
export const LongTermMemory = {
  /**
   * Append a snapshot, trim to rolling window, recompute metrics, persist.
   */
  appendSnapshot(snapshot: Snapshot) {
    const state = loadState();
    state.snapshots.push(snapshot);

    if (state.snapshots.length > Phase6Config.maxSnapshots) {
      state.snapshots = state.snapshots.slice(-Phase6Config.maxSnapshots);
    }

    state.metrics = computeMetrics(state.snapshots);
    saveState(state);
    return state;
  },

  /**
   * Load in-memory state (snapshots + metrics) from disk.
   */
  getState(): LongTermState {
    return loadState();
  },
};
