// src/framework/shadowlisp/primitives/SlSet.ts
//
// Vision 49: Set element - mutate a binding - IS a Matrix component
//
// <sl-set name="x">
//   <new-value>
// </sl-set>

import type { SExpr } from '../../../lisp/SExpr';
import { SlMatrixElement } from '../SlMatrixElement';
import type { SlEnv } from '../SlBootstrap';

/**
 * SlSet - Mutation element that IS a Matrix component.
 *
 * Changes the value of an existing binding.
 *
 * @example
 * <sl-set name="counter">
 *   <sl-add><sl-ref>counter</sl-ref><sl-num>1</sl-num></sl-add>
 * </sl-set>
 */
export class SlSet extends SlMatrixElement {
  /**
   * Get the name being mutated.
   */
  get varName(): string {
    return this.getAttribute('name') ?? '';
  }

  /**
   * Set the name being mutated.
   */
  set varName(s: string) {
    this.setAttribute('name', s);
  }

  /**
   * Get the new value element.
   */
  get valueElement(): Element | null {
    return this.firstElementChild;
  }

  /**
   * Set evaluates the new value and updates the binding.
   */
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const name = this.varName;
    if (!name) {
      throw new Error('sl-set requires a name attribute');
    }

    const valueEl = this.valueElement;

    // Evaluate the new value
    const value = valueEl ? await this.evalChild(valueEl, env) : null;

    // Find and update the binding in the environment
    for (const binding of (env as SlEnv)) {
      if (binding[0] === name) {
        binding[1] = value;
        return value;
      }
    }

    throw new Error(`Cannot set undefined variable: ${name}`);
  }

  toSExpr(): SExpr {
    const valueEl = this.valueElement;
    const valueSExpr = (valueEl instanceof SlMatrixElement)
      ? valueEl.toSExpr()
      : null;

    return ['set!', this.varName, valueSExpr];
  }
}

if (!customElements.get('sl-set')) {
  customElements.define('sl-set', SlSet);
}
