import * as plugins from './plugins.js';
import * as paths from './paths.js';
import { Cloudly } from './classes.cloudly.js';
import { logger } from './logger.js';

/**
 * handles incoming requests from CI to deploy new versions of apps
 */
export class CloudlyServer {
  /**
   * a reference to the cloudly instance
   */
  public cloudlyRef: Cloudly;
  public additionalHandlers: plugins.typedserver.servertools.Handler[] = [];

  /**
   * the smartexpress server handling the actual requests
   */
  public typedServer: plugins.typedserver.TypedServer;
  public typedsocketServer: plugins.typedsocket.TypedSocket;

  /**
   * typedrouter
   * @param cloudlyArg
   */
  public typedrouter = new plugins.typedrequest.TypedRouter();

  constructor(cloudlyArg: Cloudly) {
    this.cloudlyRef = cloudlyArg;
    this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
  }

  // =========
  // LIFECYCLE
  // =========

  /**
   * init the reception instance
   */
  public async start() {
    logger.log('info', `cloudly domain is ${this.cloudlyRef.config.data.publicUrl}`);
    let sslCert: plugins.smartacme.Cert;

    if (this.cloudlyRef.config.data.sslMode === 'letsencrypt') {
      logger.log('info', `Using letsencrypt for ssl mode. Trying to obtain a certificate...`);
      logger.log('info', `This might take 10 minutes...`);
      sslCert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
        this.cloudlyRef.config.data.publicUrl,
      );
      logger.log(
        'success',
        `Successfully obtained certificate for cloudly domain ${this.cloudlyRef.config.data.publicUrl}`,
      );
    } else if (this.cloudlyRef.config.data.sslMode === 'external') {
      logger.log(
        'info',
        `Using external certificate for ssl mode, meaning cloudly is not in charge of ssl termination.`,
      );
    }

    interface IRequestGuardData {
      req: plugins.typedserver.Request;
      res: plugins.typedserver.Response;
    }

    // guards
    const guardIp = new plugins.smartguard.Guard<IRequestGuardData>(async (dataArg) => {
      if (true) {
        return true;
      } else {
        dataArg.res.status(500);
        dataArg.res.send(`Not allowed to perform this operation!`);
        dataArg.res.end();
        return false;
      }
    });

    // server
    this.typedServer = new plugins.typedserver.TypedServer({
      cors: true,
      forceSsl: false,
      port: this.cloudlyRef.config.data.publicPort,
      ...(sslCert
        ? {
            privateKey: sslCert.privateKey,
            publicKey: sslCert.publicKey,
          }
        : {}),
      injectReload: true,
      serveDir: paths.distServeDir,
      watch: true,
      enableCompression: true,
      preferredCompressionMethod: 'gzip',
    });
    this.typedServer.typedrouter.addTypedRouter(this.typedrouter);
    this.typedServer.server.addRoute(
      '/curlfresh/:scriptname',
      this.cloudlyRef.serverManager.curlfreshInstance.handler,
    );
    await this.typedServer.start();
  }

  /**
   * stop the reception instance
   */
  public async stop() {
    await this.typedServer.stop();
  }
}