import { EventEmitterModule } from '@app/event-emitter';
import { RedmineIssuesCacheWriterService } from '@app/event-emitter/issue-cache-writer/redmine-issues-cache-writer.service';
import { EnhancerService } from '@app/event-emitter/issue-enhancers/enhancer.service';
import { TimestampEnhancer } from '@app/event-emitter/issue-enhancers/timestamps-enhancer';
import { MainController } from '@app/event-emitter/main/main.controller';
import {
  CacheModule,
  Inject,
  Logger,
  Module,
  OnModuleInit,
} from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { switchMap, tap } from 'rxjs';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import configuration from './configs/app';
import { Changes } from './couchdb-datasources/changes';
import { CurrentUserEnhancer } from './issue-enhancers/current-user-enhancer';
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
import { PersonalNotificationsService } from './notifications/personal-notifications.service';
import { StatusChangeNotificationsService } from './notifications/status-change-notifications.service';
import { ChangesCacheWriterService } from './changes-cache-writer/changes-cache-writer.service';
import { Issues } from '@app/event-emitter/couchdb-datasources/issues';
import { Users } from '@app/event-emitter/couchdb-datasources/users';
import { TelegramBotService } from './telegram-bot/telegram-bot.service';
import { UserMetaInfoService } from './user-meta-info/user-meta-info.service';
import { UserMetaInfo } from './couchdb-datasources/user-meta-info';
import { PersonalNotificationAdapterService } from './notifications/adapters/personal-notification.adapter/personal-notification.adapter.service';
import { StatusChangeAdapterService } from './notifications/adapters/status-change.adapter.service';
import { CurrentIssuesEccmReportService } from './reports/current-issues-eccm.report.service';
import { CurrentIssuesEccmBotHandlerService } from './telegram-bot/handlers/current-issues-eccm.bot-handler.service';
import { CurrentIssuesEccmReportController } from './reports/current-issues-eccm.report.controller';
import { DailyEccmReportController } from './reports/daily-eccm.report.controller';
import { DailyEccmReportService } from './reports/daily-eccm.report.service';
import { ChangesService } from './changes/changes.service';
import { DailyEccmReportsDatasource } from './couchdb-datasources/daily-eccm-reports.datasource';
import { ScheduleModule } from '@nestjs/schedule';
import { DailyEccmReportTask } from './reports/daily-eccm.report.task';
import { DailyEccmReportsUserCommentsDatasource } from './couchdb-datasources/daily-eccm-reports-user-comments.datasource';
import { DailyEccmUserCommentsService } from './reports/daily-eccm-user-comments.service';
import { SetDailyEccmUserCommentBotHandlerService } from './telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service';
import { DailyEccmWithExtraDataService } from './reports/daily-eccm-with-extra-data.service';
import { SimpleKanbanBoardController } from './dashboards/simple-kanban-board.controller';
import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-enhancer';
import { IssuesByTagsWidgetDataLoaderService } from './dashboards/widget-data-loader/issues-by-tags.widget-data-loader.service';
import { CategoryMergeToTagsEnhancer } from './issue-enhancers/category-merge-to-tags-enhancer';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { SimpleIssuesListController } from './dashboards/simple-issues-list.controller';
import { TagsManagerController } from './tags-manager/tags-manager.controller';
import { CreateTagManagerServiceProvider } from './tags-manager/tags-manager.service';
import { CalendarEnhancer } from '@app/event-emitter/issue-enhancers/calendar-enhancer';
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
import { DashboardInitService } from './dashboards/dashboard-init.service';
import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
import { DailyEccmV2ReportTaskRunnerService } from './reports/daily-eccm-v2-report-task-runner.service';
import { DashboardsService } from '@app/event-emitter/dashboards/dashboards.service';
import { DailyEccmReportsV2Datasource } from './couchdb-datasources/daily-eccm-reports-v2.datasource';
import { DailyEccmReportsV2DataLoaderService } from './eccm-statistic/dashboards/widget-data-loader/daily-eccm-v2.widget-data-loader.service';

@Module({
  imports: [
    EventEmitterModule.register({
      config: configuration().redmineIssueEventEmitterConfig,
    }),
    ConfigModule.forRoot({ load: [configuration] }),
    CacheModule.register({
      isGlobal: true,
    }),
    ScheduleModule.forRoot(),
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'frontend', 'build'),
    }),
  ],
  controllers: [
    AppController,
    MainController,
    CurrentIssuesEccmReportController,
    DailyEccmReportController,
    SimpleKanbanBoardController,
    SimpleIssuesListController,
    TagsManagerController,
    TelegramBotController,
  ],
  providers: [
    AppService,
    CustomFieldsEnhancer,
    CurrentUserEnhancer,
    PersonalNotificationsService,
    StatusChangeNotificationsService,
    Changes,
    ChangesCacheWriterService,
    TelegramBotService,
    UserMetaInfoService,
    UserMetaInfo,
    PersonalNotificationAdapterService,
    StatusChangeAdapterService,
    CurrentIssuesEccmReportService,
    CurrentIssuesEccmBotHandlerService,
    DailyEccmReportService,
    ChangesService,
    DailyEccmReportsDatasource,
    DailyEccmReportTask,
    DailyEccmReportsUserCommentsDatasource,
    DailyEccmUserCommentsService,
    SetDailyEccmUserCommentBotHandlerService,
    DailyEccmWithExtraDataService,
    IssuesByTagsWidgetDataLoaderService,
    {
      provide: 'CATEGORY_MERGE_TO_TAGS_ENHANCER',
      useFactory: (configService: ConfigService) => {
        const eccmProjectName = configService.get<string>(
          'redmineEccm.projectName',
        );
        return new CategoryMergeToTagsEnhancer([eccmProjectName, 'EDM', 'ELM']);
      },
      inject: [ConfigService],
    },
    CreateTagManagerServiceProvider('TAG_MANAGER_SERVICE'),
    DashboardInitService,
    DashboardsService,
    DailyEccmReportsV2Datasource,
    DailyEccmV2ReportTaskRunnerService,
    DailyEccmReportsV2DataLoaderService,
  ],
})
export class AppModule implements OnModuleInit {
  private logger = new Logger(AppModule.name);

