import {
  DynamicModule,
  Module,
  ModuleMetadata,
  Provider,
  Type,
} from '@nestjs/common';
import { PaymeService } from './payme.service';
import { APP_FILTER } from '@nestjs/core';
import { PaymeExceptionFilter } from '../errors';
import { MongooseModule } from '@nestjs/mongoose';
import {
  PaymeTransaction,
  PaymeTransactionSchema,
} from './schemas/payme-transaction.schema';
import { IPaymeStrategy } from './IPaymeStrategy';
import { PaymeController } from './payme.controller';

export interface IAccount {
  [key: string]: string;
}

export interface PaymeGuardOptions {
  username: string;
  password: string;
}

export interface PaymeModuleAsyncOptions
  extends Pick<ModuleMetadata, 'imports'> {
  inject?: any[];
  paymeStrategy: Type<IPaymeStrategy>;
  useFactory: (
    ...args: any[]
  ) => Promise<PaymeGuardOptions> | PaymeGuardOptions;
  isGlobal?: boolean;
}

export interface PaymeModuleOptions extends Pick<ModuleMetadata, 'imports'> {
  paymeStrategy: Type<IPaymeStrategy>;
  paymeGuard: PaymeGuardOptions;
  isGlobal?: boolean;
}

@Module({
  imports: [
    MongooseModule.forFeature([
      {
        name: PaymeTransaction.name,
        schema: PaymeTransactionSchema,
      },
    ]),
  ],
})
export class PaymeModule {
  static forRoot(options: PaymeModuleOptions): DynamicModule {
    const providers: Provider[] = [
      {
        provide: 'PAYME_STRATEGY',
        useClass: options.paymeStrategy,
      },
      {
        provide: 'PAYME_GUARD_OPTIONS',
        useValue: options.paymeGuard,
      },
      {
        provide: APP_FILTER,
        useClass: PaymeExceptionFilter,
      },
      PaymeService,
    ];

    return {
      global: !!options.isGlobal,
      module: PaymeModule,
      imports: options.imports,
      providers: providers,
      controllers: [PaymeController],
    };
  }

  static forRootAsync(options: PaymeModuleAsyncOptions): DynamicModule {
    const providers: Provider[] = [
      {
        provide: 'PAYME_STRATEGY',
        useClass: options.paymeStrategy,
      },
      {
        provide: 'PAYME_GUARD_OPTIONS',
        useFactory: options.useFactory,
        inject: options.inject || [],
      },
      {
        provide: APP_FILTER,
        useClass: PaymeExceptionFilter,
      },
      PaymeService,
    ];

    return {
      global: !!options.isGlobal,
      module: PaymeModule,
      imports: options.imports,
      exports: [PaymeService],
      providers: providers,
      controllers: [PaymeController],
    };
  }
}
