import type { IMatrixContext } from '../core/IMatrixContext';
import { RequestReply, type IRrRequestOptions } from './RequestReply';
import type { RuntimeAddressContext } from './RuntimeAddressContext';

export const ACTOR_CALL_CLIENT = Symbol.for('@open-matrix/core/ActorCallClient');

export type ActorCallOptions = Pick<IRrRequestOptions, 'timeoutMs' | 'signal' | 'protocolRequest'>;

export class ActorCallClient {
  constructor(
    private readonly context: IMatrixContext,
    private readonly address: RuntimeAddressContext,
  ) {}

  local<TReq, TRes>(
    mount: string,
    op: string,
    payload: TReq,
    opts: ActorCallOptions = {},
  ): Promise<TRes> {
    return RequestReply.execute<TReq, TRes>(
      this.context,
      mount,
      op,
      payload,
      opts,
    );
  }

  user<TReq, TRes>(
    mount: string,
    op: string,
    payload: TReq,
    opts: ActorCallOptions = {},
  ): Promise<TRes> {
    if (!this.address.userSpaceRoot) {
      throw new Error('MISSING_USER_SPACE_ROOT');
    }

    return RequestReply.execute<TReq, TRes>(
      this.context,
      mount,
      op,
      payload,
      {
        ...opts,
        targetRoot: this.address.userSpaceRoot,
      },
    );
  }

  account<TReq, TRes>(
    mount: string,
    op: string,
    payload: TReq,
    opts: ActorCallOptions = {},
  ): Promise<TRes> {
    if (!this.address.accountRoot) {
      throw new Error('MISSING_ACCOUNT_ROOT');
    }

    return RequestReply.execute<TReq, TRes>(
      this.context,
      mount,
      op,
      payload,
      {
        ...opts,
        targetRoot: this.address.accountRoot,
      },
    );
  }

  platform<TReq, TRes>(
    mount: string,
    op: string,
    payload: TReq,
    opts: ActorCallOptions = {},
  ): Promise<TRes> {
    return RequestReply.execute<TReq, TRes>(
      this.context,
      mount,
      op,
      payload,
      {
        ...opts,
        targetRoot: this.address.platformRoot,
      },
    );
  }

  space<TReq, TRes>(
    spaceRoot: string,
    mount: string,
    op: string,
    payload: TReq,
    opts: ActorCallOptions = {},
  ): Promise<TRes> {
    if (!spaceRoot) {
      throw new Error('MISSING_SPACE_ROOT');
    }

    return RequestReply.execute<TReq, TRes>(
      this.context,
      mount,
      op,
      payload,
      {
        ...opts,
        targetRoot: spaceRoot,
      },
    );
  }
}
