/* eslint-disable prettier/prettier */
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Issues } from '@app/event-emitter/couchdb-datasources/issues';
import { UNLIMITED } from '@app/event-emitter/consts/consts';
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
import nano from 'nano';
import Handlebars from 'handlebars';
import { EccmConfig } from 'src/models/eccm-config.model';
import { IssuesService } from '@app/event-emitter/issues/issues.service';
import { RedminePublicUrlConverter } from '@app/event-emitter/converters/redmine-public-url.converter';

// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace CurrentIssuesEccmReport {
  export type Options = {
    statuses?: string[];
    versions?: string[];
    userIds?: number[];
    project?: string;
    template?: string;
    fields?: string[];
  };

  // eslint-disable-next-line @typescript-eslint/no-namespace
  export namespace Defaults {
    export const statuses = [
      'In Progress',
      'Feedback',
      'Re-opened',
      'Code Review',
      'Resolved',
      'Testing',
    ];

    export const projectName = 'ECCM';
    
    export const currentIssuesFields = [
      'id',
      'tracker.name',
      'status.id',
      'status.name',
      'priority.id',
      'priority.name',
      'fixed_version.name',
      'subject',
      'updated_on',
      'updated_on_timestamp',
      'current_user.id',
      'current_user.firstname',
      'current_user.lastname',
    ];
  }

  export type User = {
    id: number;
    firstname: string;
    lastname: string;
  };

  export type Status = {
    id: number;
    name: string;
  };

  export type IssuesAndStatus = {
    status: Status;
    issues: RedmineTypes.Issue[];
  };

  export type UserReport = {
    user: User;
    issuesGroupedByStatus: IssuesAndStatus[];
  };

  export class UsersReport {
    private report: UserReport[] = [];
    
    private reportTemplate = [
      // context - this.report: UserReport[]
      '{{#each this}}',
        // context - UserReport
        '{{user.firstname}} {{user.lastname}}:',
        '',
        '{{#each issuesGroupedByStatus}}',
          // context - IssuesAndStatus
          '{{status.name}}:',
          '{{#each issues}}',
            // context - RedmineTypes.Issue
            `  - {{>redmineIssueAHref issue=.}}: {{subject}} (прио - {{priority.name}}, версия - {{fixed_version.name}})`,
          '{{/each}}',
        '{{/each}}',
        '',
      '{{/each}}',
    ].join('\n');

    constructor(
      private redminePublicUrlConverter: RedminePublicUrlConverter,
      private redminePublicUrl: string,
    ) {
      Handlebars.registerPartial(
        'redmineIssueAHref',
        `<a href="${this.redminePublicUrl}/issues/{{issue.id}}">{{issue.tracker.name}} #{{issue.id}}</a>`,
      );
    }

    push(item: any): void {
      const user: User = {
        id: item.current_user.id,
        firstname: item.current_user.firstname,
        lastname: item.current_user.lastname,
      };
      const status: Status = {
        id: item.status.id,
        name: item.status.name,
      };
      const issuesAndStatus = this.getOrCreateIssuesAndStatus(user, status);
      issuesAndStatus.issues.push(item);
    }

    getOrCreateUserReport(user: User): UserReport {
      let userReport: UserReport;
      userReport = this.report.find((r) => r.user.id == user.id);
      if (!userReport) {
        userReport = {
          user: user,
          issuesGroupedByStatus: [],
        };
        this.report.push(userReport);
      }
      return userReport;
    }

    getOrCreateIssuesAndStatus(user: User, status: Status): IssuesAndStatus {
      const userReport = this.getOrCreateUserReport(user);
      let issuesAndStatus: IssuesAndStatus;
      issuesAndStatus = userReport.issuesGroupedByStatus.find(
        (i) => i.status.id == status.id,
      );
      if (!issuesAndStatus) {
        issuesAndStatus = {
          issues: [],
          status: status,
        };
        userReport.issuesGroupedByStatus.push(issuesAndStatus);
      }
      return issuesAndStatus;
    }
    
    getAllUsersReportByTemplate(template?: string): string {
      const reportFormatter = Handlebars.compile(template || this.reportTemplate);
      return reportFormatter(this.report);
    }
  }
}

@Injectable()
export class CurrentIssuesEccmReportService {
  private eccmConfig: EccmConfig.Config;
  private logger = new Logger(CurrentIssuesEccmReportService.name);

  constructor(
    private configService: ConfigService,
    private issuesDatasource: Issues,
    private redminePublicUrlConverter: RedminePublicUrlConverter,
    private issuesService: IssuesService,
  ) {
    this.eccmConfig = this.configService.get<EccmConfig.Config>('redmineEccm');
  }

  async getData(
    options?: CurrentIssuesEccmReport.Options,
  ): Promise<RedmineTypes.Issue[]> {
    const datasource = await this.issuesDatasource.getDatasource();

    const query: nano.MangoQuery = {
      selector: {
        'status.name': {
          $in: CurrentIssuesEccmReport.Defaults.statuses,
        },
        'fixed_version.name': {
          $in: this.eccmConfig.currentVersions,
        },
        'project.name':
          options?.project || CurrentIssuesEccmReport.Defaults.projectName,
      },
      limit: UNLIMITED,
    };

    if (options && options.fields) {
      query.fields = options.fields;
    }
    if (options && options.statuses) {
      query.selector['status.name'] = {
        $in: options.statuses,
      };
    }
    if (options && options.versions) {
      query.selector['fixed_version.name'] = {
        $in: options.versions,
      };
    }
    if (options && options.userIds) {
      query.selector['current_user.id'] = {
        $in: options.userIds,
      };
    } else {
      query.selector['current_user.id'] = {
        $exists: true,
      };
    }
    
    this.logger.debug(`Query for get report data: ${JSON.stringify(query)}`);

    const data = await this.issuesService.find(query);
    this.logger.debug(`Found issues for report: ${JSON.stringify(data.length)}`);
    
    return data;
  }

  async getReport(options?: CurrentIssuesEccmReport.Options): Promise<string> {
    this.logger.debug(`Options for report: ${JSON.stringify(options || null)}`);
    const data = await this.getData(options);
    this.logger.debug(`Data for report ${JSON.stringify(data)}`);
    const report = new CurrentIssuesEccmReport.UsersReport(
      this.redminePublicUrlConverter,
      this.configService.get<string>('redmineUrlPublic'),
    );
    data.forEach((item) => report.push(item));
    return report.getAllUsersReportByTemplate(options?.template);
  }
}