  constructor(
    private personalNotificationsService: PersonalNotificationsService,
    private redmineIssuesCacheWriterService: RedmineIssuesCacheWriterService,
    private enhancerService: EnhancerService,
    private timestampEnhancer: TimestampEnhancer,
    private customFieldsEnhancer: CustomFieldsEnhancer,
    private currentUserEnhancer: CurrentUserEnhancer,
    private issueUrlEnhancer: IssueUrlEnhancer,
    private statusChangeNotificationsService: StatusChangeNotificationsService,
    private changesCacheWriterService: ChangesCacheWriterService,
    private telegramBotService: TelegramBotService,
    private personalNotificationAdapterService: PersonalNotificationAdapterService,
    private statusChangeAdapterService: StatusChangeAdapterService,
    private dailyEccmUserCommentsService: DailyEccmUserCommentsService,
    private setDailyEccmUserCommentBotHandlerService: SetDailyEccmUserCommentBotHandlerService,

    @Inject('CATEGORY_MERGE_TO_TAGS_ENHANCER')
    private categoryMergeToTagsEnhancer: CategoryMergeToTagsEnhancer,

    @Inject('CALENDAR_ENHANCER')
    private calendarEnhancer: CalendarEnhancer,

    private dashboardInitService: DashboardInitService,

    private dashboardsService: DashboardsService,
    private dailyEccmV2ReportService: DailyEccmV2ReportTaskRunnerService,
  ) {}

  onModuleInit() {
    const datasources = [
      Issues.getDatasource(),
      Users.getDatasource(),
      Changes.getDatasource(),
      UserMetaInfo.getDatasource(),
      DailyEccmReportsDatasource.getDatasource(),
      DailyEccmReportsUserCommentsDatasource.getDatasource(),
      DashboardsDs.getDatasource(),
      DailyEccmReportsV2Datasource.getDatasource(),
    ];

    this.enhancerService.addEnhancer([
      this.timestampEnhancer,
      this.customFieldsEnhancer,
      this.currentUserEnhancer,
      this.issueUrlEnhancer,
      this.categoryMergeToTagsEnhancer,
      this.calendarEnhancer,
    ]);

    this.personalNotificationsService.$messages.subscribe((resp) => {
      this.logger.log(
        `Get personal message ` +
          JSON.stringify(resp.personalParsedMessage.message) +
          ` for recipients ` +
          JSON.stringify(resp.personalParsedMessage.recipients),
      );
      this.personalNotificationAdapterService.send(resp);
    });
    this.statusChangeNotificationsService.$batchChanges.subscribe((changes) => {
      this.statusChangeAdapterService.batchSend(changes);
    });

    this.redmineIssuesCacheWriterService.subject
      .pipe(
        tap((saveResult) => {
          this.logger.debug(
            `Save result process started, issue_id = ${saveResult.current.id}`,
          );
        }),
      )
      .pipe(
        switchMap(async (saveResult) => {
          this.logger.debug(`personalNotificationsService.analize started`);
          await this.personalNotificationsService.analize(saveResult);
          this.logger.debug('personalNotificationsService.analize successed');
          return saveResult;
        }),
        switchMap(async (saveResult) => {
          // eslint-disable-next-line prettier/prettier
          this.logger.debug(`statusChangeNotificationsService.getChanges started`);
          const changes =
            await this.statusChangeNotificationsService.getChanges(saveResult);
          // eslint-disable-next-line prettier/prettier
          this.logger.debug(`statusChangeNotificationsService.getChanges successed`);
          return { changes, saveResult };
        }),
        switchMap(async (args) => {
          this.logger.debug(`Save changes in couchdb started`);
          const promises = args.changes.map((c) =>
            this.changesCacheWriterService.saveChange(c),
          );
          await Promise.all(promises);
          this.logger.debug('Save changes in couchdb successed');
          return args;
        }),
        switchMap(async (args) => {
          this.logger.debug(``);
          return args;
        }),
      )
      .subscribe(async (args) => {
        this.logger.debug(
          `Save result process success finished, issue_id = ${args.saveResult.current.id}`,
        );
      });

    this.initDailyEccmUserCommentsPipeline();
    this.initDashbordProviders();

    Promise.all(datasources).then(() => {
      this.dailyEccmV2ReportService.setDashboardsService(
        this.dashboardsService,
      );
      this.dailyEccmV2ReportService.initAutoScanJobs();
    });
  }

  private initDailyEccmUserCommentsPipeline(): void {
    this.setDailyEccmUserCommentBotHandlerService.$messages.subscribe(
      (data) => {
        this.dailyEccmUserCommentsService.setComment(
          data.userId,
          data.date,
          data.comment,
        );
      },
    );
  }

  private initDashbordProviders(): void {
    this.dashboardInitService.init();
  }
}
