import type { ActorCallClient } from '../remoting/ActorCallClient';
import type { IActorService, ServiceContract } from './ServiceContract';

export type IntentScope =
  | 'local'
  | 'account'
  | 'user'
  | 'platform'
  | { readonly space: string }
  | { readonly grant: string };

export interface WellKnownServiceBinding {
  readonly contractId: string;
  readonly defaultScope: IntentScope;
  readonly mount: string;
}

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

export class ScopedIntentResolver {
  constructor(
    private readonly call: ActorCallClient,
    private readonly known: readonly WellKnownServiceBinding[],
  ) {}

  async resolve(contract: ServiceContract, scope?: IntentScope): Promise<IActorService> {
    const binding = this.known.find((entry) => entry.contractId === contract.id);

    if (!binding) {
      throw new Error(`NO_KNOWN_SERVICE_BINDING: ${contract.id}`);
    }

    return new ScopedActorServiceProxy(
      this.call,
      contract,
      binding.mount,
      scope ?? binding.defaultScope,
    );
  }

  invokeUserMount<TResponse>(
    mount: string,
    op: string,
    payload: Record<string, unknown> = {},
  ): Promise<TResponse> {
    return this.call.user<Record<string, unknown>, TResponse>(mount, op, payload);
  }
}

class ScopedActorServiceProxy implements IActorService {
  constructor(
    private readonly call: ActorCallClient,
    readonly contract: ServiceContract,
    readonly mount: string,
    private readonly scope: IntentScope,
  ) {}

  supports(op: string): boolean {
    return op in this.contract.ops;
  }

  async invoke(op: string, payload?: Record<string, unknown>): Promise<unknown> {
    if (!this.supports(op)) {
      return {
        ok: false,
        error: `${this.contract.id}: operation '${op}' not in contract`,
      };
    }

    const request = payload ?? {};

    if (this.scope === 'local') {
      return this.call.local(this.mount, op, request);
    }

    if (this.scope === 'user') {
      return this.call.user(this.mount, op, request);
    }

    if (this.scope === 'account') {
      return this.call.account(this.mount, op, request);
    }

    if (this.scope === 'platform') {
      return this.call.platform(this.mount, op, request);
    }

    if ('space' in this.scope) {
      return this.call.space(this.scope.space, this.mount, op, request);
    }

    throw new Error(`GRANT_SCOPE_NOT_IMPLEMENTED: ${this.scope.grant}`);
  }
}
