import { ValueExpression, SqlFragment, IdentifierSqlToken, CommonQueryMethods, FragmentSqlToken, QuerySqlToken } from "slonik";
import { z } from "zod";
import { comparisonFilterType, dateFilterType, jsonbContainsFilter, stringFilterType } from "../helpers/sqlUtils";
import type { PromiseOrValue } from "../helpers/types";
declare type LoadViewParameters<TFilter extends Record<string, any> = Record<never, any>, TFilterKey extends keyof TFilter = never, TFragment extends SqlFragment | QuerySqlToken = SqlFragment, TColumns extends string[] = never> = {
    select: TFragment | TColumns;
    orderBy?: SqlFragment;
    groupBy?: SqlFragment;
    take?: number;
    skip?: number;
    ctx?: any;
    where?: RecursiveFilterConditions<{
        [x in TFilterKey]?: TFilter[x];
    }>;
    db?: Pick<CommonQueryMethods, 'any'>;
};
export declare type Interpretors<TFilter extends Record<string, any>, TFilterKey extends keyof TFilter = keyof TFilter extends Record<infer K, any> ? K extends string ? K : never : never, TContext = any> = {
    [x in TFilterKey]?: {
        prefix?: string;
        interpret: (filter: TFilter[x], allFilters: TFilter, context: TContext) => Promise<SqlFragment | null | undefined | false> | SqlFragment | null | undefined | false;
    };
};
export declare type BuildView<TFilter extends Record<string, any> = Record<never, any>, TFilterKey extends keyof TFilter = never, TAliases extends string = "_main", TColumns extends string = never> = {
    /**
     * Allows adding custom filters to the view
     * Multiple filters can be added at once
     * This is mainly to be used in conjunction with getFilters
     * WARNING: Do not use this otherwise, unless you know what you're doing.
     * Prefer using the other filter methods, especially addGenericFilter if you need more flexibility
     * @param filters - The filters to add
     */
    addFilters<TNewFilter extends Record<string, any> = Record<never, any>, TNewFilterKey extends keyof TNewFilter = keyof TNewFilter extends Record<infer K, any> ? K extends string ? K : never : never>(filters: {
        [x in TNewFilterKey]?: (filter: TNewFilter[x], allFilters: TFilter & TNewFilter, context: any) => Promise<SqlFragment | null | undefined | false> | SqlFragment | null | undefined | false;
    }): BuildView<TNewFilter & TFilter, keyof TNewFilter | TFilterKey, TAliases, TColumns>;
    /**
     * Allows filtering by string operators, e.g. "contains", "starts with", "ends with", etc.
     * @param field - The name of the filter - Can be a nested field, e.g. "user.name"
     * @param mapper - Optional if you want to use a different column name than the filter name
     */
    addStringFilter: <TKey extends Exclude<string, TFilterKey>>(field: TKey | TKey[], name?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: z.infer<typeof stringFilterType>, allFilters?: TFilter, ctx?: any) => SqlFragment)) => BuildView<TFilter & {
        [x in TKey]?: z.infer<typeof stringFilterType>;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Allows filtering by comparison operators, e.g. "greater than", "less than", "between", "in", etc.
     * @param field - The name of the filter - Can be a nested field, e.g. "user.name"
     * @param mapper - Optional if you want to use a different column name than the filter name
     * @returns
     */
    addComparisonFilter: <TKey extends Exclude<string, TFilterKey>>(name: TKey | TKey[], mapper?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: z.infer<typeof comparisonFilterType>, allFilters?: TFilter, ctx?: any) => SqlFragment), type?: "text" | "numeric" | "integer" | "bigint" | string) => BuildView<TFilter & {
        [x in TKey]?: z.infer<typeof comparisonFilterType>;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
   * Allows filtering jsonb columns, using the @> operator to check if a JSONB column contains a certain value or structure.
   * ```
    view.addJsonContainsFilter('settings', () => sql.fragment`'user.user_settings'`)
    ```
    Allows for
    ```
      where: {
        settings: {
          notifications: true,
          theme: 'dark',
          nested: {
            value: 'something'
          }
        }
      }
    ```
   * */
    addJsonContainsFilter: <TKey extends Exclude<string, TFilterKey>>(name: TKey | TKey[], mapper?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: any, allFilters?: TFilter, ctx?: any) => SqlFragment)) => BuildView<TFilter & {
        [x in TKey]?: Parameters<typeof jsonbContainsFilter>[0];
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Allows filtering by date operators, e.g. "greater than", "less than" etc.
     * */
    addDateFilter: <TKey extends Exclude<string, TFilterKey>>(name: TKey | TKey[], mapper?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: z.infer<typeof dateFilterType>, allFilters?: TFilter, ctx?: any) => SqlFragment)) => BuildView<TFilter & {
        [x in TKey]?: z.infer<typeof dateFilterType>;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Loads data from the view
     * ```
     * const data = await usersView
     *  .options({ db }).load({
     *   select: sql.fragment`*`,
     *   where: {
     *     id: 1
     *   },
     * })
     * ```
     * */
    load: <TFragment extends SqlFragment | QuerySqlToken, TSelect extends TColumns = never, TObject = [TSelect] extends [never] ? TFragment extends QuerySqlToken<infer T> ? z.infer<T> : any : Record<TSelect, any>>(args: LoadViewParameters<TFilter, TFilterKey, TFragment, TSelect[]>) => Promise<readonly TObject[]>;
    setColumns: <TNewColumns extends string = never>(columns: ({
        [x in TNewColumns]: SqlFragment;
    }) | ArrayLike<TNewColumns>) => BuildView<TFilter, TFilterKey, TAliases, TColumns | TNewColumns>;
    /**
     * Sets the context for the view. This context can be used in various parts of the view lifecycle, such as in filters or constraints.
     * @param ctx - The context object. Each key-value pair in the object sets a context variable.
     * @returns The updated BuildView instance with the new context.
     * */
    context: <TContext extends Record<string, any>>(ctx?: TContext) => BuildView<TFilter, TFilterKey, TAliases, TColumns>;
    /**
     * Sets options for the view. Options can configure various aspects of how the view operates.
     * @param opts - The options object. Each key-value pair in the object sets an option.
     * @returns The updated BuildView instance with the new options.
     * */
    options: <TOptions extends Record<string, any>>(opts?: TOptions) => BuildView<TFilter, TFilterKey, TAliases, TColumns>;
    /**
     * Allows preprocessing the filters before they are interpreted
     * */
    setFilterPreprocess: (preprocess: (filters: TFilter, context: any) => Promise<TFilter> | TFilter) => BuildView<TFilter, TFilterKey, TAliases, TColumns>;
    /**
     * Sets table aliases. By default there's a `_main` alias for the main table that's referenced in the FROM fragment.
     *
     * These aliases can then be used in some of the filters, e.g.
     * ```ts
     * buildView`FROM users`
     * .addStringFilter('name', (table) => sql.fragment`COALESCE(${table._main}.first_name, ${table._main}.last_name)`)
     * ```
     *
     * would be translated to `COALESCE(users.first_name, users.last_name)`
     *
     * because `users` is the main table that's referred in the FROM clause.
     * */
    setTableAliases: <TNewAliases extends string>(table: Record<TNewAliases, string | IdentifierSqlToken>) => BuildView<TFilter, TFilterKey, TAliases | TNewAliases, TColumns>;
    /**
     * Allows filtering by boolean operators, e.g. "is true", "is false", "is null", etc.
     * @param field - The name of the filter - Can be a nested field, e.g. "user.name"
     * @param mapper - Optional if you want to use a different column name than the filter name
     * @returns
     * */
    addBooleanFilter: <TKey extends Exclude<string, TFilterKey>>(name: TKey | TKey[], mapper?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: boolean, allFilters?: TFilter, ctx?: any) => SqlFragment), falseFragment?: SqlFragment) => BuildView<TFilter & {
        [x in TKey]?: boolean;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Allows filtering by single or multiple string values
     * And returns all rows where the value is in the array
     * */
    addInArrayFilter: <TKey extends Exclude<string, TFilterKey>, TType extends "text" | "numeric" | "integer" | "bigint" = never, TValue = [TType] extends [never] ? string : TType extends "numeric" | "integer" | "bigint" ? number : string>(name: TKey | TKey[], mapper?: SqlFragment | ((table: {
        [x in TAliases]: IdentifierSqlToken;
    } & {
        [x: string]: IdentifierSqlToken;
    }, value?: TValue | TValue[] | null, allFilters?: TFilter, ctx?: any) => SqlFragment), type?: TType) => BuildView<TFilter & {
        [x in TKey]?: TValue | TValue[] | null;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Use this to add a generic filter, that returns a SQL fragment
     * This filter won't be applied if the value is null or undefined
     * */
    addGenericFilter: <TKey extends Exclude<string, TFilterKey>, TNewFilter>(name: TKey, interpret: (filter: TNewFilter, allFilters: TFilter & {
        TKey: TNewFilter;
    }, context: any) => Promise<SqlFragment | null | undefined | false> | SqlFragment | null | undefined | false) => BuildView<TFilter & {
        [x in TKey]?: TNewFilter;
    }, keyof TFilter | TKey, TAliases, TColumns>;
    /**
     * Returns the SQL query
     * @param args - The arguments to filter by
     * @returns - The SQL query fragment
     * */
    getWhereConditions(args: {
        where?: RecursiveFilterConditions<{
            [x in TFilterKey]?: TFilter[x];
        }>;
        ctx?: any;
        options?: FilterOptions;
    }): Promise<SqlFragment[]>;
    getWhereFragment(args: {
        where?: RecursiveFilterConditions<{
            [x in TFilterKey]?: TFilter[x];
        }>;
        ctx?: any;
        options?: FilterOptions;
    }): Promise<FragmentSqlToken>;
    setConstraints: (constraints: (ctx: any) => PromiseOrValue<SqlFragment | SqlFragment[] | null | undefined>) => BuildView<TFilter, TFilterKey, TAliases>;
    getFromFragment(ctx: Record<string, any>): FragmentSqlToken;
    /**
     * Returns all filters that have been added to the view
     * @param options - Options for configuring the filters
     */
    getFilters<TInclude extends Extract<TFilterKey, string> | `${string}*` = never, TExclude extends Extract<TFilterKey, string> | `${string}*` = never, TRealInclude extends Extract<TFilterKey, string> = TInclude extends `${infer K}*` ? Extract<TFilterKey, `${K}${string}`> : Extract<TInclude, Extract<TFilterKey, string>>, TRealExclude extends Extract<TFilterKey, string> = TExclude extends `${infer K}*` ? Extract<TFilterKey, `${K}${string}`> : Extract<TExclude, Extract<TFilterKey, string>>, TPrefix extends string = "", TRealPrefix extends string = TPrefix extends `${string}.` ? TPrefix : `${TPrefix}.`>(options?: {
        table?: TPrefix;
        include?: readonly TInclude[];
        exclude?: readonly TExclude[];
    }): {
        [x in TFilterKey extends TRealExclude ? never : [TRealInclude] extends [never] ? TFilterKey extends `${TRealPrefix}${string}` ? TFilterKey : `${TRealPrefix}${Extract<TFilterKey, string>}` : TFilterKey extends `${TRealPrefix}${string}` ? Extract<TFilterKey, TRealInclude> : `${TRealPrefix}${Extract<TFilterKey, TRealInclude>}`]?: (filter: TFilter[x extends `${TRealPrefix}${infer K}` ? K extends TFilterKey ? K : x : x], allFilters: any, context: any) => Promise<SqlFragment | null | undefined | false> | SqlFragment | null | undefined | false;
    };
} & SqlFragment;
declare type FilterOptions = {
    orEnabled?: boolean;
    /** If true, auth constraints aren't considered. Only use if you're already adding them in query loaders */
    bypassConstraints?: boolean;
};
export declare const buildView: (parts: readonly string[], ...values: readonly (ValueExpression | ((ctx: Record<string, any>) => ValueExpression))[]) => BuildView<Record<never, any>, never, "_main", never>;
export declare type RecursiveFilterConditions<TFilter, TDisabled extends "AND" | "OR" | "NOT" = never> = TFilter & Omit<{
    AND?: RecursiveFilterConditions<TFilter>[];
    OR?: RecursiveFilterConditions<TFilter>[];
    NOT?: RecursiveFilterConditions<TFilter>;
}, TDisabled>;
export {};
