declare module "flexsearch-ts" {
  // Type definitions for flexsearch 0.7
  // Project: https://github.com/nextapps-de/flexsearch/
  // Definitions by: LOSSES Don <https://github.com/Losses>
  // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

  /************************************/
  /* Utils                            */
  /************************************/
  export type Id = number | string;
  export type Limit = number;
  export type ExportHandler<T> = (id: string | number, value: T) => void;
  export type AsyncCallback<T = undefined> = T extends undefined
      ? () => void
      : (result: T) => void;
  export type UnknownFunction = (...x: unknown[]) => unknown;

  export type StoreOption = boolean | string | string[];
  export type EnrichStoreOption = true | string | string[];

  /************************************/
  /* Common Options                   */
  /************************************/

  /**
   * **Document:**
   * * Presets: https://github.com/nextapps-de/flexsearch#presets
   */
  export type Preset = "memory" | "performance" | "match" | "score" | "default";

  /**
   * **Document:**
   * * Tokenizer: https://github.com/nextapps-de/flexsearch#tokenizer-prefix-search
   * * Add custom tokenizer: https://github.com/nextapps-de/flexsearch#add-custom-tokenizer
   */
  export type Tokenizer =
      | "strict"
      | "forward"
      | "reverse"
      | "full"
      | ((x: string) => string[]);

  /**
   * **Document:**
   * * Encoders: https://github.com/nextapps-de/flexsearch#encoders
   */
  export type Encoders =
      | false
      | "default"
      | "simple"
      | "balance"
      | "advanced"
      | "extra"
      | ((x: string) => string[]);

  /**
   * **Document:**
   * * Contextual search: https://github.com/nextapps-de/flexsearch#contextual
   */
  export interface ContextOptions {
    resolution: number;
    depth: false | number;
    bidirectional: boolean;
  }

  /**
   * **Document:**
   * * Search options: https://github.com/nextapps-de/flexsearch#search-options
   */
  export interface SearchOptions {
    query?: string;
    limit?: number;
    offset?: number;
    suggest?: boolean;
  }

  /**
   * **Document:**
   * * The document descriptor: https://github.com/nextapps-de/flexsearch#the-document-descriptor
   */
  export interface Descriptor<T, Store extends StoreOption = false> {
    id: string | number;
    field: string[] | Array<IndexOptions<T, Store>>;
  }

  /**
   * **Document:**
   * * Context Options: https://github.com/nextapps-de/flexsearch#context-options
   */
  export interface ContextOptions {
    resolution: number;
    depth: false | number;
    bidirectional: boolean;
  }

  /**
   * **Document:**
   * * Charset options: https://github.com/nextapps-de/flexsearch#charset-options
   */
  export interface CharsetOptions {
    split: false | string | RegExp;
    rtl: boolean;
    encode: (x: string) => string[];
  }

  export interface Stemmer {
    [key: string]: string;
  }

  export interface Matcher {
    [key: string]: string;
  }

  export type FilterFunction = (x: string) => boolean;
  export type FilterArray = string[];

  /**
   * **Document:**
   * * Language Options: https://github.com/nextapps-de/flexsearch#language-options
   * * Language: https://github.com/nextapps-de/flexsearch#languages
   */
  export interface LanguageOptions {
    stemmer: false | string | Stemmer | UnknownFunction;
    filter: false | string | FilterArray | FilterFunction;
    matcher: false | string | Matcher | UnknownFunction;
  }

  /**
   * These options will determine how the documents be indexed.
   *
   * **Generic type parameters:**
   *
   * @template T The type of the document.
   * @template Store If store is enabled.
   *
   * **Document:**
   * * Index options: https://github.com/nextapps-de/flexsearch#index-options
   * * Language: https://github.com/nextapps-de/flexsearch#languages
   */
  export interface IndexOptions<T, Store extends StoreOption = false> {
    preset?: Preset;
    tokenize?: Tokenizer;
    cache?: boolean | number;
    resolution?: number;
    context?: boolean | IndexOptions<T, Store> | ContextOptions;
    optimize?: boolean;
    boost?: (words: string[], term: string, index: number) => number;
    async?: boolean;

    // Language-specific Options and Encoding
    charset?: CharsetOptions | string;
    language?: LanguageOptions | string;
    encode?: Encoders;
    stemmer?: LanguageOptions["stemmer"];
    filter?: LanguageOptions["filter"];
    matcher?: LanguageOptions["matcher"];
  }

