// src/engine/remoting/TransportMiddleware.ts

import type {
  ITransportAdapter,
  TransportHandler,
} from './ITransportAdapter';

export interface IMiddlewareResult {
  cancel?: boolean;
  topic?: string;
  payload?: unknown;
}

export interface ITransportMiddleware {
  beforePublish?(
    topic: string,
    payload: unknown
  ): void | IMiddlewareResult | Promise<void | IMiddlewareResult>;

  afterPublish?(
    topic: string,
    payload: unknown
  ): void | Promise<void>;
}

/**
 * Wrap an ITransportAdapter with a publish‑side middleware pipeline.
 * subscribe(...) is passed through as‑is.
 */
export function wrapTransport(
  inner: ITransportAdapter,
  middlewares: ITransportMiddleware[]
): ITransportAdapter {
  if (!middlewares.length) return inner;

  return {
    get root(): string {
      return inner.root;
    },

    get realm(): string {
      return inner.root;
    },

    publish(topic: string, payload?: unknown): void {
      const chain = [...middlewares];

      const run = async (idx: number, t: string, p: unknown): Promise<void> => {
        if (idx >= chain.length) {
          inner.publish(t, p);
          return;
        }

        const mw = chain[idx];

        if (mw.beforePublish) {
          const r = await mw.beforePublish(t, p);
          if (r && typeof r === 'object') {
            if (r.cancel) return;
            if (r.topic !== undefined) t = r.topic;
            if (r.payload !== undefined) p = r.payload;
          }
        }

        await run(idx + 1, t, p);

        if (mw.afterPublish) {
          try { await mw.afterPublish(t, p); } catch (err) {
            // eslint-disable-next-line no-console -- kernel transport middleware has no actor context for afterPublish failures
            console.error('[TransportMiddleware] afterPublish error', err);
          }
        }
      };

      void run(0, topic, payload);
    },

    subscribe(topic: string, handler: TransportHandler): () => void {
      return inner.subscribe(topic, handler);
    },

    async flush(): Promise<void> {
      await inner.flush?.();
    },

    getUnderlyingClient(): unknown {
      return inner.getUnderlyingClient();
    },

    async disconnect(): Promise<void> {
      await inner.disconnect();
    },
  };
}
