/*
 * Copyright (C) 2019 StApps
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
import {Logger, SMTP, Transport, VerifiableTransport} from '@openstapps/logger';

/**
 * Provides information if a transport is a verifiable transport
 * @param instance A transport that needs to be checked
 */
export function isTransportWithVerification(instance: Transport): instance is VerifiableTransport {
  return typeof (instance as VerifiableTransport).verify === 'function';
}

/**
 * Singleton for getting only one transport service
 *
 * In the future this may support more than loading SMTP as a transport.
 */
export class BackendTransport {
  /**
   * One (and only one) instance of the backend transport
   */
  private static _instance?: BackendTransport;

  /**
   * Stores information if transport is in state of waiting for the verification
   */
  private waitingForVerification = false;

  /**
   * A (SMTP) transport which includes settings for sending mails
   */
  protected transport: SMTP | undefined;

  /**
   * Destroys the singleton instance of the class (for unit test purposes)
   */
  public static destroy() {
    delete BackendTransport._instance;
  }

  /**
   * Provides instance of a backend transport
   */
  public static getInstance(): BackendTransport {
    if (BackendTransport._instance !== undefined) {
      return BackendTransport._instance;
    }

    BackendTransport._instance = new BackendTransport();

    return BackendTransport._instance;
  }

  /**
   * Provides an instance of a transport
   */
  public static getTransportInstance(): SMTP | undefined {
    return BackendTransport.getInstance().transport;
  }

  private constructor() {
    // get SMTP instance for the time
    // in the future we may implement some other transport services which can be selected
    // via the configuration files
    try {
      this.transport = SMTP.getInstance();
    } catch (error) {
      if (process.env.ALLOW_NO_TRANSPORT === 'true') {
        Logger.warn('SMTP error was ignored.');
      } else {
        throw error;
      }
    }

    if (this.transport !== undefined && isTransportWithVerification(this.transport)) {
      void this.verifyTransport(this.transport);
    }
  }

  /**
   * Verifies the transport using its verification method
   */
  private async verifyTransport(transport: VerifiableTransport): Promise<void> {
    this.waitingForVerification = true;
    try {
      const successful = await transport.verify();
      if (successful) {
        Logger.log('SMTP verification successful.');
      }
    } catch (error) {
      throw error;
    } finally {
      this.waitingForVerification = false;
    }
  }

  /**
   * Provides information if transport is in state of waiting for the verification
   */
  public isWaitingForVerification(): boolean {
    return this.waitingForVerification;
  }
}
