import { injectable, inject } from 'tsyringe';
import * as operations from '../../shared/operations';
import * as parsers from './parsers';
import * as urls from '../../shared/urls';
import TabPresenter from '../presenters/TabPresenter';
import WindowPresenter from '../presenters/WindowPresenter';
import HelpPresenter from '../presenters/HelpPresenter';
import CachedSettingRepository from '../repositories/CachedSettingRepository';
import BookmarkRepository from '../repositories/BookmarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
import RepeatUseCase from '../usecases/RepeatUseCase';

@injectable()
export default class CommandIndicator {
  constructor(
    @inject('TabPresenter') private tabPresenter: TabPresenter,
    private windowPresenter: WindowPresenter,
    private helpPresenter: HelpPresenter,
    @inject("CachedSettingRepository") private cachedSettingRepository: CachedSettingRepository,
    private bookmarkRepository: BookmarkRepository,
    private consoleClient: ConsoleClient,
    private contentMessageClient: ContentMessageClient,
    private repeatUseCase: RepeatUseCase,
  ) {
  }

  async open(keywords: string): Promise<browser.tabs.Tab> {
    const url = await this.urlOrSearch(keywords);
    this.repeatUseCase.storeLastOperation({
      type: operations.INTERNAL_OPEN_URL,
      url,
    });
    return this.tabPresenter.open(url);
  }

  async tabopen(keywords: string): Promise<browser.tabs.Tab> {
    const url = await this.urlOrSearch(keywords);
    this.repeatUseCase.storeLastOperation({
      type: operations.INTERNAL_OPEN_URL,
      url,
      newTab: true,
    });
    return this.tabPresenter.create(url);
  }

  async winopen(keywords: string): Promise<browser.windows.Window> {
    const url = await this.urlOrSearch(keywords);
    this.repeatUseCase.storeLastOperation({
      type: operations.INTERNAL_OPEN_URL,
      url,
      newWindow: true,
    });
    return this.windowPresenter.create(url);
  }

  // eslint-disable-next-line max-statements
  async buffer(keywords: string): Promise<any> {
    if (keywords.length === 0) {
      return;
    }

    if (!isNaN(Number(keywords))) {
      const tabs = await this.tabPresenter.getAll();
      const index = parseInt(keywords, 10) - 1;
      if (index < 0 || tabs.length <= index) {
        throw new RangeError(`tab ${index + 1} does not exist`);
      }
      return this.tabPresenter.select(tabs[index].id as number);
    } else if (keywords.trim() === '%') {
      // Select current window
      return;
    } else if (keywords.trim() === '#') {
      // Select last selected window
      const lastId = await this.tabPresenter.getLastSelectedId();
      if (typeof lastId === 'undefined' || lastId === null) {
        throw new Error('No last selected tab');
      }
      return this.tabPresenter.select(lastId);
    }

    const current = await this.tabPresenter.getCurrent();
    const tabs = await this.tabPresenter.getByKeyword(keywords, false);
    if (tabs.length === 0) {
      throw new RangeError('No matching buffer for ' + keywords);
    }
    for (const tab of tabs) {
      if (tab.index > current.index) {
        return this.tabPresenter.select(tab.id as number);
      }
    }
    return this.tabPresenter.select(tabs[0].id as number);
  }

  async bdelete(force: boolean, keywords: string): Promise<any> {
    const excludePinned = !force;
    const tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
    if (tabs.length === 0) {
      throw new Error('No matching buffer for ' + keywords);
    } else if (tabs.length > 1) {
      throw new Error('More than one match for ' + keywords);
    }
    return this.tabPresenter.remove([tabs[0].id as number]);
  }

  async bdeletes(force: boolean, keywords: string): Promise<any> {
    const excludePinned = !force;
    const tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
    const ids = tabs.map(tab => tab.id as number);
    return this.tabPresenter.remove(ids);
  }

  async quit(): Promise<any> {
    const tab = await this.tabPresenter.getCurrent();
    return this.tabPresenter.remove([tab.id as number]);
  }

  async quitAll(): Promise<any> {
    const tabs = await this.tabPresenter.getAll();
    const ids = tabs.map(tab => tab.id as number);
    this.tabPresenter.remove(ids);
  }

  async addbookmark(title: string): Promise<any> {
    const tab = await this.tabPresenter.getCurrent();
    const item = await this.bookmarkRepository.create(title, tab.url as string);
    const message = 'Saved current page: ' + item.url;
    return this.consoleClient.showInfo(tab.id as number, message);
  }

  async set(keywords: string): Promise<any> {
    if (keywords.length === 0) {
      return;
    }
    const [name, value] = parsers.parseSetOption(keywords);
    await this.cachedSettingRepository.setProperty(name, value);

    return this.contentMessageClient.broadcastSettingsChanged();
  }

  help(): Promise<void> {
    return this.helpPresenter.open();
  }

  private async urlOrSearch(keywords: string): Promise<any> {
    const settings = await this.cachedSettingRepository.get();
    return urls.searchUrl(keywords, settings.search);
  }
}