import { Signal, signal } from '@angular/core';

/**
 * A tiny "Elm-style" store. The whole idea: all state lives in ONE value
 * (the Model). The only way to change it is to send a message (Msg) to a PURE
 * function `update(model, msg)` that returns the next Model. Nothing else
 * mutates state, so to understand the app you only read the update function.
 *
 * Side effects (HTTP, timers) do NOT go in `update` — that stays pure and easy
 * to test. Instead, effectful "command" functions call the network and then
 * `dispatch` a message describing what happened (e.g. Loaded / Failed).
 */
export interface Store<Model, Msg> {
  /** The current state, as a read-only Angular signal. */
  readonly model: Signal<Model>;
  /** Send a message; the model becomes update(model, msg). */
  dispatch(msg: Msg): void;
}

export function createStore<Model, Msg>(
  init: Model,
  update: (model: Model, msg: Msg) => Model,
): Store<Model, Msg> {
  const model = signal(init);
  return {
    model: model.asReadonly(),
    // Use `.update` (raw current value, no tracked read) not `set(update(model(), …))`:
    // dispatch is a command and must never subscribe its caller to `model`. Reading
    // `model()` here inside an effect that also dispatches makes the effect depend on
    // its own write and livelock the main thread (crashed the upload wizards).
    dispatch: (msg) => model.update((m) => update(m, msg)),
  };
}