  /************************************/
  /* Index Search                     */
  /************************************/

  export type IndexSearchResult = Id[];

  /**
   * **Document:**
   * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants
   * * API overview: https://github.com/nextapps-de/flexsearch#api-overview
   * * Usage: https://github.com/nextapps-de/flexsearch#usage
   */

  export class Index {
    constructor(x?: Preset | IndexOptions<string>);

    add(id: Id, item: string): this;

    append(id: Id, item: string): this;

    update(id: Id, item: string): this;

    remove(target: Id): this;

    search(query: string, options?: Limit | SearchOptions): IndexSearchResult;
    search(
        query: string,
        limit: number,
        options: SearchOptions
    ): IndexSearchResult;
    search(options: SearchOptions): IndexSearchResult;

    // https://github.com/nextapps-de/flexsearch#check-existence-of-already-indexed-ids
    contain(id: Id): boolean;

    export(handler: ExportHandler<string>): Promise<void>;

    import(id: Id, item: string): Promise<void>;

    // Async Methods
    addAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    appendAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    updateAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    removeAsync(target: Id, callback?: AsyncCallback<this>): Promise<this>;

    searchAsync(
        query: string,
        options?: Limit | SearchOptions,
        callback?: AsyncCallback<IndexSearchResult>
    ): Promise<IndexSearchResult>;
    searchAsync(
        query: string,
        limit: number,
        options?: Limit | SearchOptions
    ): IndexSearchResult;
    searchAsync(options: SearchOptions): Promise<IndexSearchResult>;
  }

  /**
   * **Document:**
   * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants
   * * API overview: https://github.com/nextapps-de/flexsearch#api-overview
   * * Worker index: https://github.com/nextapps-de/flexsearch#worker-index
   */
  export class Worker {
    constructor(x?: Preset | IndexOptions<string>);

    add(id: Id, item: string, callback?: AsyncCallback<Worker>): Promise<this>;

    append(id: Id, item: string, callback?: AsyncCallback<this>): Promise<this>;

    update(id: Id, item: string, callback?: AsyncCallback<this>): Promise<this>;

    remove(target: Id, callback?: AsyncCallback<this>): Promise<this>;

    search(
        query: string,
        options?: Limit | SearchOptions,
        callback?: AsyncCallback<IndexSearchResult>
    ): Promise<IndexSearchResult>;
    search(
        query: string,
        limit: number,
        options?: Limit | SearchOptions
    ): IndexSearchResult;
    search(options: SearchOptions): Promise<IndexSearchResult>;

    // Async Methods
    addAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    appendAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    updateAsync(
        id: Id,
        item: string,
        callback?: AsyncCallback<this>
    ): Promise<this>;

    removeAsync(target: Id, callback?: AsyncCallback<this>): Promise<this>;

    searchAsync(
        query: string,
        options?: Limit | SearchOptions,
        callback?: AsyncCallback<IndexSearchResult>
    ): Promise<IndexSearchResult>;
    searchAsync(
        query: string,
        limit: number,
        options?: Limit | SearchOptions
    ): IndexSearchResult;
    searchAsync(options: SearchOptions): Promise<IndexSearchResult>;
  }

  /************************************/
  /* Document Search                  */
  /************************************/

  /*
   * **Document:**
   * * Document options: https://github.com/nextapps-de/flexsearch#document-options
   */
  export interface DocumentOptions<T, Store extends StoreOption = false> {
    id: string;
    tag?: false | string;
    index:
        | string
        | string[]
        | Array<IndexOptions<T, Store> & { field: string }>;
    store?: Store;
  }

  /*
   * **Document:**
   * * Index options: https://github.com/nextapps-de/flexsearch#index-options
   */
  export interface IndexOptionsForDocumentSearch<
      T,
      Store extends StoreOption
      > extends IndexOptions<T, Store> {
    // Additional Options for Document Indexes
    worker?: boolean;
    document?: DocumentOptions<T, Store> | Descriptor<T, Store>;
  }

  export interface SimpleDocumentSearchResultSetUnit {
    field: string;
    result: Id[];
  }

  export interface EnrichedDocumentSearchResultSetUnitResultUnit<T> {
    id: Id;
    doc: T;
  }

