// src/lisp/components/BootstrapNode.ts
// BootstrapNode MatrixActor - Virtual LISP Machine Foundation
//
// The BootstrapNode is the "last turtle" - it implements McCarthy's 7 primitives
// plus essential additions. All higher-level LISP can be built on top of this.
//
// Key feature: Traveling Interpreter (interpreter-in-message pattern)
// The eval code travels WITH the message, enabling distributed evaluation.
//
// Reference: Vision/18-VIRTUAL-LISP-MACHINE.md

import { MatrixActor } from '../../core/MatrixActor';
import type { SExpr } from '../SExpr';
import type { ITravelingMessage } from '../ITravelingMessage';
import { METACIRCULAR_EVAL } from '../vlm/MetacircularEval';

/**
 * BootstrapNode - The foundation of the Virtual LISP Machine
 *
 * This component implements the bare minimum needed to bootstrap LISP:
 * - McCarthy's 7 primitives: quote, atom, eq, car, cdr, cons, cond
 * - Essential additions: null?, pair?, list, number?, boolean?, and, or, not
 * - Special forms: if, lambda, letrec, apply
 * - Arithmetic for practical use: +, -, *, /, <, >, =
 *
 * The magic: It can receive a TravelingMessage containing the interpreter
 * itself as data. This enables the "interpreter-in-message" pattern where
 * the evaluation semantics travel with the computation.
 *
 * Mount: {namespace}.bootstrap
 *
 * @example
 * // Simple evaluation
 * await callComponent('flowpad.bootstrap', 'eval', {
 *   expr: ['+', 1, 2, 3]
 * })
 * // → 6
 *
 * @example
 * // Traveling interpreter pattern
 * await callComponent('flowpad.bootstrap', 'evalTraveling', {
 *   expr: ['*', 5, 5],
 *   eval: METACIRCULAR_EVAL,  // The interpreter travels with the message!
 *   continuation: 'results.topic'
 * })
 */
export class BootstrapNode extends MatrixActor {
  static accepts = {
    // Standard eval for simple expressions
    eval: { expr: 'any' },
    // Traveling message format (interpreter-in-message)
    evalTraveling: { expr: 'any', eval: 'any?', continuation: 'string?' },
  };

  static emits = {
    evaluated: { expr: 'any', result: 'any', durationMs: 'number' },
    evalError: { expr: 'any', error: 'string' },
    resultSent: { continuation: 'string', result: 'any' },
  };

  protected async onBootstrap(): Promise<void> {
    // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
    console.log(`[BootstrapNode] Initialized at ${this._context?.mount}`);
  }

  /**
   * Evaluate expression with bootstrap primitives only
   */
  async onEval(params: { expr: SExpr }): Promise<unknown> {
    const startTime = performance.now();
    const { expr } = params;

    try {
      const result = this._bootstrapEval(expr, {});
      const durationMs = performance.now() - startTime;
      this.emit('evaluated', { expr, result, durationMs });
      return result;
    } catch (err: unknown) {
      const error = err instanceof Error ? err.message : String(err);
      this.emit('evalError', { expr, error });
      throw err;
    }
  }

  /**
   * Evaluate traveling message (interpreter-in-message pattern)
   *
   * THIS IS THE KEY: The eval field contains the LISP interpreter as data.
   * The bootstrap node runs the traveling interpreter on the expression.
   * The continuation field is where to send the result.
   *
   * If no eval is provided, use the default METACIRCULAR_EVAL.
   */
  async onEvalTraveling(params: ITravelingMessage): Promise<unknown> {
    const startTime = performance.now();
    const { expr, eval: travelingEval, continuation } = params;

    try {
      let result: unknown;

      if (travelingEval) {
        // THE MAGIC: Run the traveling interpreter on the expression!
        // The traveling eval is LISP code that expects 'e' to be bound.
        result = this._bootstrapEval(travelingEval, { e: expr });
      } else {
        // No traveling eval provided - use the default metacircular eval
        result = this._bootstrapEval(METACIRCULAR_EVAL, { e: expr });
      }

      const durationMs = performance.now() - startTime;

      // Send result to continuation if specified
      if (continuation && this._context?.transport) {
        this._context.transport.publish(continuation, {
          ok: true,
          result,
          durationMs,
          from: this._context.mount,
          travelingEval: !!travelingEval,
        });
        this.emit('resultSent', { continuation, result });
      }

      this.emit('evaluated', { expr, result, durationMs });
      return result;
    } catch (err: unknown) {
      const error = err instanceof Error ? err.message : String(err);

      // Send error to continuation if specified
      if (continuation && this._context?.transport) {
        this._context.transport.publish(continuation, {
          ok: false,
          error: { message: error },
          from: this._context.mount,
        });
      }

      this.emit('evalError', { expr, error });
      throw err;
    }
  }

