// src/framework/shadowlisp/primitives/SlElement.ts
//
// Vision 49: DOM Element creator - THE BRIDGE FROM LISP TO VISIBLE DOM
//
// <sl-element tag="div" class="row">...children...</sl-element>
//
// This is the KEY primitive that makes layout-as-function-application work.
// When evaluated, it creates an actual DOM element that can be inserted.
//
// Combined with sl-lambda, this enables layout combinators:
//
//   <sl-define name="row">
//     <sl-lambda params="...children">
//       <sl-element tag="div" class="flex-row">
//         <sl-spread><sl-ref>children</sl-ref></sl-spread>
//       </sl-element>
//     </sl-lambda>
//   </sl-define>
//
// Then: (row (card a) (card b)) creates a flex row containing two cards!

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

/**
 * SlElement - Creates visible DOM elements from Lisp evaluation.
 *
 * This is the bridge that makes "layout as function application" work.
 * When evaluated, returns an HTMLElement that can be rendered.
 *
 * @example
 * <sl-element tag="div" class="card" style="padding: 1rem">
 *   <sl-element tag="h2"><sl-ref>title</sl-ref></sl-element>
 *   <sl-ref>content</sl-ref>
 * </sl-element>
 */
export class SlElement extends SlMatrixElement {
  /**
   * Get the tag name to create.
   */
  get tag(): string {
    return this.getAttribute('tag') ?? 'div';
  }

  /**
   * Get CSS classes.
   */
  get cssClass(): string {
    return this.getAttribute('class') ?? '';
  }

  /**
   * Get inline styles.
   */
  get cssStyle(): string {
    return this.getAttribute('style') ?? '';
  }

  /**
   * Evaluate: create an actual DOM element with evaluated children.
   */
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    // Create the actual DOM element
    const el = document.createElement(this.tag);

    if (this.cssClass) {
      el.className = this.cssClass;
    }

    if (this.cssStyle) {
      el.setAttribute('style', this.cssStyle);
    }

    // Copy other attributes (except tag, class, style, mount)
    for (const attr of this.attributes) {
      if (!['tag', 'class', 'style', 'mount', 'id'].includes(attr.name)) {
        el.setAttribute(attr.name, attr.value);
      }
    }

    // Evaluate children and append to element
    // Use childNodes to include text nodes, not just elements
    for (const child of this.childNodes) {
      if (child.nodeType === Node.TEXT_NODE) {
        // Text node - append directly if non-empty
        const text = child.textContent?.trim();
        if (text) {
          el.appendChild(document.createTextNode(text));
        }
      } else if (child.nodeType === Node.ELEMENT_NODE) {
        // Element - evaluate it
        const childValue = await this.evalChild(child as Element, env);
        this._appendValue(el, childValue);
      }
    }

    // Mark as generated by ShadowLisp (for debugging)
    el.setAttribute('data-sl-generated', 'true');

    return el as unknown as SExpr;
  }

  /**
   * Append a value to an element, handling different types.
   */
  private _appendValue(parent: HTMLElement, value: SExpr): void {
    if (value === null || value === undefined) {
      return;
    }

    // DOM Element - append directly
    if (value instanceof HTMLElement) {
      parent.appendChild(value);
      return;
    }

    // Array - append each element
    if (Array.isArray(value)) {
      for (const item of value) {
        this._appendValue(parent, item);
      }
      return;
    }

    // Primitive (string, number) - create text node
    if (typeof value === 'string' || typeof value === 'number') {
      parent.appendChild(document.createTextNode(String(value)));
      return;
    }

    // Object with toString - use that
    if (typeof value === 'object' && value !== null) {
      parent.appendChild(document.createTextNode(String(value)));
      return;
    }
  }

  /**
   * Convert to S-expression.
   */
  toSExpr(): SExpr {
    return ['element', this.tag, ...Array.from(this.children).map(c =>
      c instanceof SlMatrixElement ? c.toSExpr() : c.textContent
    )];
  }
}

// Register
if (!customElements.get('sl-element')) {
  customElements.define('sl-element', SlElement);
}
