import { Requester, UpstashRequest, UpstashResponse, Index } from '@upstash/vector';

type MutuallyExclusives<TFields extends string, TParameter> = {
    [P in TFields]: {
        [Q in P]: Q extends "in" | "notIn" ? TParameter[] : TParameter;
    } & {
        [R in Exclude<TFields, P>]?: never;
    };
}[TFields];
type StringOperation = "equals" | "notEquals" | "glob" | "notGlob" | "in" | "notIn";
type NumberOperation = "equals" | "notEquals" | "lessThan" | "lessThanOrEquals" | "greaterThan" | "greaterThanOrEquals" | "in" | "notIn";
type BooleanOperation = "equals" | "notEquals" | "in" | "notIn";
type ArrayOperation = "contains" | "notContains";
type ValidOperations<T> = T extends number ? MutuallyExclusives<NumberOperation, T> : T extends string ? MutuallyExclusives<StringOperation, T> : T extends boolean ? MutuallyExclusives<BooleanOperation, T> : T extends any[] ? MutuallyExclusives<ArrayOperation, any> : never;
type Leaf<TContent> = {
    [Field in keyof TContent]: {
        [K in Field]: ValidOperations<TContent[K]>;
    } & {
        [K in Exclude<keyof TContent, Field>]?: never;
    };
}[keyof TContent];
type TreeNode<TContent> = Leaf<TContent> | {
    OR: TreeNode<TContent>[];
} | {
    AND: TreeNode<TContent>[];
};

type CacheSetting = "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload" | false;
type RetryConfig = false | {
    /**
     * The number of retries to attempt before giving up.
     *
     * @default 5
     */
    retries?: number;
    /**
     * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.
     *
     * @default
     * ```ts
     * Math.exp(retryCount) * 50
     * ```
     */
    backoff?: (retryCount: number) => number;
};
type RequesterConfig = {
    /**
     * Configure the retry behaviour in case of network errors
     */
    retry?: RetryConfig;
    /**
     * Configure the cache behaviour
     * @default "no-store"
     */
    cache?: CacheSetting;
};
type HttpClientConfig = {
    headers?: Record<string, string>;
    baseUrl: string;
    retry?: RetryConfig;
} & RequesterConfig;
declare class HttpClient implements Requester {
    baseUrl: string;
    headers: Record<string, string>;
    readonly options: {
        cache?: CacheSetting;
    };
    readonly retry: {
        attempts: number;
        backoff: (retryCount: number) => number;
    };
    constructor(config: HttpClientConfig);
    request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>>;
}

type Dict = Record<string, unknown>;
type UpsertParameters<TContent extends Dict, TIndexMetadata extends Dict> = {
    id: string;
    content: TContent;
    metadata?: TIndexMetadata;
};
type Document<TContent extends Dict, TMetadata extends Dict, TWithScore extends boolean = false> = {
    id: string;
    content: TContent;
    metadata?: TMetadata;
} & (TWithScore extends true ? {
    score: number;
} : {});
type SearchResult<TContent extends Dict, TMetadata extends Dict> = Document<TContent, TMetadata, true>[];

/**
 * Represents a search index for managing and querying documents.
 *
 * Each SearchIndex instance operates within a specific index, allowing for
 * isolated document storage and retrieval. It provides methods to upsert, search,
 * fetch, delete, and manage documents within the index.
 *
 * @template TContent - Content shape associated with each document.
 * @template TIndexMetadata - Metadata shape associated with each document.
 */
