import { DynamicModule, Logger, Module, OnModuleInit } from '@nestjs/common';
import { EventEmitterService } from './event-emitter.service';
import { RedmineEventsGateway } from './events/redmine-events.gateway';
import MainConfig from './configs/main-config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RedmineDataLoader } from './redmine-data-loader/redmine-data-loader';
import { MainController } from './main/main.controller';
import { ModuleParams } from './models/module-params';
import { RedmineIssuesCacheWriterService } from './issue-cache-writer/redmine-issues-cache-writer.service';
import { CouchDb } from './couchdb-datasources/couchdb';
import { Users } from './couchdb-datasources/users';
import { Issues } from './couchdb-datasources/issues';
import { RedmineUserCacheWriterService } from './user-cache-writer/user-cache-writer.service';
import { UsersController } from './users/users.controller';
import { RedmineTypes } from './models/redmine-types';
import { UsersService } from './users/users.service';
import { IssuesService } from './issues/issues.service';
import { IssuesController } from './issues/issues.controller';
import { TimestampEnhancer } from './issue-enhancers/timestamps-enhancer';
import { EnhancerService } from './issue-enhancers/enhancer.service';
import { DynamicLoader } from './configs/dynamic-loader';
import { RedminePublicUrlConverter } from './converters/redmine-public-url.converter';
import { IssueUrlEnhancer } from './issue-enhancers/issue-url-enhancer';
import { TimePassedHighlightEnhancer } from './issue-enhancers/time-passed-highlight-enhancer';
import { IssuesUpdaterService } from './issues-updater/issues-updater.service';
import { CalendarEnhancer } from './issue-enhancers/calendar-enhancer';
import { CalendarService } from './calendar/calendar.service';
import { CalendarController } from './calendar/calendar.controller';
import { Dashboards as DashboardsDs } from './couchdb-datasources/dashboards';
import { DashboardController } from './dashboards/dashboard.controller';
import { DashboardsService } from './dashboards/dashboards.service';
import { DashboardsDataService } from './dashboards/dashboards-data.service';
import { RootIssueSubTreesWidgetDataLoaderService } from './dashboards/widget-data-loader/root-issue-subtrees.widget-data-loader.service';
import { ListIssuesByUsersWidgetDataLoaderService } from './dashboards/widget-data-loader/list-issues-by-users.widget-data-loader.service';
import { ListIssuesByUsersLikeJiraWidgetDataLoaderService } from './dashboards/widget-data-loader/list-issues-by-users-like-jira.widget-data-loader.service';
import { ListIssuesByFieldsWidgetDataLoaderService } from './dashboards/widget-data-loader/list-issues-by-fields.widget-data-loader.service';
import { WidgetsCollectionService } from './dashboards/widgets-collection.service';
import { DashboardsController } from './dashboards/dashboards.controller';
import { CalendarWidgetDataLoaderService } from './dashboards/widget-data-loader/calendar.widget-data-loader.service';
import { ScheduleModule } from '@nestjs/schedule';
import { CsvListenerFactory } from './csvlistener/csv-listener';
import { RootIssueListenerFactory } from './rootissuelistener/root-issue-listener';

