import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
import { AppwriteModuleOptions } from './interface/appwrite-module-options.interface';
import { Client } from 'node-appwrite';
import { CLIENT_PROVIDER_NAME, CONFIG_PROVIDER_NAME } from './appwrite.constants';
import { SecretStoreService } from './service/secret-store.service';
import { EnvVarsStoreService } from './service/env-vars-store.service';
import { ParameterStoreService } from './service/parameter-store.service';
import { AccessTokenGuard } from './guard/access-token.guard';
import { Config } from './config/config.interface';

@Global()
@Module({})
export class AppwriteCoreModule {

  private static readonly logger = new Logger(AppwriteCoreModule.name);

  static forRoot(options: AppwriteModuleOptions): DynamicModule {
    const storeServiceProvider = {
      provide: SecretStoreService,
      useClass: options.useEnvVarsForSecrets ? EnvVarsStoreService : ParameterStoreService,
    };

    const configProvider = {
      provide: CONFIG_PROVIDER_NAME,
      useValue: {
        APPWRITE_ENDPOINT: options.endpoint,
        APPWRITE_PROJECT_ID: options.projectId,
        APPWRITE_DATABASE_ID: options.databaseId,
        AWS_REGION: options.awsRegion,
        DELETE_REMOVED_ATTRIBUTES: options.deleteRemovedAttributes,
      }
    };

    const appwriteApiKeyName = options.appwriteApiKeyName ?? 'appwrite_api_key';

    const connectionProvider = {
      provide: CLIENT_PROVIDER_NAME,
      useFactory: async (storeService: SecretStoreService): Promise<Client> => {
        const apiKey = await storeService.getSecretString(appwriteApiKeyName);
        if (!apiKey) {
          this.logger.error(`Unable to find Appwrite API key for name ${appwriteApiKeyName}`);
        }
        if(options.showEnvironmentVars) {
          this.logger.log(`Connecting to Appwrite with endpoint ${options.endpoint} and project ${options.projectId}`);
          this.logger.log(`With API key ${apiKey}`);
          this.logger.log(`With database ${options.databaseId}`);
          this.logger.log(`Using store service ${storeService.constructor.name}`)
        }
        return new Client()
          .setEndpoint(options.endpoint)
          .setProject(options.projectId)
          .setKey(apiKey ?? '');
      },
      inject: [SecretStoreService],
    };

    const accessTokenGuardProvider = {
      provide: AccessTokenGuard,
      useFactory: (storeService: SecretStoreService, config: Config): AccessTokenGuard => {
        return new AccessTokenGuard(storeService, config);
      },
      inject: [SecretStoreService, CONFIG_PROVIDER_NAME]
    };

    return {
      module: AppwriteCoreModule,
      providers: [
        storeServiceProvider,
        configProvider,
        connectionProvider,
        accessTokenGuardProvider,
      ],
      exports: [
        storeServiceProvider,
        configProvider,
        connectionProvider,
        accessTokenGuardProvider,
      ],
    };
  }
}
