// src/lisp/primitives/matrix.ts
// Matrix Bridge Primitives: call, emit, spawn, introspect, subscribe
//
// These primitives connect LISP evaluation to the Matrix component fabric.
// Reference: Vision/15-LISP-ON-PROTOCOL.md

import type { SExpr } from '../SExpr';
import type { IPrimitiveDefinition } from './mccarthy';

/**
 * CALL - RPC to any Matrix component
 * (call "service.name" "method" params)
 *
 * @example
 * (call "flowpad.db" "Query" (obj "sql" "SELECT * FROM users"))
 */
export const call: IPrimitiveDefinition = {
  name: 'call',
  handler: async (args, evaluate) => {
    // Requires runtime context - interpreter must handle this
    throw new Error('call must be handled by interpreter with Matrix context');
  },
};

/**
 * EMIT - Publish an event via TopicRouter
 * (emit "service.name" "eventName" payload)
 */
export const emit: IPrimitiveDefinition = {
  name: 'emit',
  handler: async (args, evaluate) => {
    throw new Error('emit must be handled by interpreter with Matrix context');
  },
};

/**
 * SPAWN - Create a dynamic Matrix component
 * (spawn "my-agent" (obj "handler1" (lambda (p) ...) "handler2" (lambda (p) ...)))
 *
 * Returns: { mount, handlers, createdAt }
 */
export const spawn: IPrimitiveDefinition = {
  name: 'spawn',
  handler: async (args, evaluate) => {
    throw new Error('spawn must be handled by interpreter with Matrix context');
  },
};

/**
 * DESTROY - Remove a dynamic component
 * (destroy "flowpad.spawned.my-agent")
 */
export const destroy: IPrimitiveDefinition = {
  name: 'destroy',
  handler: async (args, evaluate) => {
    throw new Error('destroy must be handled by interpreter with Matrix context');
  },
};

/**
 * INTROSPECT - Get component's accepts/emits spec
 * (introspect "service.name")
 *
 * Returns: { accepts: {...}, emits: {...} }
 */
export const introspect: IPrimitiveDefinition = {
  name: 'introspect',
  handler: async (args, evaluate) => {
    throw new Error('introspect must be handled by interpreter with Matrix context');
  },
};

/**
 * LIST-SPAWNED - List all dynamically created components
 * (list-spawned)
 */
export const listSpawned: IPrimitiveDefinition = {
  name: 'list-spawned',
  handler: async (args, evaluate) => {
    throw new Error('list-spawned must be handled by interpreter with Matrix context');
  },
};

/**
 * SUBSCRIBE - Subscribe to a Matrix topic
 * (subscribe "sensor.temperature" (lambda (msg) ...))
 *
 * Returns: subscription ID
 */
export const subscribe: IPrimitiveDefinition = {
  name: 'subscribe',
  handler: async (args, evaluate) => {
    throw new Error('subscribe must be handled by interpreter with Matrix context');
  },
};

/**
 * UNSUBSCRIBE - Cancel a subscription
 * (unsubscribe subscription-id)
 */
export const unsubscribe: IPrimitiveDefinition = {
  name: 'unsubscribe',
  handler: async (args, evaluate) => {
    throw new Error('unsubscribe must be handled by interpreter with Matrix context');
  },
};

/**
 * GET - Property access for objects (returned from RPC)
 * (get user "name") → "Alice"
 */
export const get: IPrimitiveDefinition = {
  name: 'get',
  handler: async (args, evaluate) => {
    const obj = await evaluate(args[0]) as Record<string, unknown>;
    const key = await evaluate(args[1]) as string;
    return obj?.[key];
  },
};

/**
 * SET - Create modified object with new property
 * (set user "name" "Bob") → { ...user, name: "Bob" }
 */
export const set: IPrimitiveDefinition = {
  name: 'set',
  aliases: ['assoc-set'],
  handler: async (args, evaluate) => {
    const obj = await evaluate(args[0]) as Record<string, unknown>;
    const key = await evaluate(args[1]) as string;
    const val = await evaluate(args[2]);
    return { ...obj, [key]: val };
  },
};

/**
 * KEYS - Get object keys as list
 * (keys user) → ("name" "age" "email")
 */
export const keys: IPrimitiveDefinition = {
  name: 'keys',
  handler: async (args, evaluate) => {
    const obj = await evaluate(args[0]) as Record<string, unknown>;
    return obj && typeof obj === 'object' && !Array.isArray(obj) ? Object.keys(obj) : [];
  },
};

/**
 * VALUES - Get object values as list
 * (values user) → ("Alice" 30 "alice@example.com")
 */
export const values: IPrimitiveDefinition = {
  name: 'values',
  handler: async (args, evaluate) => {
    const obj = await evaluate(args[0]) as Record<string, unknown>;
    return obj && typeof obj === 'object' && !Array.isArray(obj) ? Object.values(obj) : [];
  },
};

/**
 * ENTRIES - Get object as list of [key, value] pairs
 * (entries user) → (("name" "Alice") ("age" 30))
 */
export const entries: IPrimitiveDefinition = {
  name: 'entries',
  aliases: ['pairs'],
  handler: async (args, evaluate) => {
    const obj = await evaluate(args[0]) as Record<string, unknown>;
    return obj && typeof obj === 'object' && !Array.isArray(obj) ? Object.entries(obj) : [];
  },
};

/**
 * OBJ - Construct object from key-value pairs
 * (obj "name" "Alice" "age" 30) → { name: "Alice", age: 30 }
 */
export const obj: IPrimitiveDefinition = {
  name: 'obj',
  aliases: ['hash-map', 'object'],
  handler: async (args, evaluate) => {
    const result: Record<string, unknown> = {};
    for (let i = 0; i < args.length; i += 2) {
      const key = await evaluate(args[i]) as string;
      const val = await evaluate(args[i + 1]);
      result[key] = val;
    }
    return result;
  },
};

/**
 * MERGE - Merge objects
 * (merge obj1 obj2 obj3) → combined object
 */
export const merge: IPrimitiveDefinition = {
  name: 'merge',
  handler: async (args, evaluate) => {
    const objs = await Promise.all(args.map(a => evaluate(a)));
    return Object.assign({}, ...objs as object[]);
  },
};

/**
 * OBJECT? - Is it an object (not array)?
 * (object? {}) → true
 */
export const objectp: IPrimitiveDefinition = {
  name: 'object?',
  aliases: ['map?'],
  handler: async (args, evaluate) => {
    const val = await evaluate(args[0]);
    return val !== null && typeof val === 'object' && !Array.isArray(val);
  },
};

/**
 * All Matrix bridge primitives
 */
export const matrixPrimitives: IPrimitiveDefinition[] = [
  call,
  emit,
  spawn,
  destroy,
  introspect,
  listSpawned,
  subscribe,
  unsubscribe,
  get,
  set,
  keys,
  values,
  entries,
  obj,
  merge,
  objectp,
];
