declare module "flexsearch" {
    // 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;

        // 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 = false
    > 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 = false> {
        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;
}