import { LazyQueryHookOptions, MutationHookOptions, MutationOptions, QueryHookOptions, QueryOptions } from '@apollo/client';
import { ResultOf, TypedDocumentNode, VariablesOf } from '@graphql-typed-document-node/core';
import { ModelWithId } from '../../..';
import { Services as IServices } from '../services-factory';
import { Documents } from '../types';
type RemoveSuffix = true;
export type CanBeMovedCallback<T = unknown> = (data?: {
    newModel?: T;
    oldModel?: T;
}) => void | boolean;
export type CacheUpdateOptions<T> = T & {
    cacheUpdateOptions?: {
        canBeMoved?: CanBeMovedCallback<unknown>;
        enabled?: boolean;
        isEvictDisabled?: boolean;
        optimistic?: boolean;
    };
};
export type ServiceQueryOptions<T extends TypedDocumentNode> = Omit<QueryOptions<VariablesOf<T>, ResultOf<T>>, 'query'>;
export type ServiceQueryHookOptions<T extends TypedDocumentNode> = Omit<QueryHookOptions<ResultOf<T>, VariablesOf<T>>, 'query'>;
export type ServiceLazyQueryHookOptions<T extends TypedDocumentNode> = Omit<LazyQueryHookOptions<ResultOf<T>, VariablesOf<T>>, 'query'>;
export type ServiceMutationOptions<T extends TypedDocumentNode> = Omit<MutationOptions<ResultOf<T>, VariablesOf<T>>, 'mutation'>;
export type ServiceMutationHookOptions<T extends TypedDocumentNode> = Omit<MutationHookOptions<ResultOf<T>, VariablesOf<T>>, 'mutation'>;
export type ServiceReturnType<T extends keyof IServices, M extends keyof IServices[T]> = IServices[T][M] extends (...args: any) => any ? Awaited<ReturnType<IServices[T][M]>> : IServices[T][M];
export type ServiceListNode<Data extends {
    edges: Array<{
        node: ModelWithId;
    }>;
}> = Data['edges'][number]['node'];
export type ServiceUseListNode<Data extends {
    data: {
        list: {
            edges: Array<{
                node: ModelWithId;
            }>;
        };
    };
}> = ServiceListNode<Data['data']['list']>;
/**
 * all the fields which have suffix "ID" but with removed suffix
 * Example
 * ```ts
 *  HasOneRelationsOf<{
 *    organizationID: string;
 *    accountID: string
 *   }>
 * ````
 * will return
 * ```ts
 * 'organization' | 'account
 * ```
 */
export type HasOneRelationsOf<T> = FindBySuffix<T, 'ID', RemoveSuffix> | FindBySuffix<T, 'Id', RemoveSuffix>;
/**
 * all the array fields which have suffix "s"
 * Example
 * ```ts
 *  HasOneRelationsOf<{
 *    themes: Theme[];
 *    topics: Topic[]
 *    id: string
 *   }>
 * ````
 * will return
 * ```ts
 * 'themes' | 'topics
 * ```
 */
export type InputAndRelations<T extends Documents.Create, Model extends ModelWithId> = ModelRelationsInput<Model> & VariablesOf<T>['input'];
export type HasManyRelationsOf<T> = PickArrayFields<T>;
export type ModelRelationsInput<T> = Partial<Record<HasManyRelationsOf<T>, ModelWithId[]> & Record<HasOneRelationsOf<T>, ModelWithId>>;
export type MutationOptionsWithoutMutation<T extends TypedDocumentNode> = Omit<CacheUpdateOptions<MutationOptions<ResultOf<T>, VariablesOf<T>>>, 'mutation'>;
export type IDocuments = {
    counts: TypedDocumentNode;
    create: Documents.Create;
    delete: Documents.Delete;
    get: Documents.Get;
    getBySegmentID: Documents.GetBySegmentID;
    list: Documents.List;
    reducedList: Documents.List;
    table: Documents.List;
    update: Documents.Update;
    updateMany: Documents.UpdateMany;
};
type CreateMutationVariables<Variables, Relations> = Relations & Omit<Variables, keyof Relations>;
type UpdateMutationVariables<Variables, Relations> = Relations & Omit<Variables, keyof Relations> & {
    id: string;
};
export type ICrudService<Model extends ModelWithId> = {
    create: <T extends Documents.Create>(document: T) => (input: CreateMutationVariables<VariablesOf<T>['input'], ModelRelationsInput<Model>>, options?: MutationOptionsWithoutMutation<T>) => Promise<ResultOf<T>['createOne']>;
    delete: <T extends Documents.Delete>(document: T) => (id: string, options?: MutationOptionsWithoutMutation<T>) => Promise<ResultOf<T>['deleteOne']>;
    get: <T extends Documents.Get>(document: T) => (id: string, options?: Omit<QueryOptions<VariablesOf<T>, ResultOf<T>>, 'query'>) => Promise<ResultOf<T>['get']>;
    getBySegmentID: <T extends Documents.GetBySegmentID>(document: T) => (segmentID: string, options?: Omit<QueryOptions<VariablesOf<T>, ResultOf<T>>, 'query'>) => Promise<ResultOf<T>['get']>;
    hasManyModels?: Partial<Record<HasManyRelationsOf<Model>, TypedDocumentNode<unknown, {
        input: {
            id: string;
            relationIds: string[];
        };
    }>>>;
    /**
     *  Keys will be taken from the input, transformed and put back into the input.
     *  Example:
     * ```ts
     * { user: {id: '1', age: '2'} } -> { userID: user?.id || null }
     * ```
     */
    hasOneModels?: HasOneRelationsOf<Model>[];
    list: <T extends Documents.List>(document: T, cacheKey?: keyof IDocuments | false) => (input: VariablesOf<T>, options?: ServiceQueryOptions<T>) => Promise<ResultOf<T>['list']>;
    update: <T extends Documents.Update>(document: T) => (input: UpdateMutationVariables<VariablesOf<T>['input']['update'], ModelRelationsInput<Model>>, options?: MutationOptionsWithoutMutation<T>) => Promise<ResultOf<T>['updateOne']>;
    updateMany: <T extends Documents.UpdateMany>(document: T) => (input: VariablesOf<T>['input'], options?: MutationOptionsWithoutMutation<T>) => Promise<ResultOf<T>['updateMany']>;
};
export type ValueOf<T> = T[keyof T];
type PickArrayFields<T> = keyof Omit<PickByValue<T, Array<ModelWithId>>, keyof PickByValue<T, string | number | boolean>>;
type PickByValue<T, ValueType> = Pick<T, {
    [Key in keyof T]-?: T[Key] extends ValueType ? Key : never;
}[keyof T]>;
/**
 *  Here is the explanation for the code above:
 *  1. We want to get a type of a property where the property's value is a string and the property's key is a string with the given suffix.
 *  2. We do that by iterating through the keys of the type T and checking if the value of the current key is a string.
 *  3. If the value of the current key is a string, we check if the current key is a string with the given suffix.
 *  4. If the current key is a string with the given suffix, we want to return the current key if the ShouldRemoveSuffix is false or return a part of the current key without the suffix if the ShouldRemoveSuffix is true.
 */
type FindBySuffix<T, Suffix extends string, ShouldRemoveSuffix extends boolean = false> = {
    [Key in keyof T]: T[Key] extends string ? Key extends `${infer PartWithoutSuffix}${Suffix}` ? ShouldRemoveSuffix extends true ? PartWithoutSuffix : Key : never : never;
}[keyof T];
export {};
