// src/lisp/primitives/stream.ts
// Reactive Stream Primitives: stream-of, map, filter, scan, debounce, etc.
//
// Streams are lazy, composable data pipelines that don't execute until subscribed.
// Reference: Vision/17-REACTIVE-STREAMS.md

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

/**
 * STREAM-OF - Create a stream from a topic
 * (stream-of "sensor.temperature")
 */
export const streamOf: IPrimitiveDefinition = {
  name: 'stream-of',
  aliases: ['from-topic'],
  handler: async (args, evaluate) => {
    const source = await evaluate(args[0]) as string;
    return {
      __type: 'stream',
      source,
      operators: [],
    } as IStream;
  },
};

/**
 * STREAM-MAP - Add map operator to stream
 * (stream-map (lambda (x) (* x 2)) stream)
 */
export const streamMap: IPrimitiveDefinition = {
  name: 'stream-map',
  handler: async (args, evaluate) => {
    const fn = await evaluate(args[0]) as ILambdaClosure;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'map', fn }],
    } as IStream;
  },
};

/**
 * STREAM-FILTER - Add filter operator to stream
 * (stream-filter (lambda (x) (> x 0)) stream)
 */
export const streamFilter: IPrimitiveDefinition = {
  name: 'stream-filter',
  handler: async (args, evaluate) => {
    const fn = await evaluate(args[0]) as ILambdaClosure;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'filter', fn }],
    } as IStream;
  },
};

/**
 * STREAM-SCAN - Add scan/accumulator operator
 * (stream-scan + 0 stream)
 */
export const streamScan: IPrimitiveDefinition = {
  name: 'stream-scan',
  handler: async (args, evaluate) => {
    const fn = await evaluate(args[0]) as ILambdaClosure;
    const initial = await evaluate(args[1]);
    const stream = await evaluate(args[2]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'scan', fn, initial }],
    } as IStream;
  },
};

/**
 * STREAM-DEBOUNCE - Add debounce operator
 * (stream-debounce 300 stream) ; wait 300ms of silence
 */
export const streamDebounce: IPrimitiveDefinition = {
  name: 'stream-debounce',
  handler: async (args, evaluate) => {
    const ms = await evaluate(args[0]) as number;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'debounce', ms }],
    } as IStream;
  },
};

/**
 * STREAM-THROTTLE - Add throttle operator
 * (stream-throttle 100 stream) ; max one per 100ms
 */
export const streamThrottle: IPrimitiveDefinition = {
  name: 'stream-throttle',
  handler: async (args, evaluate) => {
    const ms = await evaluate(args[0]) as number;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'throttle', ms }],
    } as IStream;
  },
};

/**
 * STREAM-TAKE - Take only first n values
 * (stream-take 10 stream)
 */
export const streamTake: IPrimitiveDefinition = {
  name: 'stream-take',
  handler: async (args, evaluate) => {
    const count = await evaluate(args[0]) as number;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'take', count }],
    } as IStream;
  },
};

/**
 * STREAM-DISTINCT - Only emit when value changes
 * (stream-distinct stream)
 */
export const streamDistinct: IPrimitiveDefinition = {
  name: 'stream-distinct',
  handler: async (args, evaluate) => {
    const stream = await evaluate(args[0]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'distinct' }],
    } as IStream;
  },
};

/**
 * STREAM-TAP - Side effect without changing value
 * (stream-tap (lambda (x) (print x)) stream)
 */
export const streamTap: IPrimitiveDefinition = {
  name: 'stream-tap',
  handler: async (args, evaluate) => {
    const fn = await evaluate(args[0]) as ILambdaClosure;
    const stream = await evaluate(args[1]) as IStream;
    return {
      ...stream,
      operators: [...stream.operators, { type: 'tap', fn }],
    } as IStream;
  },
};

/**
 * STREAM-MERGE - Merge multiple streams
 * (stream-merge stream1 stream2 stream3)
 */
export const streamMerge: IPrimitiveDefinition = {
  name: 'stream-merge',
  handler: async (args, evaluate) => {
    const streams = await Promise.all(args.map(a => evaluate(a))) as IStream[];
    return {
      __type: 'stream',
      source: streams,
      operators: [],
    } as IStream;
  },
};

/**
 * STREAM-TO - Pipe stream output to a topic
 * (stream-to "output.topic" stream)
 *
 * Note: This activates the stream and returns a subscription ID
 */
export const streamTo: IPrimitiveDefinition = {
  name: 'stream-to',
  aliases: ['subscribe'],
  handler: async (args, evaluate) => {
    // This needs runtime support to actually activate
    throw new Error('stream-to must be handled by interpreter with transport support');
  },
};

/**
 * STREAM? - Is it a stream?
 * (stream? x) → true/false
 */
export const streamp: IPrimitiveDefinition = {
  name: 'stream?',
  handler: async (args, evaluate) => {
    const val = await evaluate(args[0]) as IStream;
    return val && typeof val === 'object' && val.__type === 'stream';
  },
};

/**
 * All stream primitives
 */
export const streamPrimitives: IPrimitiveDefinition[] = [
  streamOf,
  streamMap,
  streamFilter,
  streamScan,
  streamDebounce,
  streamThrottle,
  streamTake,
  streamDistinct,
  streamTap,
  streamMerge,
  streamTo,
  streamp,
];
