import { IPlatformListener } from '@cloudbase/cals';
import { generateEmit } from './runtime';
import { IGenerateOptions, IGenerateContext, generateListnerInstances } from './adapter/generate';

export interface IEventFlowContext extends IGenerateContext {
  $w?: any;
  target?: any; // widget
  forContext?: any;
  scopeContext?: any;
  dataContext?: any;
}

export class EventFlow {
  id: string;
  description: string;
  #listenerInstances;
  #context: IEventFlowContext;

  constructor({
    schema,
    context,
    options,
  }: {
    schema: {
      id?: string;
      description?: string;
      listeners?: IPlatformListener[];
    };
    context: IEventFlowContext;
    options?: IGenerateOptions;
  }) {
    this.#context = context;
    const { id, description, listeners = [] } = schema || {};
    const startNode = listeners[0];
    const [_, eventFlowId] = startNode?.eventName.match(/(.*?)\.start/) || [];
    this.id = id || eventFlowId;
    this.description = description || startNode?.description || '';
    this.#listenerInstances = generateListnerInstances(
      { $app: this.#context.$app, $page: this.#context.$page },
      listeners || [],
      {
        ...options,
        key: `事件流 ${id}`,
        useScopeWidgetData: options.useScopeWidgetData === undefined ? true : options.useScopeWidgetData,
      },
    );

    /**
     * TODO: 缺乏 currentTarget
     */
    // const { $w } = context || {};
    // this._emit = generateEmit($w);
    return this;
  }

  trigger(additionalScope, options: Partial<IEventFlowContext> = {}) {
    const mergedContext = {
      ...this.#context,
      ...options,
    };
    const { $w, target, scopeContext, forContext, dataContext } = mergedContext;
    const emit = (trigger, eventData, originalEvent?) =>
      generateEmit($w, target)(
        trigger,
        this.#listenerInstances,
        eventData,
        forContext,
        originalEvent,
        scopeContext,
        dataContext,
        $w.page.id,
        true,
      );
    return emit(`${this.id}.start`, additionalScope);
  }

  toJSON() {
    const KEYS = ['id', 'description'];
    return KEYS.reduce((obj, key) => {
      obj[key] = this[key];
      return obj;
    }, {});
  }
}