  /**
   * Bootstrap evaluator - the "last turtle"
   * Knows ONLY McCarthy's 7 primitives + essential additions.
   */
  private _bootstrapEval(expr: SExpr, env: Record<string, unknown>): unknown {
    // Self-evaluating: numbers, booleans, null
    if (typeof expr === 'number' || typeof expr === 'boolean' || expr === null) {
      return expr;
    }

    // Variable lookup
    if (typeof expr === 'string') {
      if (expr in env) return env[expr];
      return expr; // Return as literal if not bound
    }

    // Empty list
    if (!Array.isArray(expr) || expr.length === 0) {
      return expr;
    }

    const [op, ...args] = expr;
    if (typeof op !== 'string') {
      throw new Error(`Bootstrap: invalid op ${JSON.stringify(op)}`);
    }

    // =========================================================================
    // THE 7 PRIMITIVES (McCarthy's original + eq?)
    // =========================================================================

    // QUOTE - return unevaluated
    if (op === 'quote') return args[0];

    // CAR - first element
    if (op === 'car') {
      const list = this._bootstrapEval(args[0], env);
      return Array.isArray(list) ? list[0] : null;
    }

    // CDR - rest of list
    if (op === 'cdr') {
      const list = this._bootstrapEval(args[0], env);
      return Array.isArray(list) ? list.slice(1) : [];
    }

    // CONS - construct list
    if (op === 'cons') {
      const head = this._bootstrapEval(args[0], env);
      const tail = this._bootstrapEval(args[1], env);
      return [head, ...(Array.isArray(tail) ? tail : [tail])];
    }

    // ATOM? - is it NOT a list?
    if (op === 'atom?' || op === 'atom') {
      const val = this._bootstrapEval(args[0], env);
      return !Array.isArray(val);
    }

    // EQ? - are two atoms equal?
    if (op === 'eq?' || op === 'eq') {
      const a = this._bootstrapEval(args[0], env);
      const b = this._bootstrapEval(args[1], env);
      return a === b;
    }

    // COND - conditional
    if (op === 'cond') {
      for (const clause of args) {
        const [test, result] = clause as SExpr[];
        if (test === 'else' || this._bootstrapEval(test, env)) {
          return this._bootstrapEval(result, env);
        }
      }
      return null;
    }

    // =========================================================================
    // ESSENTIAL ADDITIONS (for practical use)
    // =========================================================================

    // NULL? - is it null or empty list?
    if (op === 'null?') {
      const val = this._bootstrapEval(args[0], env);
      return val === null || (Array.isArray(val) && val.length === 0);
    }

    // PAIR? - is it a non-empty list?
    if (op === 'pair?') {
      const val = this._bootstrapEval(args[0], env);
      return Array.isArray(val) && val.length > 0;
    }

    // LIST - construct a list from args
    if (op === 'list') {
      return args.map((a) => this._bootstrapEval(a, env));
    }

    // NUMBER? - type check
    if (op === 'number?') {
      const val = this._bootstrapEval(args[0], env);
      return typeof val === 'number';
    }

    // BOOLEAN? - type check
    if (op === 'boolean?') {
      const val = this._bootstrapEval(args[0], env);
      return typeof val === 'boolean';
    }

    // STRING? - type check
    if (op === 'string?') {
      const val = this._bootstrapEval(args[0], env);
      return typeof val === 'string';
    }

    // LIST? - type check
    if (op === 'list?') {
      const val = this._bootstrapEval(args[0], env);
      return Array.isArray(val);
    }

    // CADR, CADDR, CADDDR - list accessors
    if (op === 'cadr' || op === 'second') {
      const list = this._bootstrapEval(args[0], env) as unknown[];
      return Array.isArray(list) && list.length > 1 ? list[1] : null;
    }
    if (op === 'caddr' || op === 'third') {
      const list = this._bootstrapEval(args[0], env) as unknown[];
      return Array.isArray(list) && list.length > 2 ? list[2] : null;
    }
    if (op === 'cadddr' || op === 'fourth') {
      const list = this._bootstrapEval(args[0], env) as unknown[];
      return Array.isArray(list) && list.length > 3 ? list[3] : null;
    }

    // =========================================================================
    // BOOLEAN OPERATORS
    // =========================================================================

    if (op === 'and') {
      for (const arg of args) {
        if (!this._bootstrapEval(arg, env)) return false;
      }
      return true;
    }

    if (op === 'or') {
      for (const arg of args) {
        const val = this._bootstrapEval(arg, env);
        if (val) return val;
      }
      return false;
    }

    if (op === 'not') {
      return !this._bootstrapEval(args[0], env);
    }

    // =========================================================================
    // CONTROL FLOW
    // =========================================================================

    // IF - conditional
    if (op === 'if') {
      const test = this._bootstrapEval(args[0], env);
      return test
        ? this._bootstrapEval(args[1], env)
        : args.length > 2
          ? this._bootstrapEval(args[2], env)
          : null;
    }

    // LAMBDA - create closure
    if (op === 'lambda') {
      const params = args[0] as string[];
      const body = args[1];
      return {
        __type: 'closure',
        params,
        body,
        env: { ...env },
      };
    }

    // LETREC - recursive bindings (essential for metacircular eval)
    if (op === 'letrec') {
      const bindings = args[0] as SExpr[][];
      const body = args[1];

      // Create new environment with placeholders
      const newEnv = { ...env };

      // First pass: create closures with forward references
      const closures: Record<string, unknown> = {};
      for (const [name, expr] of bindings) {
        if (Array.isArray(expr) && expr[0] === 'lambda') {
          const params = expr[1] as string[];
          const lambdaBody = expr[2];
          closures[name as string] = {
            __type: 'closure',
            params,
            body: lambdaBody,
            env: newEnv, // Will be mutated below
          };
          newEnv[name as string] = closures[name as string];
        } else {
          newEnv[name as string] = this._bootstrapEval(expr, newEnv);
        }
      }

      // Evaluate body in new environment
      return this._bootstrapEval(body, newEnv);
    }

    // =========================================================================
    // ARITHMETIC (for practical use)
    // =========================================================================

    if (op === '+') {
      const nums = args.map((a) => this._bootstrapEval(a, env) as number);
      return nums.reduce((sum, n) => sum + n, 0);
    }

    if (op === '-') {
      const nums = args.map((a) => this._bootstrapEval(a, env) as number);
      if (nums.length === 1) return -nums[0];
      return nums.slice(1).reduce((diff, n) => diff - n, nums[0]);
    }

    if (op === '*') {
      const nums = args.map((a) => this._bootstrapEval(a, env) as number);
      return nums.reduce((prod, n) => prod * n, 1);
    }

    if (op === '/') {
      const nums = args.map((a) => this._bootstrapEval(a, env) as number);
      if (nums.length === 1) return 1 / nums[0];
      return nums.slice(1).reduce((quot, n) => quot / n, nums[0]);
    }

    if (op === '<') {
      const a = this._bootstrapEval(args[0], env) as number;
      const b = this._bootstrapEval(args[1], env) as number;
      return a < b;
    }

    if (op === '>') {
      const a = this._bootstrapEval(args[0], env) as number;
      const b = this._bootstrapEval(args[1], env) as number;
      return a > b;
    }

    if (op === '=') {
      const a = this._bootstrapEval(args[0], env);
      const b = this._bootstrapEval(args[1], env);
      return a === b;
    }

    if (op === '<=') {
      const a = this._bootstrapEval(args[0], env) as number;
      const b = this._bootstrapEval(args[1], env) as number;
      return a <= b;
    }

    if (op === '>=') {
      const a = this._bootstrapEval(args[0], env) as number;
      const b = this._bootstrapEval(args[1], env) as number;
      return a >= b;
    }

    // =========================================================================
    // APPLY - function application
    // =========================================================================

    if (op === 'apply') {
      const fn = this._bootstrapEval(args[0], env) as {
        __type: string;
        params: string[];
        body: SExpr;
        env: Record<string, unknown>;
      };
      const fnArgs = this._bootstrapEval(args[1], env) as unknown[];

      if (typeof fn === 'string') {
        // It's a primitive - recursively evaluate
        return this._bootstrapEval([fn, ...fnArgs] as SExpr, env);
      }

      if (fn && fn.__type === 'closure') {
        // Apply closure
        const newEnv = { ...fn.env };
        fn.params.forEach((p, i) => {
          newEnv[p] = fnArgs[i];
        });
        return this._bootstrapEval(fn.body, newEnv);
      }

      throw new Error(`Cannot apply: ${JSON.stringify(fn)}`);
    }

    // =========================================================================
    // FUNCTION APPLICATION (user-defined functions)
    // =========================================================================

    // Look up the operator
    const fn = this._bootstrapEval(op, env) as {
      __type?: string;
      params?: string[];
      body?: SExpr;
      env?: Record<string, unknown>;
    };

    if (fn && typeof fn === 'object' && fn.__type === 'closure') {
      // It's a closure - evaluate args and apply
      const evaledArgs = args.map((a) => this._bootstrapEval(a, env));
      const newEnv = { ...fn.env };
      fn.params!.forEach((p, i) => {
        newEnv[p] = evaledArgs[i];
      });
      return this._bootstrapEval(fn.body!, newEnv);
    }

    throw new Error(`Bootstrap: unknown operation: ${op}`);
  }
}