  export interface EnrichedDocumentSearchResultSetUnit<T> {
    field: string;
    result: Array<EnrichedDocumentSearchResultSetUnitResultUnit<T>>;
  }

  /**
   *  # Document Search Result
   *
   *  To make your result return the full document:
   *  * set `store` to `true` while creating the document;
   *  * set `enrich` to `true` while searching.
   *
   *  If neither of these conditions is met, then the returned result will be a `ISimpleDocumentSearchResult`.
   */
  export type DocumentSearchResult<
      T,
      Store extends StoreOption = false,
      Enrich extends boolean = false
      > = [Store, Enrich] extends [EnrichStoreOption, true]
      ? Array<EnrichedDocumentSearchResultSetUnit<T>>
      : SimpleDocumentSearchResultSetUnit[];

  /**
   * **Document:**
   * * Document search options: https://github.com/nextapps-de/flexsearch#document-search-options
   */
  export interface DocumentSearchOptions<T extends boolean>
      extends SearchOptions {
    index?: string | string[] | SearchOptions[];
    tag?: string | string[];
    enrich?: T;
    bool?: "and" | "or";
  }

  /**
   * **Document:**
   * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants
   * * API overview: https://github.com/nextapps-de/flexsearch#api-overview
   * * Document store: https://github.com/nextapps-de/flexsearch#document-store
   */
  export class Document<T, Store extends StoreOption> {
    constructor(options: IndexOptionsForDocumentSearch<T, Store>, typeHack?: T);

    add(document: T): this;
    add(id: Id, document: T): this;

    append(document: T): this;
    append(id: Id, document: T): this;

    update(document: T): this;
    update(id: Id, document: T): this;

    remove(target: Id | T): this;

    search(query: string, limit?: number): SimpleDocumentSearchResultSetUnit[];

    // https://github.com/nextapps-de/flexsearch#field-search
    search(
        query: string,
        options: string[] | Partial<DocumentSearchOptions<boolean>>
    ): SimpleDocumentSearchResultSetUnit[];

    search<Enrich extends boolean = false>(
        query: string,
        limit?: number,
        options?: Partial<DocumentSearchOptions<Enrich>>
    ): DocumentSearchResult<T, Store, Enrich>;
    search(
        options: Partial<DocumentSearchOptions<boolean>>
    ): SimpleDocumentSearchResultSetUnit[];

    export(handler: ExportHandler<T>): Promise<void>;

    import(id: Id, document: T): Promise<void>;

    // Async Methods
    addAsync(id: Id, document: T, callback?: AsyncCallback): Promise<this>;

    appendAsync(id: Id, document: T, callback?: AsyncCallback): Promise<this>;

    updateAsync(id: Id, document: T, callback?: AsyncCallback): Promise<this>;

    removeAsync(target: Id | T, callback?: AsyncCallback): Promise<this>;

    searchAsync<Enrich extends boolean = false>(
        query: string,
        options: string[] | Partial<DocumentSearchOptions<Enrich>>
    ): Promise<DocumentSearchResult<T, Store, Enrich>>;
    searchAsync(
        query: string,
        limit?: number
    ): Promise<SimpleDocumentSearchResultSetUnit[]>;
    searchAsync(
        query: string,
        limit: number,
        callback: AsyncCallback<SimpleDocumentSearchResultSetUnit[]>
    ): Promise<this>;
    searchAsync<Enrich extends boolean = false>(
        query: string,
        options: Partial<DocumentSearchOptions<Enrich>>,
        callback: AsyncCallback<DocumentSearchResult<T, Store, Enrich>>
    ): Promise<this>;
    searchAsync<Enrich extends boolean = false>(
        options: Partial<DocumentSearchOptions<Enrich>>
    ): Promise<DocumentSearchResult<T, Store, Enrich>>;
    searchAsync<Enrich extends boolean = false>(
        options: Partial<DocumentSearchOptions<Enrich>>,
        callback: AsyncCallback<DocumentSearchResult<T, Store, Enrich>>
    ): Promise<this>;
  }

  /************************************/
  /* Miscellaneous                    */

  /************************************/
  export function create(options: IndexOptions<string>): Index;

  export function registerCharset(name: string, charset: CharsetOptions): void;

  export function registerLanguage(
      name: string,
      language: LanguageOptions
  ): void;
}
