/* eslint-disable @typescript-eslint/no-namespace */
import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-enhancer';
import { TagStyledEnhancerNs } from '@app/event-emitter/issue-enhancers/tag-styled-enhancer';
import { TimePassedHighlightEnhancer } from '@app/event-emitter/issue-enhancers/time-passed-highlight-enhancer';
import {
  IssuesService,
  IssuesServiceNs,
} from '@app/event-emitter/issues/issues.service';
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
import { FlatIssuesStore } from '@app/event-emitter/utils/flat-issues-store';
import { GetValueFromObjectByKey } from '@app/event-emitter/utils/get-value-from-object-by-key';
import { TreeIssuesStore } from '@app/event-emitter/utils/tree-issues-store';
import { Injectable, Logger } from '@nestjs/common';
import nano from 'nano';
import * as PriorityStylesEnhancerNs from '@app/event-emitter/issue-enhancers/priority-styles-enhancer';
import { WidgetDataLoaderInterface } from '../widget-data-loader-interface';
import {
  AppError,
  Result,
  createAppError,
  success,
} from '@app/event-emitter/utils/result';

export namespace ListIssuesByFieldsWidgetNs {
  export type Params = {
    fromRootIssueId?: number;
    fromQuery?: nano.MangoQuery;
    fields: Field[];
    delimiter: string;
    sort?: boolean;
    statuses: string[];
  } & TagStyledEnhancerNs.ConfigWithTagsStyles;

  export type Field = {
    path: string;
    default: string;
  };
}

type Params = ListIssuesByFieldsWidgetNs.Params;

@Injectable()
export class ListIssuesByFieldsWidgetDataLoaderService
  implements WidgetDataLoaderInterface<Params, any, any>
{
  private logger = new Logger(ListIssuesByFieldsWidgetDataLoaderService.name);
  private issuesLoader: IssuesServiceNs.IssuesLoader;

  constructor(
    private issuesService: IssuesService,
    private timePassedHighlightEnhancer: TimePassedHighlightEnhancer,
    private issueUrlEnhancer: IssueUrlEnhancer,
  ) {
    this.issuesLoader = this.issuesService.createDynamicIssuesLoader();
  }

  isMyConfig(): boolean {
    return true;
  }

  async load(widgetParams: Params): Promise<Result<any, AppError>> {
    let store: FlatIssuesStore;
    if (widgetParams.fromRootIssueId) {
      store = await this.getListFromRoot(widgetParams.fromRootIssueId);
    } else if (widgetParams.fromQuery) {
      store = await this.getListByQuery(widgetParams.fromQuery);
    } else {
      const errMsg = `Wrong widgetParams value`;
      this.logger.error(errMsg);
      return fail(createAppError(errMsg));
    }
    await store.enhanceIssues([
      this.timePassedHighlightEnhancer,
      this.issueUrlEnhancer,
      TagStyledEnhancerNs.CreateTagStyledEnhancerForConfig(widgetParams),
      PriorityStylesEnhancerNs.CreatePriorityStylesEnhancer(widgetParams),
    ]);
    const grouped = store.groupByStatusWithExtra((issue) => {
      return this.getGroupFromIssue(issue, widgetParams);
    }, widgetParams.statuses);
    let res = [] as any[];
    for (const key in grouped) {
      if (Object.prototype.hasOwnProperty.call(grouped, key)) {
        const data = grouped[key];
        res.push({
          data: data,
          metainfo: this.createMetaInfo(key),
        });
      }
    }
    if (widgetParams.sort) {
      res = res.sort((a, b) => {
        return a.metainfo.title.localeCompare(b.metainfo.title);
      });
    }
    return success(res);
  }

  private async getListFromRoot(issueId: number): Promise<FlatIssuesStore> {
    const treeStore = new TreeIssuesStore();
    const rootIssue = await this.issuesService.getIssue(issueId);
    treeStore.setRootIssue(rootIssue);
    await treeStore.fillData(this.issuesLoader);
    return treeStore.getFlatStore();
  }

  private async getListByQuery(
    query: nano.MangoQuery,
  ): Promise<FlatIssuesStore> {
    const rawData = await this.issuesService.find(query);
    const store = new FlatIssuesStore();
    for (let i = 0; i < rawData.length; i++) {
      const issue = rawData[i];
      store.push(issue);
    }
    return store;
  }

  private getGroupFromIssue(
    issue: RedmineTypes.ExtendedIssue,
    params: Params,
  ): string | null {
    if (!issue) return null;
    const values = [] as string[];
    for (let i = 0; i < params.fields.length; i++) {
      const field = params.fields[i];
      const valueResult = GetValueFromObjectByKey(issue, field.path);
      const value: string = valueResult.result
        ? valueResult.result
        : field.default;
      values.push(value);
    }
    return values.join(params.delimiter);
  }

  private createMetaInfo(title: string): Record<string, any> {
    return {
      title: title,
    };
  }
}
