import { ISyncManagerCS } from '../types';
import { fromObjectSyncTaskFactory } from './syncTasks/fromObjectSyncTask';
import { objectAssign } from '../../utils/lang/objectAssign';
import { ISplitsParser } from './splitsParser/types';
import { IReadinessManager } from '../../readiness/types';
import { SDK_SEGMENTS_ARRIVED } from '../../readiness/constants';
import { ISdkFactoryContextSync } from '../../sdkFactory/types';

function flush() {
  return Promise.resolve();
}

/**
 * Offline SyncManager factory.
 * Can be used for server-side API, and client-side API with or without multiple clients.
 *
 * @param splitsParser - e.g., `splitsParserFromFile`, `splitsParserFromSettings`.
 */
export function syncManagerOfflineFactory(
  splitsParserFactory: () => ISplitsParser
): (params: ISdkFactoryContextSync) => ISyncManagerCS {

  /**
   * SyncManager factory for modular SDK
   */
  return function ({
    settings,
    readiness,
    storage,
  }: ISdkFactoryContextSync): ISyncManagerCS {

    const mainSyncManager = fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings);
    const mainStart = mainSyncManager.start;
    const sharedStarts: Array<() => void> = [];

    return objectAssign(
      mainSyncManager,
      {
        start() {
          mainStart();
          sharedStarts.forEach(cb => cb());
          sharedStarts.length = 0;
        },
        // fake flush, that resolves immediately
        flush,

        // [Only used for client-side]
        shared(matchingKey: string, readinessManager: IReadinessManager) {
          // In LOCALHOST mode, shared clients are ready in the next event-loop cycle than created
          // SDK_READY cannot be emitted directly because this will not update the readiness status
          function emitSdkReady() {
            readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // SDK_SPLITS_ARRIVED emitted by main SyncManager
          }

          if (mainSyncManager.isRunning()) setTimeout(emitSdkReady);
          else sharedStarts.push(emitSdkReady);

          return {
            stop() { },
            flush,
          };
        }
      }
    );
  };
}
