import { HttpModule, HttpService } from '@nestjs/axios';
import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { ENGINE_MODULE_OPTIONS, ENGINE_SERVICE } from './engine.constants';
import { EngineController } from './engine.controller';
import { IEngineOptions, IEngineService } from './engine.interfaces';
import { EngineResolver } from './engine.resolver';

@Global()
@Module({})
export class EngineModule {
  private static readonly logger = new Logger(EngineModule.name);

  static async forRootAsync(options: IEngineOptions): Promise<DynamicModule> {
    const optionsProvider = {
      provide: ENGINE_MODULE_OPTIONS,
      useValue: options,
    };

    const engineProvider = {
      provide: ENGINE_SERVICE,
      useFactory: async (httpService: HttpService, req: Request) => {
        return await this.createEngineConnection(options, httpService, req);
      },
      inject: [HttpService, REQUEST],
    };

    return {
      module: EngineModule,
      imports: [
        HttpModule,
        GraphQLModule.forRoot({
          autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
        }),
      ],
      providers: [optionsProvider, engineProvider, EngineResolver],
      controllers: [EngineController],
      exports: [optionsProvider, engineProvider],
    };
  }

  private static async createEngineConnection(
    options: IEngineOptions,
    httpService: HttpService,
    req: Request,
  ): Promise<IEngineService> {
    try {
      const service = await import(
        `./connectors/${options.type}/main.connector`
      );
      const engine = new service.default(options, httpService, req);

      return engine;
    } catch (e) {
      this.logger.error(
        `There is a problem with the connector '${options.type}'`,
      );
      this.logger.verbose(e);
      process.exit(); // We can't continue without an engine, shutdown the process...
    }
  }
}