// src/framework/shadowlisp/primitives/SlLogic.ts
//
// Vision 49: REAL logic primitives - NO JavaScript cheating
//
// <sl-not>...</sl-not>
// <sl-and>...</sl-and>
// <sl-or>...</sl-or>
// <sl-even>...</sl-even>
// <sl-odd>...</sl-odd>
// <sl-mod>...</sl-mod>

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

/**
 * SlNot - Logical NOT.
 *
 * @example
 * <sl-not><sl-eq>...</sl-eq></sl-not>
 */
export class SlNot extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const child = this.children[0];
    if (!child) return true;

    const value = await this.evalChild(child, env);
    const result = value === false || value === null || value === undefined;
    this.infoKernel(`[SlNot] not(${value}) => ${result}`);
    return result;
  }

  toSExpr(): SExpr {
    const child = this.children[0];
    return ['not', child instanceof SlMatrixElement ? child.toSExpr() : null];
  }
}

/**
 * SlAnd - Logical AND (short-circuit).
 *
 * @example
 * <sl-and><sl-ref>a</sl-ref><sl-ref>b</sl-ref></sl-and>
 */
export class SlAnd extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    for (const child of this.children) {
      const value = await this.evalChild(child, env);
      if (value === false || value === null || value === undefined) {
        return false;
      }
    }
    return true;
  }

  toSExpr(): SExpr {
    return ['and', ...Array.from(this.children).map(c =>
      c instanceof SlMatrixElement ? c.toSExpr() : null
    )];
  }
}

/**
 * SlOr - Logical OR (short-circuit).
 *
 * @example
 * <sl-or><sl-ref>a</sl-ref><sl-ref>b</sl-ref></sl-or>
 */
export class SlOr extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    for (const child of this.children) {
      const value = await this.evalChild(child, env);
      if (value !== false && value !== null && value !== undefined) {
        return value;
      }
    }
    return false;
  }

  toSExpr(): SExpr {
    return ['or', ...Array.from(this.children).map(c =>
      c instanceof SlMatrixElement ? c.toSExpr() : null
    )];
  }
}

/**
 * SlMod - Modulo operation.
 *
 * @example
 * <sl-mod><sl-ref>index</sl-ref><sl-num>2</sl-num></sl-mod>
 */
export class SlMod extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const children = Array.from(this.children);
    if (children.length < 2) {
      this.warnKernel('[SlMod] Requires two children');
      return null;
    }

    const a = await this.evalChild(children[0], env);
    const b = await this.evalChild(children[1], env);

    if (typeof a !== 'number' || typeof b !== 'number') {
      this.warnKernel('[SlMod] Children must be numbers:', a, b);
      return null;
    }

    const result = a % b;
    this.infoKernel(`[SlMod] ${a} % ${b} => ${result}`);
    return result;
  }

  toSExpr(): SExpr {
    const children = Array.from(this.children);
    return ['mod',
      children[0] instanceof SlMatrixElement ? children[0].toSExpr() : null,
      children[1] instanceof SlMatrixElement ? children[1].toSExpr() : null
    ];
  }
}

/**
 * SlEven - Check if number is even.
 * Accepts both numbers and numeric strings (e.g., from sl-get-attr).
 *
 * @example
 * <sl-even><sl-ref>index</sl-ref></sl-even>
 * <sl-even><sl-get-attr name="data-index">...</sl-get-attr></sl-even>
 */
export class SlEven extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const child = this.children[0];
    if (!child) return false;

    const value = await this.evalChild(child, env);

    // Convert to number if string (e.g., from sl-get-attr)
    let num: number;
    if (typeof value === 'number') {
      num = value;
    } else if (typeof value === 'string') {
      num = parseInt(value, 10);
      if (isNaN(num)) {
        this.warnKernel('[SlEven] Could not parse string as number:', value);
        return false;
      }
    } else {
      this.warnKernel('[SlEven] Child did not evaluate to number or string:', value);
      return false;
    }

    const result = num % 2 === 0;
    this.infoKernel(`[SlEven] even?(${num}) => ${result}`);
    return result;
  }

  toSExpr(): SExpr {
    const child = this.children[0];
    return ['even?', child instanceof SlMatrixElement ? child.toSExpr() : null];
  }
}

/**
 * SlOdd - Check if number is odd.
 * Accepts both numbers and numeric strings (e.g., from sl-get-attr).
 *
 * @example
 * <sl-odd><sl-ref>index</sl-ref></sl-odd>
 * <sl-odd><sl-get-attr name="data-index">...</sl-get-attr></sl-odd>
 */
export class SlOdd extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const child = this.children[0];
    if (!child) return false;

    const value = await this.evalChild(child, env);

    // Convert to number if string (e.g., from sl-get-attr)
    let num: number;
    if (typeof value === 'number') {
      num = value;
    } else if (typeof value === 'string') {
      num = parseInt(value, 10);
      if (isNaN(num)) return false;
    } else {
      return false;
    }

    const result = num % 2 !== 0;
    this.infoKernel(`[SlOdd] odd?(${num}) => ${result}`);
    return result;
  }

  toSExpr(): SExpr {
    const child = this.children[0];
    return ['odd?', child instanceof SlMatrixElement ? child.toSExpr() : null];
  }
}

/**
 * SlZero - Check if number is zero.
 */
export class SlZero extends SlMatrixElement {
  async evalSelf(env: SExpr[]): Promise<SExpr> {
    const child = this.children[0];
    if (!child) return false;

    const value = await this.evalChild(child, env);
    return value === 0;
  }

  toSExpr(): SExpr {
    const child = this.children[0];
    return ['zero?', child instanceof SlMatrixElement ? child.toSExpr() : null];
  }
}

// Register all
if (!customElements.get('sl-not')) {
  customElements.define('sl-not', SlNot);
}
if (!customElements.get('sl-and')) {
  customElements.define('sl-and', SlAnd);
}
if (!customElements.get('sl-or')) {
  customElements.define('sl-or', SlOr);
}
if (!customElements.get('sl-mod')) {
  customElements.define('sl-mod', SlMod);
}
if (!customElements.get('sl-even')) {
  customElements.define('sl-even', SlEven);
}
if (!customElements.get('sl-odd')) {
  customElements.define('sl-odd', SlOdd);
}
if (!customElements.get('sl-zero')) {
  customElements.define('sl-zero', SlZero);
}
