// src/framework/shadowlisp/primitives/SlIf.ts
//
// Vision 49: If element - conditional branch - IS a Matrix component
//
// <sl-if>
//   <test>
//   <then>
//   <else>  (optional)
// </sl-if>
//
// As a Matrix component:
//   mount: app.if-1
//   accepts: eval
//   Evaluates children by calling their mounts via Matrix transport

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

/**
 * SlIf - Conditional element that IS a Matrix component.
 *
 * If the first child (test) evaluates to true, returns the second child (then).
 * Otherwise returns the third child (else) or null.
 *
 * @example
 * <sl-if mount="app.if-1">
 *   <sl-gt><sl-ref>x</sl-ref><sl-num>0</sl-num></sl-gt>
 *   <sl-str>positive</sl-str>
 *   <sl-str>non-positive</sl-str>
 * </sl-if>
 *
 * Callable via Matrix MCP:
 * mcp__matrix_call({ mount: "matrix/app.if-1", command: "eval", params: { env: [["x", 5]] } })
 * => "positive"
 */
export class SlIf extends SlMatrixElement {
  /**
   * Get the test element.
   */
  get test(): Element | null {
    return this.children[0] ?? null;
  }

  /**
   * Get the consequent (then) element.
   */
  get consequent(): Element | null {
    return this.children[1] ?? null;
  }

  /**
   * Get the alternate (else) element.
   */
  get alternate(): Element | null {
    return this.children[2] ?? null;
  }

  /**
   * Evaluate the conditional.
   * Called via Matrix transport when someone sends eval command.
   */
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const testEl = this.test;
    if (!testEl) {
      throw new Error('sl-if requires a test element');
    }

    // Evaluate the test by calling the child's mount
    const testResult = await this.evalChild(testEl, env);

    // Branch based on truthiness
    if (testResult !== false && testResult !== null) {
      // Truthy - evaluate consequent
      const thenEl = this.consequent;
      if (!thenEl) {
        return null;
      }
      return this.evalChild(thenEl, env);
    } else {
      // Falsy - evaluate alternate
      const elseEl = this.alternate;
      if (!elseEl) {
        return null;
      }
      return this.evalChild(elseEl, env);
    }
  }

  toSExpr(): SExpr {
    const test = this.test;
    const consequent = this.consequent;
    const alternate = this.alternate;

    const result: SExpr[] = ['if'];
    if (test instanceof SlMatrixElement) {
      result.push(test.toSExpr());
    } else {
      result.push(null);
    }
    if (consequent instanceof SlMatrixElement) {
      result.push(consequent.toSExpr());
    } else {
      result.push(null);
    }
    if (alternate instanceof SlMatrixElement) {
      result.push(alternate.toSExpr());
    }
    return result;
  }
}

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