// src/flow/FlowEngine.ts

import type { IMatrixContext } from '../engine/core/IMatrixContext';
import { RequestReply } from '../engine/remoting/RequestReply';
import type { IFlowPlan } from './FlowPlan';
import type {
  AnyStage,
  ILocalMapStage,
  ILocalFilterStage,
  IRemoteRRStage,
  IRemoteSinkStage,
} from './FlowTypes';

/**
 * Interprets a IFlowPlan against a context.
 *
 * All remote stages use RequestReply over the scheme-appropriate transport.
 */
export class FlowEngine {
  constructor(private readonly context: IMatrixContext) {}

  async run<TIn = unknown, TOut = unknown>(
    plan: IFlowPlan<TIn, TOut>,
    input: TIn
  ): Promise<TOut> {
    let value: unknown = input;

    for (const stage of plan.stages) {
      value = await this.executeStage(stage, value);
    }

    return value as TOut;
  }

  private async executeStage(stage: AnyStage, value: unknown): Promise<unknown> {
    switch (stage.kind) {
      case 'local-map': {
        const s = stage as ILocalMapStage;
        return s.fn(value);
      }

      case 'local-filter': {
        const s = stage as ILocalFilterStage;
        const keep = await s.fn(value);
        if (!keep) {
          throw new Error('Flow value rejected by filter');
        }
        return value;
      }

      case 'remote-rr': {
        const s = stage as IRemoteRRStage;
        return RequestReply.execute<unknown, unknown>(
          this.context,
          s.mount,
          s.op,
          value,
          { timeoutMs: s.timeoutMs }
        );
      }

      case 'remote-sink': {
        const s = stage as IRemoteSinkStage;
        await RequestReply.execute<unknown, unknown>(
          this.context,
          s.mount,
          s.op,
          value,
          { timeoutMs: s.timeoutMs }
        );
        return value;
      }

      default:
        return value;
    }
  }
}
