// src/framework/shadowlisp/primitives/SlNum.ts
//
// Vision 49: Number literal element - IS a Matrix component
//
// <sl-num>42</sl-num> evaluates to the number 42.
// Numbers are self-evaluating - they return themselves.
//
// As a Matrix component:
//   mount: app.num-1
//   accepts: eval, set
//   Can be called via: mcp__matrix_call({ mount: "matrix/app.num-1", command: "eval" })
//   Can set value via: mcp__matrix_call({ mount: "matrix/app.num-1", command: "set", params: { value: 100 } })

import type { SExpr } from '../../../lisp/SExpr';
import { SlMatrixElement } from '../SlMatrixElement';
import { RequestReply } from '../../../engine/remoting/RequestReply';

/**
 * SlNum - Number literal element that IS a Matrix component.
 *
 * @example
 * <sl-num>42</sl-num>      => 42
 * <sl-num>3.14</sl-num>    => 3.14
 * <sl-num>-10</sl-num>     => -10
 *
 * Callable via Matrix MCP:
 * mcp__matrix_call({ mount: "matrix/app.num-1", command: "eval" })
 * => 42
 */
export class SlNum extends SlMatrixElement {
  private _setUnsub: (() => void) | null = null;

  /**
   * Override connectedCallback to bind additional commands.
   */
  connectedCallback(): void {
    super.connectedCallback();
    this._bindSetCommand();
  }

  /**
   * Override disconnectedCallback to clean up "set" subscription.
   */
  disconnectedCallback(): void {
    if (this._setUnsub) {
      this._setUnsub();
      this._setUnsub = null;
    }
    super.disconnectedCallback();
  }

  /**
   * Bind the "set" command to transport so MCP can change the value.
   */
  private _bindSetCommand(): void {
    if (!this._context?.transport) return;

    this._setUnsub = RequestReply.bind(
      this._context,
      this._mount,
      'set',
      async (params: { value: number }) => {
        if (typeof params?.value === 'number') {
          return this.setValue(params.value);
        }
        throw new Error('set command requires { value: number }');
      }
    );

    this.infoKernel(`[SlNum] Bound 'set' command at ${this._mount}`);
  }

  /**
   * Get the numeric value.
   */
  get value(): number {
    const text = this.textContent?.trim() ?? '0';
    return parseFloat(text);
  }

  /**
   * Set the numeric value.
   */
  set value(n: number) {
    this.textContent = String(n);
  }

  /**
   * Numbers are self-evaluating.
   * Called via Matrix transport when someone sends eval command.
   */
  async evalSelf(_env: SExpr[]): Promise<SExpr> {
    return this.value;
  }

  /**
   * Set the value via MCP command.
   * This allows Claude to directly modify the number!
   *
   * @example
   * mcp__matrix_call({
   *   mount: "matrix/counter.num-2",
   *   command: "set",
   *   params: { value: 100 }
   * })
   */
  setValue(newValue: number): number {
    this.value = newValue;

    // Dispatch event so renders can update
    this.dispatchEvent(new CustomEvent('sl-value-changed', {
      bubbles: true,
      detail: { value: newValue, mount: this._mount }
    }));

    return newValue;
  }

  /**
   * Convert to S-expression (just the number).
   */
  toSExpr(): SExpr {
    return this.value;
  }

}

// Register the custom element
if (!customElements.get('sl-num')) {
  customElements.define('sl-num', SlNum);
}
