import { Autowired, Injectable } from '@opensumi/di';
import {
  CancellationToken,
  CancellationTokenSource,
  QuickOpenHandler,
  QuickOpenItem,
  QuickOpenMode,
  QuickOpenModel,
  URI,
  getIcon,
  getSymbolIcon,
} from '@opensumi/ide-core-browser';
import { ILogger, IReporterService, REPORT_NAME, localize } from '@opensumi/ide-core-common';
import { QuickOpenActionProvider, QuickOpenBaseAction } from '@opensumi/ide-quick-open';
import { IWorkspaceService } from '@opensumi/ide-workspace';
import { SymbolKind as SymbolKindEnum } from '@opensumi/monaco-editor-core/esm/vs/editor/common/languages';

import {
  EditorGroupSplitAction,
  ILanguageService,
  WorkbenchEditorService,
  WorkspaceSymbolParams,
  WorkspaceSymbolProvider,
} from '../../common';

import type { Range, SymbolInformation } from 'vscode-languageserver-types';

@Injectable()
class WorkspaceSymbolOpenSideAction extends QuickOpenBaseAction {
  constructor() {
    super({
      id: 'workspace-symbol:splitToRight',
      tooltip: localize('quickOpen.openOnTheRightSide'),
      class: getIcon('embed'),
    });
  }

  async run(item: SymbolInformationQuickOpenItem): Promise<void> {
    await item.openSide();
  }
}

@Injectable()
class WorkspaceSymbolActionProvider implements QuickOpenActionProvider {
  @Autowired()
  workspaceSymbolOpenSideActionOpen: WorkspaceSymbolOpenSideAction;

  hasActions(): boolean {
    return true;
  }

  getActions() {
    return [this.workspaceSymbolOpenSideActionOpen];
  }
}

@Injectable()
export class WorkspaceSymbolQuickOpenHandler implements QuickOpenHandler {
  @Autowired(ILanguageService)
  private readonly languageService: ILanguageService;

  @Autowired(IWorkspaceService)
  private readonly workspaceService: IWorkspaceService;

  @Autowired(ILogger)
  private readonly logger: ILogger;

  @Autowired(WorkbenchEditorService)
  private readonly workbenchEditorService: WorkbenchEditorService;

  @Autowired()
  private readonly workspaceSymbolActionProvider: WorkspaceSymbolActionProvider;

  @Autowired(IReporterService)
  reporterService: IReporterService;

  prefix = '#';

  get description() {
    return localize('editor.workspaceSymbol.description');
  }

  private cancellationSource = new CancellationTokenSource();

