import * as _azure_functions from '@azure/functions';
import { CosmosClient, Container, SqlQuerySpec, FeedOptions, Resource, ItemDefinition } from '@azure/cosmos';

type Base = object;
type CosmosResource<T extends Base> = Resource & T;
type CosmosItemDefinition<T extends Base> = ItemDefinition & T;
interface AutoFields {
    /** Automatically generate an ID on document creation - defaults to true */
    id?: boolean;
    /** Automatically generate createdAt and updatedAt fields on document create/updates - defaults to true */
    timestamp?: boolean;
}
interface ModelOptions {
    /** The name of the Cosmos database */
    database: string;
    /** The name of the Cosmos container within the database */
    container: string;
    /** The instantiated Cosmos client */
    client: CosmosClient;
    /** The name of the env of the Cosmos connection string - defaults to `COSMOS_CONNECTION_STRING` */
    connectionStringSetting?: string;
    /** Automatic fields creation - defaults to true */
    fields?: AutoFields | boolean;
}
declare const initial: {};
declare class BaseModel<T extends Base = typeof initial> {
    private options;
    client: Container;
    connectionStringSetting: string;
    fields: AutoFields;
    constructor(options: ModelOptions);
    /**
     * Create an Azure Function app input binding to find a specific document by an input variable.
     * You can also provide a type (matching `Record<string, any>`) as the first type generic,
     * and it will validate the variable to be one of the types keys.
     *
     * @example
     * ```ts
     * const shopInput = orm.shops.createFindBinding(`shop`)
     * ```
     * @example
     * An example with a type to check the variable:
     * ```ts
     * interface ActivityInput {
     *  shop: string
     *  // ...
     * }
     *
     * const shopDocument = orm.shops.createFindBinding<ActivityInput>('shop') // ✅
     * const shopDocument2 = orm.shops.createFindBinding<ActivityInput>('id') // ❌
     * ```
     */
    createFindBinding<Input extends object = object, Key = keyof Input & string>(variable: Key): _azure_functions.CosmosDBInput;
    /** Create an Azure Function app input binding to fetch all documents from this container. */
    createAllBinding(): _azure_functions.CosmosDBInput;
    /**
     * Create an Azure Function app input binding with a custom SQL query.
     * @example
     * ```ts
     * const inputDoc = orm.posts.createSQLBinding(`SELECT * FROM c WHERE c.deleted_at IS NULL`)
     * ```
     */
    createSQLBinding(sqlQuery: string): _azure_functions.CosmosDBInput;
    /** Fetch all resources in a container */
    all(): Promise<CosmosItemDefinition<T>[]>;
    /** Fetch a specific resource by its ID */
    find(id: string): Promise<CosmosResource<T> | undefined>;
    /** Fetch multiple resources using their ID's */
    findMany(ids: string[]): Promise<CosmosResource<T>[]>;
    /** Find a resource by a specific key */
    findBy(key: keyof T, value: string): Promise<CosmosResource<T> | undefined>;
    /** Find multiple resources by a specific key */
    findManyBy(key: keyof T, value: string): Promise<CosmosResource<T>[]>;
    /** Create a resource */
    create(input: Omit<T, 'id' | 'createdAt' | 'updatedAt'> & Partial<{
        id: string;
        createdAt: string;
        updatedAt: string;
    }>): Promise<CosmosResource<T> | undefined>;
    /** Either update or create a resource */
    upsert(input: T & {
        id: string;
    }): Promise<CosmosResource<T> | undefined>;
    /** Update a resource - replaces the whole resource, so make sure to provide a full input */
    replace(id: string, input: Omit<T, 'updatedAt'> & Partial<{
        updatedAt: string;
    }>): Promise<CosmosResource<T> | undefined>;
    /** Delete a resource */
    delete(id: string): Promise<CosmosResource<T> | undefined>;
    /**
     * Run a query, and fetch all results
     *
     * This function accepts a generic, so you can pass in the type of the response if
     * you are running a custom select query - for example:
     *
     * `.query<{ id: string }>('SELECT c.id FROM c') // returns { id: string }[]`
     *
     * `.query<number>('SELECT VALUE count(c.id) FROM c') // returns [number]`
     *
     * This is just a wrapper of the `.client.items.query()` function, so you can use that
     * instead if you need access to the request metrics for example.
     */
    query<R = any>(query: string | SqlQuerySpec, options?: Pick<FeedOptions, 'maxItemCount'>): Promise<R[]>;
}

interface Builder {
    createModel: <T extends Base>(container: string, options?: Pick<ModelOptions, 'fields'>) => BaseModel<T>;
}
/** Default client configuration - for example the connection string setting, and the database name. */
interface Options<M extends {
    [K: string]: BaseModel;
}> {
    /** The name of the Cosmos database */
    database: string;
    /**
     * The name of the env of the Cosmos connection string - defaults to `COSMOS_CONNECTION_STRING`.
     * This can be replaced by directly passing in the connection string with the `connectionString` option,
     * but if you are using the binding shortcuts then this setting is required as it is used in the Azure Function bindings.
     */
    connectionStringSetting?: string;
    /**
     * The Cosmos connection string - overrides using the `connectionStringSetting` env.
     *
     * Preferably use the `connectionStringSetting` with the connection string as an environment variable if you are using
     * this within an Azure Functions app.
     */
    connectionString?: string;
    /** A list of the models to create, and their container names. */
    models: (builder: Builder) => M;
}
type DB<M extends Record<string, BaseModel>> = ReturnType<Options<M>['models']> & {
    client: CosmosClient;
};
declare function createClient<M extends Record<string, BaseModel>>(options: Options<M>): DB<M>;

export { type Base, BaseModel, type DB, type ModelOptions, type Options, createClient };
