/* 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 { 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 '@app/event-emitter/dashboards/widget-data-loader-interface';
import {
  AppError,
  Result,
  createAppError,
  success,
} from '@app/event-emitter/utils/result';

export namespace IssuesByTagsWidgetNs {
  export type Params = {
    fromRootIssueId?: number;
    fromQuery?: nano.MangoQuery;
    tagsSort?: boolean;
    showEmptyTags?: string;
    statuses: string[];
  } & TagStyledEnhancerNs.ConfigWithTagsStyles;
}

type Params = IssuesByTagsWidgetNs.Params;

@Injectable()
export class IssuesByTagsWidgetDataLoaderService
  implements WidgetDataLoaderInterface<Params, any, any>
{
  private logger = new Logger(IssuesByTagsWidgetDataLoaderService.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.groupByStatusWithExtraToMultipleStories((issue) => {
      if (!issue || !widgetParams.tags || !widgetParams.tags.tagsKeyName) {
        return [];
      }
      const tagsResult = GetValueFromObjectByKey(
        issue,
        widgetParams.tags.tagsKeyName,
      );
      if (
        (tagsResult.error == 'NOT_FOUND' ||
          (tagsResult.result && tagsResult.result.length <= 0)) &&
        widgetParams.showEmptyTags
      ) {
        return [widgetParams.showEmptyTags];
      }
      if (
        typeof tagsResult.result !== 'object' ||
        tagsResult.result.length <= 0
      ) {
        return [];
      }
      return tagsResult.result;
    }, widgetParams.statuses);
    let res = [] as any[];
    for (const tag in grouped) {
      if (Object.prototype.hasOwnProperty.call(grouped, tag)) {
        const data = grouped[tag];
        res.push({
          data: data,
          metainfo: this.createMetaInfo(tag),
        });
      }
    }
    if (widgetParams.tagsSort) {
      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 createMetaInfo(tag: string): Record<string, any> {
    return {
      title: tag,
    };
  }
}