declare class SearchIndex<TContent extends Dict = Dict, TIndexMetadata extends Dict = Dict> {
    private httpClient;
    private vectorIndex;
    private indexName;
    /**
     * Initializes a new SearchIndex instance for the specified index.
     *
     * @param vectorIndex - The underlying vector index used for search operations.
     * @param indexName - The name to use for this index. Must be a non-empty string.
     * @throws Will throw an error if the indexn name is not provided.
     */
    constructor(httpClient: HttpClient, vectorIndex: Index, indexName: string);
    /**
     * Inserts or updates documents in the index.
     *
     * Documents are identified by their unique IDs. If a document with the same ID exists, it will be updated.
     *
     * @param params - A document or array of documents to upsert, including `id`, `content`, and optional `metadata`.
     * @returns A promise resolving to the result of the upsert operation.
     */
    upsert: (params: UpsertParameters<TContent, TIndexMetadata> | UpsertParameters<TContent, TIndexMetadata>[]) => Promise<string>;
    /**
     * Searches for documents matching a query string.
     *
     * Returns documents that best match the provided query, optionally filtered and limited in number.
     *
     * @param query - Text string used to find matching documents within the index.
     * @param limit - Maximum number of results to retrieve (defaults to 5 documents).
     * @param filter - Optional search constraint using either a string expression or structured filter object.
     * @param reranking - Optional boolean to enhance search result ordering.
     * @param semanticWeight - Optional relevance balance between semantic and keyword search (0-1 range, defaults to 0.75).
     *   For instance, 0.2 applies 20% semantic matching with 80% full-text matching.
     *   You can learn more about how Upstash Search works from [our docs](https://upstash.com/docs/search/features/algorithm).
     * @param inputEnrichment - Optional boolean to enhance queries before searching (enabled by default).
     * @returns Promise that resolves to an array of documents matching the
     */
    search: (params: {
        query: string;
        limit?: number;
        filter?: string | TreeNode<TContent>;
        reranking?: boolean;
        semanticWeight?: number;
        inputEnrichment?: boolean;
    }) => Promise<SearchResult<TContent, TIndexMetadata>>;
    /**
     * Fetches documents by their IDs from the index.
     *
     * @param params - An array of document IDs to retrieve.
     * @returns A promise resolving to an array of documents or `null` if a document is not found.
     */
    fetch: (params: Parameters<Index["fetch"]>[0]) => Promise<({
        id: string;
        content: TContent;
        metadata: TIndexMetadata;
    } | null)[]>;
    /**
     * Deletes documents by their IDs from the index.
     *
     * @param params - An array of document IDs to delete.
     * @returns A promise resolving to the result of the deletion operation.
     */
    delete: (params: Parameters<Index["delete"]>[0]) => Promise<{
        deleted: number;
    }>;
    /**
     * Retrieves documents within a specific range, with pagination support.
     *
     * Useful for paginating through large result sets by providing a `cursor`.
     *
     * @param params - Range parameters including `cursor`, `limit`, and ID `prefix`.
     * @returns A promise resolving to the next cursor and documents in the range.
     */
    range: (params: {
        cursor: string;
        limit: number;
        prefix?: string;
    }) => Promise<{
        nextCursor: string;
        documents: {
            id: string;
            content: TContent;
            metadata: TIndexMetadata | undefined;
        }[];
    }>;
    /**
     * Clears all documents in the current index.
     *
     * Useful for resetting the index before or after tests, or when a clean state is needed.
     *
     * @returns A promise resolving to the result of the reset operation.
     */
    reset: () => Promise<string>;
    /**
     * Deletes the entire index and all its documents.
     *
     * Use with caution, as this operation is irreversible.
     *
     * @returns A promise resolving to the result of the delete operation.
     */
    deleteIndex: () => Promise<string>;
    /**
     * Retrieves information about the current index.
     *
     * Provides document count and pending document count, indicating documents that are awaiting indexing.
     *
     * @returns A promise resolving to index information with document counts.
     */
    info: () => Promise<{
        pendingDocumentCount: number;
        documentCount: number;
    }>;
}

/**
 * Provides search capabilities over indexes.
 */
declare class Search {
    private client;
    protected vectorIndex: Index;
    /**
     * Creates a new Search instance.
     *
     * @param vectorIndex - The underlying index used for search operations.
     */
    constructor(client: HttpClient);
    /**
     * Returns a SearchIndex instance for a given index.
     *
     * Each index is an isolated collection where documents can be added,
     * retrieved, searched, and deleted.
     *
     * @param indexName - The name to use as an index.
     * @returns A SearchIndex instance for managing documents within the index.
     */
    index: <TContent extends Dict = Dict, TIndexMetadata extends Dict = Dict>(indexName: string) => SearchIndex<TContent, TIndexMetadata>;
    /**
     * Retrieves a list of all available indexes.
     *
     * @returns An array of strings representing the names of available indexes.
     */
    listIndexes: () => Promise<string[]>;
    /**
     * Retrieves overall search index statistics.
     *
     * This includes disk usage, total document count, pending document count,
     * and details about each available index.
     *
     * @returns An object containing search system metrics and index details.
     */
    info: () => Promise<{
        diskSize: number;
        pendingDocumentCount: number;
        documentCount: number;
        indexes: {
            [k: string]: {
                pendingDocumentCount: number;
                documentCount: number;
            };
        };
    }>;
}

export { type RequesterConfig as R, Search as S };