@Module({})
export class EventEmitterModule implements OnModuleInit {
  static register(params?: ModuleParams): DynamicModule {
    return {
      module: EventEmitterModule,
      imports: [
        ConfigModule.forRoot({ load: [() => params?.config || MainConfig()] }),
        ScheduleModule.forRoot(),
      ],
      providers: [
        EventEmitterService,
        RedmineEventsGateway,
        RedmineDataLoader,
        RedmineIssuesCacheWriterService,
        CouchDb,
        Users,
        Issues,
        DashboardsDs,
        RedmineUserCacheWriterService,
        UsersService,
        IssuesService,
        TimestampEnhancer,
        EnhancerService,
        RootIssueSubTreesWidgetDataLoaderService,
        DynamicLoader,
        RedminePublicUrlConverter,
        IssueUrlEnhancer,
        ListIssuesByUsersWidgetDataLoaderService,
        ListIssuesByUsersLikeJiraWidgetDataLoaderService,
        TimePassedHighlightEnhancer,
        ListIssuesByFieldsWidgetDataLoaderService,
        {
          provide: 'ISSUES_UPDATER_SERVICE',
          useFactory: (configService: ConfigService) => {
            const redminePublicUrl =
              configService.get<string>('redmineUrlPublic');
            return new IssuesUpdaterService(redminePublicUrl);
          },
          inject: [ConfigService],
        },
        {
          provide: 'CALENDAR_ENHANCER',
          useFactory: (configService: ConfigService) => {
            const calendarEnhancerConfig =
              configService.get<Record<string, any>>('calendarEnhancer');
            return new CalendarEnhancer(
              calendarEnhancerConfig.useForProjects,
              calendarEnhancerConfig.customFields,
              calendarEnhancerConfig.descriptionCalendarParams,
              calendarEnhancerConfig.calendarEventsKey,
            );
          },
          inject: [ConfigService],
        },
        {
          provide: 'CALENDAR_SERVICE',
          useFactory: (
            calendarEnhancer: CalendarEnhancer,
            issuesService: IssuesService,
          ): CalendarService => {
            const calendarEventsKey = calendarEnhancer.calendarEventsKey;
            return new CalendarService(calendarEventsKey, issuesService);
          },
          inject: ['CALENDAR_ENHANCER', IssuesService],
        },
        DashboardsService,
        DashboardsDataService,
        WidgetsCollectionService,
        CalendarWidgetDataLoaderService,
        CsvListenerFactory,
        RootIssueListenerFactory,
      ],
      exports: [
        EventEmitterService,
        RedmineEventsGateway,
        RedmineDataLoader,
        RedmineIssuesCacheWriterService,
        CouchDb,
        Users,
        Issues,
        DashboardsDs,
        RedmineUserCacheWriterService,
        UsersService,
        IssuesService,
        TimestampEnhancer,
        EnhancerService,
        RootIssueSubTreesWidgetDataLoaderService,
        DynamicLoader,
        RedminePublicUrlConverter,
        IssueUrlEnhancer,
        ListIssuesByUsersWidgetDataLoaderService,
        ListIssuesByUsersLikeJiraWidgetDataLoaderService,
        TimePassedHighlightEnhancer,
        ListIssuesByFieldsWidgetDataLoaderService,
        {
          provide: 'ISSUES_UPDATER_SERVICE',
          useExisting: 'ISSUES_UPDATER_SERVICE',
        },
        {
          provide: 'CALENDAR_ENHANCER',
          useExisting: 'CALENDAR_ENHANCER',
        },
        {
          provide: 'CALENDAR_SERVICE',
          useExisting: 'CALENDAR_SERVICE',
        },
        DashboardsService,
        DashboardsDataService,
        WidgetsCollectionService,
        CalendarWidgetDataLoaderService,
      ],
      controllers: [
        MainController,
        UsersController,
        IssuesController,
        CalendarController,
        DashboardController,
        DashboardsController,
      ],
    };
  }

  private logger = new Logger(EventEmitterModule.name);

  constructor(
    private redmineEventsGateway: RedmineEventsGateway,
    private redmineIssuesCacheWriterService: RedmineIssuesCacheWriterService,
    private csvListenerFactory: CsvListenerFactory,
    private rootIssueListenerFactory: RootIssueListenerFactory,
  ) {}

  onModuleInit() {
    const csvListener = this.csvListenerFactory.getCsvListener();
    this.redmineEventsGateway.appendAndInitListener(csvListener);
    const rootIssueListener =
      this.rootIssueListenerFactory.getRootIssueListener();
    this.redmineEventsGateway.appendAndInitListener(rootIssueListener);
    const queue = this.redmineEventsGateway.getIssuesChangesQueue();
    const subj = queue.queue;
    subj.subscribe(async (issues: RedmineTypes.Issue[]) => {
      this.logger.debug(
        `Changed issues - ` +
          issues.map(
            (i) => {
              return (i && i.id && i.subject)
                ? `#${i.id} (${i.subject})`
                : '';
            }
          ).join(', '),
      );

      for (let i = 0; i < issues.length; i++) {
        const issue: RedmineTypes.Issue = issues[i];

        try {
          this.logger.debug(`Save issue #${issue.id} (${issue.subject})`);

          const response = await this.redmineIssuesCacheWriterService.saveIssue(
            issue,
          );

          this.logger.debug(
            // eslint-disable-next-line prettier/prettier
            `Save ${(response.prev) ? 'exists' : 'new'} issue #${issue.id} - count of changes = ${response.journalsDiff.length}`,
          );
        } catch (ex) {
          this.logger.error(`Saving issue error - ${ex}`, null, {
            issue: issue,
          });
        }
      }
    });
  }
}
