// src/lisp/vlm/VLMAtomizer.ts
// VLM Atomizer - Splits expressions into parallel children
//
// Detects parallelizable expressions and spawns child messages to different
// bootstrap nodes for parallel evaluation.
//
// Reference: Vision/18-VIRTUAL-LISP-MACHINE.md

import { MatrixActor } from '../../core/MatrixActor';
import { RequestReply } from '../../engine/remoting/RequestReply';
import type { SExpr } from '../SExpr';
import type { IAtomizedMessage } from './IAtomizedMessage';
import { canAtomize, allArgsAreAtoms } from './AtomizationUtils';
import { getVLMNamespace } from './VLMRouter';

// Debug flag - set to true for verbose logging (EXPENSIVE - uses JSON.stringify)
const VLM_DEBUG = false;

// Helper to make RPC calls
async function rpc<TReq, TRes>(
  ctx: unknown,
  targetMount: string,
  op: string,
  params: TReq
): Promise<TRes> {
  return RequestReply.execute<TReq, TRes>(
    ctx as Parameters<typeof RequestReply.execute>[0],
    targetMount,
    op,
    params,
    { timeoutMs: 30000 }
  );
}

/**
 * VLMAtomizer - Detects parallelizable expressions, spawns child messages
 *
 * For expressions like (+ a b c), the atomizer:
 * 1. Creates a join point expecting 3 results
 * 2. Dispatches a, b, c to different bootstrap nodes
 * 3. Each child carries the traveling interpreter
 *
 * Mount: {namespace}.vlm-atomizer
 */
export class VLMAtomizer extends MatrixActor {
  static accepts = {
    atomize: {
      expr: 'any',
      eval: 'any',
      continuation: 'string',
      trace: 'object?',
    },
  };

  static emits = {
    atomized: { joinId: 'string', op: 'string', childCount: 'number', nodeTargets: 'array' },
    notAtomizable: { expr: 'any', reason: 'string' },
    childDispatched: { joinId: 'string', slot: 'number', nodeMount: 'string', expr: 'any' },
  };

  protected _ns!: ReturnType<typeof getVLMNamespace>;

  protected async onBootstrap(): Promise<void> {
    this._ns = getVLMNamespace(this._context!.mount);
    // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
    console.log('[VLMAtomizer] Initialized at', this._context!.mount);
  }

  async onAtomize(params: IAtomizedMessage): Promise<void> {
    const { expr, eval: travelingEval, continuation, trace } = params;

    // Check if atomizable
    if (!canAtomize(expr)) {
      // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
      if (VLM_DEBUG) console.log(`[VLMAtomizer] Not atomizable: ${JSON.stringify(expr)}`);
      this.emit('notAtomizable', { expr, reason: 'not parallelizable' });

      // Send directly to a single node
      const nodeResult = await rpc<object, { mount: string } | null>(
        this._context!,
        this._ns.router,
        'getNode',
        {}
      );
      if (nodeResult) {
        await rpc(this._context!, nodeResult.mount, 'evalAtomized', params);
      } else {
        // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
        console.error('[VLMAtomizer] No nodes available!');
      }
      return;
    }

    const [op, ...args] = expr as SExpr[];
    const joinId = `join-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
    if (VLM_DEBUG) console.log(`[VLMAtomizer] Atomizing: (${op} ...) into ${args.length} children, joinId=${joinId}`);

    // Get N nodes for parallel execution
    const { mounts } = await rpc<{ count: number }, { mounts: string[] }>(
      this._context!,
      this._ns.router,
      'getNodes',
      { count: args.length }
    );

    if (mounts.length === 0) {
      // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
      console.error('[VLMAtomizer] No nodes available for atomization!');
      return;
    }

    // Create join point
    await rpc(this._context!, this._ns.join, 'createJoin', {
      id: joinId,
      totalSlots: args.length,
      parentOp: op,
      parentContinuation: continuation,
      travelingEval,
      trace,
    });

    this.emit('atomized', {
      joinId,
      op: op as string,
      childCount: args.length,
      nodeTargets: mounts,
    });

    // Spawn child messages
    for (let i = 0; i < args.length; i++) {
      const childExpr = args[i];
      const targetMount = mounts[i % mounts.length];

      const childMessage: IAtomizedMessage = {
        expr: childExpr,
        eval: travelingEval,  // INTERPRETER REPLICATES!
        continuation: `join:${joinId}:${i}`,
        trace: {
          id: `${trace?.id || 'root'}-child-${i}`,
          parentId: trace?.id,
          depth: (trace?.depth || 0) + 1,
          createdAt: Date.now(),
        },
        join: {
          id: joinId,
          slot: i,
          totalSlots: args.length,
          parentOp: op as string,
          parentContinuation: continuation,
        },
      };

      this.emit('childDispatched', {
        joinId,
        slot: i,
        nodeMount: targetMount,
        expr: childExpr,
      });

      // Log the traveling interpreter proof (EXPENSIVE - only in debug mode)
      if (VLM_DEBUG) {
        const evalSize = JSON.stringify(travelingEval).length;
        // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
        console.log(`[VLM DISPATCH] Child ${i}: Target=${targetMount}, Expr=${JSON.stringify(childExpr)}, Interpreter=${evalSize} chars`);
      }

      // Check if child needs further atomization
      if (canAtomize(childExpr) && !allArgsAreAtoms(childExpr)) {
        // Recursive atomization
        // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
        if (VLM_DEBUG) console.log(`  Needs further atomization - sending to atomizer`);
        await rpc(this._context!, this._ns.atomizer, 'atomize', childMessage);
      } else {
        // Leaf computation - send to bootstrap node
        // eslint-disable-next-line no-console -- standalone lisp runtime, no actor context
        if (VLM_DEBUG) console.log(`  Leaf node - dispatching to ${targetMount}`);
        await rpc(this._context!, targetMount, 'evalAtomized', childMessage);
      }
    }
  }
}
