import { CaseReducer } from '@reduxjs/toolkit';
import { CaseReducerActions } from '@reduxjs/toolkit';
import { Comparer } from '@reduxjs/toolkit';
import { Draft } from '@reduxjs/toolkit';
import { EntityId } from '@reduxjs/toolkit';
import { EntitySelectors } from '@reduxjs/toolkit';
import { EntityState } from '@reduxjs/toolkit';
import { OutputSelector } from '@reduxjs/toolkit';
import { PayloadAction } from '@reduxjs/toolkit';
import { Reducer } from '@reduxjs/toolkit';
import { Selector } from '@reduxjs/toolkit';
import { SerializedError } from '@reduxjs/toolkit';
import { SliceCaseReducers } from '@reduxjs/toolkit';
import { Update } from '@reduxjs/toolkit';

/**
 * @public
 */
export declare function createEntitySlice<TGlobalState, TEntity, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError>(options: ICreateEntitySliceOptions<TGlobalState, TEntity, TStatusEnum, TError>): IEntitySlice<TGlobalState, TEntity, TStatusEnum, TError>;

/**
 * @public
 */
export declare function createModelSlice<TGlobalState, TModel, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError>(options: ICreateModelSliceOptions<TGlobalState, TModel, TStatusEnum, TError>): IModelSlice<TGlobalState, TModel, TStatusEnum, TError>;

/**
 * @public
 */
export declare interface ICreateEntitySliceOptions<TGlobalState, TEntity, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends ISliceOptions<TGlobalState, IEntityState<TEntity, TStatusEnum, TError>> {
    selectId: (o: TEntity) => EntityId;
    sortComparer: false | Comparer<TEntity>;
}

/**
 * @public
 */
export declare interface ICreateModelSliceOptions<TGlobalState, TModel, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends ISliceOptions<TGlobalState, IModelState<TModel, TStatusEnum, TError>> {
    /**
     * This is how the slice will merge the current model and the update model during an update action
     * @defaultValue `merge` from {@link https://www.npmjs.com/package/ts-deepmerge}
     */
    handleUpdate?: (current: Draft<TModel>, update: Partial<TModel>) => TModel;
}

/**
 * @public
 */
export declare type IEntitySlice<TGlobalState, TEntity, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> = ISlice<TGlobalState, IEntityState<TEntity, TStatusEnum, TError>, IEntitySliceReducers<IEntityState<TEntity, TStatusEnum, TError>, TEntity, TStatusEnum, TError>, IEntitySliceSelectors<TGlobalState, TEntity, TStatusEnum, TError>>;

/**
 * @public
 */
export declare interface IEntitySliceReducers<TSliceState, TEntity, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends IMetaSliceReducers<TSliceState, TStatusEnum, TError> {
    /**
     * This will modify the slice. This adds one entity to the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter}
     */
    addOne: CaseReducer<TSliceState, PayloadAction<TEntity>>;
    /**
     * This will modify the slice. This adds many entities to the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter}
     */
    addMany: CaseReducer<TSliceState, PayloadAction<Array<TEntity> | Record<EntityId, TEntity>>>;
    /**
     * This will hydrate the slice. This sets one entity of the slice and lastHydrated, and resets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.setOne}
     */
    hydrateOne: CaseReducer<TSliceState, PayloadAction<TEntity>>;
    /**
     * This will hydrate the slice. This sets many entities of the slice and lastHydrated, and resets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.setMany}
     */
    hydrateMany: CaseReducer<TSliceState, PayloadAction<Array<TEntity> | Record<EntityId, TEntity>>>;
    /**
     * This will hydrate the slice. This sets all entities of the slice and lastHydrated, and resets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.setAll}
     */
    hydrateAll: CaseReducer<TSliceState, PayloadAction<Array<TEntity> | Record<EntityId, TEntity>>>;
    /**
     * This will modify the slice. This updates one entity of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.updateOne}
     */
    updateOne: CaseReducer<TSliceState, PayloadAction<Update<TEntity>>>;
    /**
     * This will modify the slice. This updates many entities of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.updateMany}
     */
    updateMany: CaseReducer<TSliceState, PayloadAction<Array<Update<TEntity>>>>;
    /**
     * This will modify the slice. This updates or inserts one entity of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.upsertOne}
     */
    upsertOne: CaseReducer<TSliceState, PayloadAction<TEntity>>;
    /**
     * This will modify the slice. This updates or inserts many entities of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.upsertMany}
     */
    upsertMany: CaseReducer<TSliceState, PayloadAction<Array<TEntity> | Record<EntityId, TEntity>>>;
    /**
     * This will modify the slice. This removes one entity of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.removeOne}
     */
    removeOne: CaseReducer<TSliceState, PayloadAction<EntityId>>;
    /**
     * This will modify the slice. This removes many entities of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.removeMany}
     */
    removeMany: CaseReducer<TSliceState, PayloadAction<Array<EntityId>>>;
    /**
     * This will modify the slice. This removes all entities of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.removeAll}
     */
    removeAll: CaseReducer<TSliceState, PayloadAction>;
    /**
     * This will reset the slice to its initial state.
     */
    reset: CaseReducer<TSliceState, PayloadAction>;
    /**
     * This will modify the slice. This sets all entities of the slice and sets lastModified.
     * @see {@link @reduxjs/toolkit#EntityStateAdapter.setAll}
     */
    setAll: CaseReducer<TSliceState, PayloadAction<Array<TEntity> | Record<EntityId, TEntity>>>;
}