  getModel(): QuickOpenModel {
    return {
      onType: (
        lookFor: string,
        acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider | undefined) => void,
      ) => {
        if (lookFor === '') {
          acceptor([
            new QuickOpenItem({
              label: localize('editor.workspaceSymbol.search'),
              run: () => false,
            }),
          ]);
          return;
        }

        if (lookFor === '#') {
          return void acceptor([
            new QuickOpenItem({
              label: localize('editor.workspaceSymbolClass.search'),
              run: () => false,
            }),
          ]);
        }

        if (this.languageService.workspaceSymbolProviders.length === 0) {
          acceptor([
            new QuickOpenItem({
              label: localize('editor.workspaceSymbol.notfound'),
              run: () => false,
            }),
          ]);
          return;
        }

        const isSearchClass = lookFor[0] === '#';

        const items: QuickOpenItem[] = [];
        this.cancellationSource.cancel();
        const newCancellationSource = new CancellationTokenSource();
        this.cancellationSource = newCancellationSource;

        const param: WorkspaceSymbolParams = {
          query: isSearchClass ? lookFor.slice(1) : lookFor,
        };
        const timer = this.reporterService.time(REPORT_NAME.QUICK_OPEN_MEASURE);
        Promise.all(
          this.languageService.workspaceSymbolProviders.map(async (provider) => {
            let symbols = await provider.provideWorkspaceSymbols(param, newCancellationSource.token);
            if (isSearchClass) {
              symbols = symbols?.filter((symbol) => symbol.kind === SymbolKindEnum.Class);
            }
            if (symbols && symbols.length && !newCancellationSource.token.isCancellationRequested) {
              const quickOpenItems = await Promise.all(
                symbols.map(async (symbol) => {
                  const relative = await this.workspaceService.asRelativePath(new URI(symbol.location.uri).parent);
                  return new SymbolInformationQuickOpenItem(
                    symbol,
                    provider,
                    this.workbenchEditorService,
                    newCancellationSource.token,
                    relative?.path || '',
                  );
                }),
              );
              items.push(...quickOpenItems);
              acceptor(items, this.workspaceSymbolActionProvider);
            }
            return symbols;
          }),
        )
          .catch((e) => {
            this.logger.log(e);
          })
          .finally(() => {
            if (!newCancellationSource.token.isCancellationRequested) {
              // 无数据清空历史数据
              if (!items.length) {
                acceptor([]);
              }
              timer.timeEnd(isSearchClass ? 'class' : 'symbol', {
                lookFor,
                stat: {
                  symbol: items.length,
                },
              });
            }
          });
      },
    };
  }

  getOptions() {
    return {
      fuzzyMatchLabel: {
        enableSeparateSubstringMatching: true,
      },
      showItemsWithoutHighlight: true,
      // 不搜索文件路径
      fuzzyMatchDescription: false,
      getPlaceholderItem: (lookFor: string, originLookFor: string) =>
        new QuickOpenItem({
          label: localize(
            originLookFor.startsWith('##') ? 'editor.workspaceSymbolClass.notfound' : 'editor.workspaceSymbol.notfound',
          ),
          run: () => false,
        }),
    };
  }

  onClose() {
    this.cancellationSource.cancel();
  }

  onToggle() {
    this.cancellationSource.cancel();
  }
}

export class SymbolInformationQuickOpenItem extends QuickOpenItem {
  constructor(
    protected readonly symbol: SymbolInformation,
    private readonly provider: WorkspaceSymbolProvider,
    private readonly workbenchEditorService: WorkbenchEditorService,
    private readonly token: CancellationToken,
    private relativePath: string,
  ) {
    super({});
  }

  getLabel(): string {
    return this.symbol.name;
  }

  getUri() {
    return new URI(this.symbol.location.uri);
  }

  getIconClass() {
    return getSymbolIcon(this.symbol.kind as SymbolKindEnum);
  }

  getDescription() {
    const containerName = this.symbol.containerName;
    return `${containerName ? `${containerName} · ` : ''}${decodeURIComponent(this.relativePath)}`;
  }

  private fromRange(range: Range) {
    if (!range) {
      return undefined;
    }
    const { start, end } = range;
    return {
      startLineNumber: start.line + 1,
      startColumn: start.character + 1,
      endLineNumber: end.line + 1,
      endColumn: end.character + 1,
    };
  }

  private open(uri: URI, range: Range) {
    this.workbenchEditorService.open(uri, {
      range: this.fromRange(range),
    });
  }

  run(mode: QuickOpenMode): boolean {
    const uri = this.getUri();
    if (mode === QuickOpenMode.OPEN) {
      this.provider.resolveWorkspaceSymbol(this.symbol, this.token).then((resolvedSymbol) => {
        if (resolvedSymbol) {
          this.open(uri, resolvedSymbol.location.range);
        } else {
          this.open(uri, this.symbol.location.range);
        }
      });
    }
    return true;
  }

  openSide() {
    const uri = this.getUri();
    return this.provider.resolveWorkspaceSymbol(this.symbol, this.token).then((resolvedSymbol) => {
      this.workbenchEditorService.open(uri, {
        preview: false,
        split: EditorGroupSplitAction.Right,
        range: this.fromRange(resolvedSymbol ? resolvedSymbol.location.range : this.symbol.location.range),
        focus: true,
      });
    });
  }
}
