/**
 * Shared test helpers for membrane tests.
 */
import { MatrixActor } from '../../MatrixActor.js';
import { MatrixContext } from '../../../engine/core/MatrixContext.js';
import { InMemoryBroker } from '../../../transport/InMemoryBroker.js';
import { InMemoryTransport } from '../../../transport/InMemoryTransport.js';
import type { IMatrixContext } from '../../../engine/core/IMatrixContext.js';
import type { IMatrixRuntime } from '../../../engine/core/IMatrixRuntime.js';

export const tick = (ms = 50) => new Promise(r => setTimeout(r, ms));

export interface ReplyEnvelope {
  ok?: boolean;
  result?: unknown;
  payload?: unknown;
  params?: unknown;
  error?: string | { message?: string };
}

export type TestTransport = IMatrixContext['transport'];
export type SkippableTestContext = { skip: () => void };

export function asReplyEnvelope(reply: unknown): ReplyEnvelope | null {
  if (typeof reply !== 'object' || reply === null) {
    return null;
  }
  return reply as ReplyEnvelope;
}

export function unwrapReply(reply: unknown): unknown {
  const envelope = asReplyEnvelope(reply);
  return envelope?.result ?? envelope?.params ?? envelope?.payload ?? reply;
}

export function replyErrorMessage(reply: unknown, fallback = 'Unknown error'): string {
  const envelope = asReplyEnvelope(reply);
  const error = envelope?.error;
  if (typeof error === 'string') {
    return error;
  }
  if (typeof error === 'object' && error !== null && typeof error.message === 'string') {
    return error.message;
  }
  return fallback;
}

/** Minimal IMatrixRuntime stub */
export function mockRuntime(): IMatrixRuntime {
  return {
    type: 'headless',
    supportsDom: false,
    createChild: async () => ({ mount: 'mock', created: true, reused: false }),
    createElement: async () => null as unknown,
    mount: async () => {},
    unmount: async () => {},
    remove: async () => {},
    createMailbox: () => null as unknown,
    shutdown: async () => {},
  } as unknown as IMatrixRuntime;
}

/** Build a root context backed by InMemoryTransport */
export function buildContext(broker: InMemoryBroker): IMatrixContext {
  const transport = new InMemoryTransport(broker, { name: 'test' });
  return new MatrixContext('', transport, undefined, mockRuntime());
}

/** Test actor that tracks all handler calls */
export class TestActor extends MatrixActor {
  static accepts = {
    'doWork': { data: 'string' },
    'customOp': {},
  };

  calls: Array<{ op: string; payload: unknown }> = [];
  lastPayload: unknown = undefined;

  onDoWork(payload: unknown) {
    this.calls.push({ op: 'doWork', payload });
    this.lastPayload = payload;
    return { ok: true, echo: payload };
  }

  onCustomOp(payload: unknown) {
    this.calls.push({ op: 'customOp', payload });
    this.lastPayload = payload;
    return { ok: true };
  }
}
