// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import {useSyncExternalStore} from "react";

type Unsub = () => void;


/**
 * A simple reactive state container that holds a value of type `T` that provides methods to get, set, and subscribe.
 */
export type Cell<T> = {
  /**
   * Returns the current value stored in the cell.
   */
  get: () => T;
  /**
   * Updates the cell's value, pass either a direct value or an updater function.
   *
   * @example
   * ```ts
   * count.set(5);
   * count.set(prev => prev + 1);
   * ```
   */
  set: (next: T | ((prev: T) => T)) => void;

  /**
   * Subscribe to changes in the cell's value, meaning the provided callback is called whenever the value changes.
   * Returns an unsubscribe function.
   *
   * @example
   * ```ts
   * const unsubscribe = count.subscribe(() => console.log(count.get()));
   * // later:
   * unsubscribe();
   * ```
   */
  subscribe: (callback: () => void) => Unsub;
};

/**
 * Creates a new reactive state container (`Cell`) with an initial value.
 *
 * This function allows you to store and mutate state outside of React,
 * while still supporting subscriptions for reactivity.
 *
 * @param initial - The initial value for the cell.
 * @returns A Cell object with `get`, `set`, and `subscribe` methods.
 *
 * @example
 * ```ts
 * const count = cell(0);
 * count.set(10);
 * console.log(count.get()); // 10
 * ```
 */
export function cell<T>(initial: T): Cell<T> {
  let value = initial;
  const listeners = new Set<() => void>();
  return {
    get: () => value,
    set: (next) => {
      value = typeof next === "function" ? (next as (v: T) => T)(value) : next;
      for (const l of listeners) l();
    },
    subscribe: (callback) => {
      listeners.add(callback);
      return () => listeners.delete(callback);
    },
  };
}

/**
 * React hook that subscribes a component to a Cell.
 *
 * Automatically re-renders the component whenever the Cell's value changes.
 * Uses React’s built-in `useSyncExternalStore` for correct subscription behavior.
 *
 * @param c - The cell to subscribe to.
 * @returns The current value of the cell.
 *
 * @example
 * ```tsx
 * const count = cell(0);
 *
 * function Counter() {
 *   const value = useCell(count);
 *   return (
 *     <button onClick={() => count.set(v => v + 1)}>
 *       Count: {value}
 *     </button>
 *   );
 * }
 * ```
 */
export function useCell<T>(c: Cell<T>) {
  return useSyncExternalStore(c.subscribe, c.get, c.get);
}
