// @TODO eventually migrate to Node.js SDK package.
import { ISignalListener } from './types';
import { thenable } from '../utils/promise/thenable';
import { MaybeThenable } from '../dtos/types';
import { ISettings } from '../types';
import { LOG_PREFIX_CLEANUP, CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
import { ISdkFactoryContext } from '../sdkFactory/types';

const SIGTERM = 'SIGTERM';
const EVENT_NAME = 'for SIGTERM signal.';

/**
 * We'll listen for SIGTERM since it's the standard signal for server shutdown.
 *
 * If you're stopping the execution yourself via the keyboard, or by calling process.exit,
 * you should call the cleanup logic yourself, since we cannot ensure the data is sent after
 * the process is already exiting.
 */
export class NodeSignalListener implements ISignalListener {

  private handler: () => MaybeThenable<any>;
  private settings: ISettings;

  constructor({ syncManager, settings }: ISdkFactoryContext) {
    // @TODO review handler logic when implementing Node.js SDK
    this.handler = function () {
      if (syncManager) {
        // syncManager.stop();
        return syncManager.flush();
      }
    };
    this.settings = settings;
    this._sigtermHandler = this._sigtermHandler.bind(this);
  }

  start() {
    this.settings.log.debug(CLEANUP_REGISTERING, [EVENT_NAME]);
    // eslint-disable-next-line no-undef
    process.on(SIGTERM, this._sigtermHandler);
  }

  stop() {
    this.settings.log.debug(CLEANUP_DEREGISTERING, [EVENT_NAME]);
    // eslint-disable-next-line no-undef
    process.removeListener(SIGTERM, this._sigtermHandler);
  }

  /**
   * Call the handler, clean up listeners and emit the signal again.
   */
  private _sigtermHandler(): MaybeThenable<void> {
    const wrapUp = () => {
      // Cleaned up, remove handlers.
      this.stop();

      // This handler prevented the default behavior, start again.
      // eslint-disable-next-line no-undef
      process.kill(process.pid, SIGTERM);
    };

    this.settings.log.debug(`${LOG_PREFIX_CLEANUP}SDK graceful shutdown after SIGTERM.`);

    let handlerResult = null;

    try {
      handlerResult = this.handler();
    } catch (err) {
      this.settings.log.error(`${LOG_PREFIX_CLEANUP}Error with SDK graceful shutdown: ${err}`);
    }

    if (thenable(handlerResult)) {
      // Always exit, even with errors. The promise is returned for UT purposes.
      return handlerResult.then(wrapUp).catch(wrapUp);
    } else {
      wrapUp();
    }
  }
}
