// src/framework/shadowlisp/primitives/SlStr.ts
//
// Vision 49: String literal element - IS a Matrix component
//
// <sl-str>hello</sl-str> evaluates to the string "hello".
// Strings are self-evaluating.
//
// As a Matrix component:
//   mount: app.str-1
//   accepts: eval, set
//   Can set value via: mcp__matrix_call({ mount: "matrix/app.str-1", command: "set", params: { value: "new" } })

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

/**
 * SlStr - String literal element that IS a Matrix component.
 *
 * @example
 * <sl-str>hello</sl-str>           => "hello"
 * <sl-str>Hello, World!</sl-str>   => "Hello, World!"
 *
 * Callable via Matrix MCP:
 * mcp__matrix_call({ mount: "matrix/app.str-1", command: "set", params: { value: "world" } })
 */
export class SlStr extends SlMatrixElement {
  private _setUnsub: (() => void) | null = null;

  /**
   * Override connectedCallback to bind "set" command.
   */
  connectedCallback(): void {
    super.connectedCallback();
    this._bindSetCommand();
  }

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

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

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

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

  /**
   * Get the string value.
   */
  get value(): string {
    return this.textContent ?? '';
  }

  /**
   * Set the string value.
   */
  set value(s: string) {
    this.textContent = s;
  }

  /**
   * Set value via MCP and dispatch event.
   */
  setValue(newValue: string): string {
    this.value = newValue;

    this.dispatchEvent(new CustomEvent('sl-value-changed', {
      bubbles: true,
      detail: { value: newValue, mount: this._mount }
    }));

    return newValue;
  }

  /**
   * Strings are self-evaluating.
   */
  async evalSelf(_env: SExpr[]): Promise<SExpr> {
    return this.value;
  }

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

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