// Pure helpers for array-valued local state: parse the declared initial value
// and enforce the declared sizing policy (dynamic / capped / fixed). Shared by
// localstate.ts (write path) and logic.ts (node handlers + migration).

import type { StateVar } from './types';

export type ArrVal = number | ArrVal[];
export const ARRAY_MAX = 1_000_000;

// zeroFill builds a length-n array of zeros (flat; fixed nested init must come
// from an explicit `initial` literal).
function zeroFill(n: number): ArrVal[] {
  return new Array(Math.max(0, n)).fill(0);
}

// parseInitialArray returns the starting contents of an array local.
export function parseInitialArray(sv: StateVar): ArrVal[] {
  const cap = sv.capacity ?? 0;
  const raw = (sv.initial ?? '').trim();
  let parsed: ArrVal[] | null = null;
  if (raw) {
    try {
      const j = JSON.parse(raw);
      if (Array.isArray(j)) parsed = j as ArrVal[];
    } catch { parsed = null; }
  }
  if (sv.sizing === 'fixed') {
    if (!parsed) return zeroFill(cap);
    // truncate / zero-pad to capacity
    const out = parsed.slice(0, cap);
    while (out.length < cap) out.push(0);
    return out;
  }
  return parsed ?? [];
}

// applySizing returns arr clamped to the declared policy.
//   dynamic → unchanged (but globally capped at ARRAY_MAX, dropping oldest)
//   capped  → keep at most capacity elements, dropping oldest (ring/FIFO)
//   fixed   → exactly capacity elements (truncate / zero-pad); never grow/shrink
export function applySizing(arr: ArrVal[], sv: StateVar): ArrVal[] {
  const cap = sv.capacity ?? 0;
  switch (sv.sizing) {
    case 'fixed': {
      const out = arr.slice(0, cap);
      while (out.length < cap) out.push(0);
      return out;
    }
    case 'capped':
      return arr.length > cap ? arr.slice(arr.length - cap) : arr;
    default:
      return arr.length > ARRAY_MAX ? arr.slice(arr.length - ARRAY_MAX) : arr;
  }
}