/**
 * @public
 */
export declare interface IEntitySliceSelectors<TGlobalState, TEntity, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends EntitySelectors<TEntity, TGlobalState>, ISliceSelectors<TGlobalState, IEntityState<TEntity, TStatusEnum, TError>>, IMetaSliceSelectors<TGlobalState, IEntityState<TEntity, TStatusEnum, TError>, TStatusEnum, TError> {
}

/**
 * @public
 */
export declare interface IEntityState<T, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends EntityState<T>, IMetaState<TStatusEnum, TError> {
}

declare interface IMetaSliceReducers<TSliceState, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends SliceCaseReducers<TSliceState> {
    /**
     * This will set the status of the slice.
     */
    setStatus: CaseReducer<TSliceState, PayloadAction<TStatusEnum>>;
    /**
     * This will set the error of the slice.
     */
    setError: CaseReducer<TSliceState, PayloadAction<TError | null>>;
    /**
     * This will set any meta state properties that are defined
     */
    setMetaState: CaseReducer<TSliceState, PayloadAction<Partial<Pick<IMetaState<TStatusEnum, TError>, 'error' | 'status'>>>>;
}

/**
 * @public
 * @see IMetaState
 */
export declare interface IMetaSliceSelectors<TGlobalState, TSliceState, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> {
    /**
     * This selects the status of the slice.
     * @see {@link IMetaState.status}
     */
    selectStatus: IOutputSelector<TGlobalState, TSliceState, TStatusEnum>;
    /**
     * This selects the error of the slice.
     * @see {@link IMetaState.error}
     */
    selectError: IOutputSelector<TGlobalState, TSliceState, TError | null>;
    /**
     * This selects the lastModified of the slice.
     * @see {@link IMetaState.lastModified}
     */
    selectLastModified: IOutputSelector<TGlobalState, TSliceState, string | null>;
    /**
     * This selects the status of the slice.
     * @see {@link IMetaState.lastHydrated}
     */
    selectLastHydrated: IOutputSelector<TGlobalState, TSliceState, string | null>;
}

/**
 * @public
 */
export declare interface IMetaState<TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> {
    /**
     * The status of the slice.
     * @defaultValue `StatusEnum`
     */
    status: TStatusEnum;
    /**
     * The status of the slice.
     * @defaultValue `SerializedError`
     */
    error: TError | null;
    /**
     * The timestamp of the last time the slice was modified.
     */
    lastModified: string | null;
    /**
     * The timestamp of the last time the slice was hydrated.
     */
    lastHydrated: string | null;
}

/**
 * @public
 */
export declare type IModelSlice<TGlobalState, TModel, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> = ISlice<TGlobalState, IModelState<TModel, TStatusEnum, TError>, IModelSliceReducers<IModelState<TModel, TStatusEnum, TError>, TModel, TStatusEnum, TError>, IModelSliceSelectors<TGlobalState, TModel, TStatusEnum, TError>>;

/**
 * @public
 */
export declare interface IModelSliceReducers<TSliceState, TModel, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends IMetaSliceReducers<TSliceState, TStatusEnum, TError> {
    /**
     * This will hydrate the slice. This sets the model and lastHydrated, and resets lastModified.
     */
    hydrate: CaseReducer<TSliceState, PayloadAction<TModel>>;
    /**
     * This will modify the slice. This updates the model and sets lastModified.
     */
    update: CaseReducer<TSliceState, PayloadAction<Partial<TModel>>>;
    /**
     * This will modify the slice. This sets the model and sets lastModified.
     */
    set: CaseReducer<TSliceState, PayloadAction<TModel>>;
    /**
     * This will reset the slice to its initial state.
     */
    reset: CaseReducer<TSliceState, PayloadAction>;
}

/**
 * @public
 */
export declare interface IModelSliceSelectors<TGlobalState, TModel, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends ISliceSelectors<TGlobalState, IModelState<TModel, TStatusEnum, TError>>, IMetaSliceSelectors<TGlobalState, IModelState<TModel, TStatusEnum, TError>, TStatusEnum, TError> {
    /**
     * This selects the slice model.
     */
    selectModel: IOutputSelector<TGlobalState, IModelState<TModel, TStatusEnum, TError>, TModel>;
}

/**
 * @public
 */
export declare interface IModelState<T, TStatusEnum extends keyof typeof StatusEnum | string = keyof typeof StatusEnum, TError extends SerializedError = SerializedError> extends IMetaState<TStatusEnum, TError> {
    /**
     * The model of the slice.
     */
    model: T;
}

/**
 * @public
 */
declare type IOutputSelector<TGlobalState, TSliceState, TResult> = OutputSelector<[Selector<TGlobalState, TSliceState>], TResult, (sliceState: TSliceState) => TResult>;

/**
 * @public
 */
declare type ISelectCanRequest<TSliceState> = (sliceState: TSliceState) => boolean;

/**
 * @public
 */
declare type ISelectShouldRequest<TSliceState> = (sliceState: TSliceState, canRequest: ReturnType<ISelectCanRequest<TSliceState>>) => boolean;

/**
 * @public
 */
declare type ISelectSliceState<TGlobalState, TSliceState> = (globalState: TGlobalState) => TSliceState;

/**
 * @public
 */
export declare interface ISlice<TGlobalState, TSliceState, TCaseReducers extends SliceCaseReducers<TSliceState>, TSliceSelectors extends ISliceSelectors<TGlobalState, TSliceState>> {
    name: ISliceName<TGlobalState>;
    reducer: Reducer<TSliceState>;
    actions: CaseReducerActions<TCaseReducers>;
    selectors: TSliceSelectors;
}

/**
 * @internal
 */
declare type ISliceName<TGlobalState> = keyof TGlobalState & string;

/**
 * @public
 */
declare interface ISliceOptions<TGlobalState, TSliceState> {
    /**
     * The name of the slice in the redux store.
     */
    name: ISliceName<TGlobalState>;
    /**
     * @see {@link ISliceSelectors.selectSliceState}
     */
    selectSliceState: ISelectSliceState<TGlobalState, TSliceState>;
    /**
     * @see {@link ISliceSelectors.selectCanRequest}
     */
    selectCanRequest?: ISelectCanRequest<TSliceState>;
    /**
     * @see {@link ISliceSelectors.selectShouldRequest}
     */
    selectShouldRequest?: ISelectShouldRequest<TSliceState>;
    /**
     * The initial state of the slice.
     */
    initialState?: Partial<TSliceState>;
    /**
     * The function for mapping a `Date` to a timestamp.
     */
    createTimestamp?: (datetime?: Date) => string;
}

/**
 * @public
 */
export declare interface ISliceSelectors<TGlobalState, TSliceState> {
    /**
     * @public
     * Selects the entire slice state.
     */
    selectSliceState: IOutputSelector<TGlobalState, TSliceState, TSliceState>;
    /**
     * @defaultValue (overrideable) The slice can request if it is not requesting, error is empty, and the slice has not been modified
     */
    selectCanRequest: IOutputSelector<TGlobalState, TSliceState, boolean>;
    /**
     * @defaultValue (overrideable) The slice should request if it can request and it has not been hydrated
     * @see {@link ISliceSelectors.selectCanRequest}
     */
    selectShouldRequest: IOutputSelector<TGlobalState, TSliceState, boolean>;
}

/**
 * @public
 */
export declare enum StatusEnum {
    Settled = "Settled",
    Requesting = "Requesting",
    Failed = "Failed"
}

export { }
