import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';

/**
 * Represents an in-flight HTTP request to load some data.
 *
 * A <code>value</code> may be provided to represent the previously-known value,
 * or a default value to display whilst loading is in progress (e.g., for a
 * reload operation, or a list of values loaded so far in an infinite scrolling
 * UI).
 *
 */
interface LoadingState<T> {
    readonly isLoading: true;
    readonly value?: T;
    readonly error: undefined;
}
/**
 * Represents an in-flight HTTP request to load some data, with some existing
 * known data already present.
 *
 * Useful for loading states with a default value to render while waiting, or
 * to represent a loading state with previously-loaded data when using a
 * 'load more' (e.g., infinite scroll) pattern.
 */
interface LoadingStateWithValue<T> extends LoadingState<T> {
    readonly value: T;
}
/**
 * Represents a successfully-completed HTTP request, with the given data.
 *
 * A <code>value</code> may be omitted if there is no data to display and such
 * a scenario is not considered an error condition.
 */
interface LoadedState<T> {
    readonly isLoading: false;
    readonly value: T;
    readonly error: undefined;
}
/**
 * Represents an unsuccessful HTTP request with the failure details given in the
 * <code>error</code> property.
 *
 * A <code>value</code> may be set to represent a last-known value, or similar.
 */
interface ErrorState<T> {
    readonly isLoading: false;
    readonly value?: T;
    readonly error: HttpErrorResponse | Error;
}
/**
 * Represents an unsuccessful HTTP request with the failure details given in the
 * <code>error</code> property, with some pre-existing known data already present.
 *
 * Useful for error states with a fallback value to render, or to represent an
 * error state with previously-loaded data when using a 'load more' (e.g.,
 * infinite scroll) pattern.
 */
interface ErrorStateWithValue<T> extends ErrorState<T> {
    readonly value: T;
}
/**
 * Representation of the state of a data-loading operation.
 *
 * The various fields are readonly as this is meant to be used to represent an
 * immutable snapshot of the current state in a stream of state change events.
 */
type HttpRequestState<T> = LoadingState<T> | LoadedState<T> | ErrorState<T>;

/**
 * Returns a new LoadingState instance with an optional last-known value.
 *
 * @param value may be provided to indicate the previously-loaded, or last-known, state
 */
declare function loadingState<T>(value: T): LoadingStateWithValue<T>;
declare function loadingState<T = never>(): LoadingState<T>;
/**
 * Returns a new LoadedState instance, with the optional value as the loaded data.
 *
 * @param value
 */
declare function loadedState<T>(value: T): LoadedState<T>;
/**
 * Returns a new ErrorState instance with the given error and optional last-known value.
 *
 * @param error
 * @param value may be provided to indicate the previously-loaded, or last-known, state
 */
declare function errorState<T>(error: HttpErrorResponse | Error, value: T): ErrorStateWithValue<T>;
declare function errorState<T = never>(error: HttpErrorResponse | Error): ErrorState<T>;

declare function isLoadingState<T>(state?: LoadingState<T> | ErrorState<unknown> | LoadedState<unknown>): state is LoadingState<T>;
declare function isLoadedState<T>(state?: LoadedState<T> | LoadingState<unknown> | ErrorState<unknown>): state is LoadedState<T>;
declare function isErrorState<T>(state?: ErrorState<T> | LoadedState<unknown> | LoadingState<unknown>): state is ErrorState<T>;

declare function httpRequestStates<T>(): (source: Observable<HttpResponse<T> | T>) => Observable<HttpRequestState<T>>;
/**
 *
 * @param mapResponse a function to map from HttpResponse<T> to K
 * @deprecated use rxjs's map() operator to transform the response prior to using httpRequestStates() instead.
 */
declare function httpRequestStates<T, K>(mapResponse: (result: HttpResponse<T>) => K): (source: Observable<HttpResponse<T>>) => Observable<HttpRequestState<K>>;
/**
 *
 * @param mapResponse a function to map from T to K
 * @deprecated use rxjs's map() operator to transform the value prior to using httpRequestStates() instead.
 */
declare function httpRequestStates<T, K>(mapResponse: (result: T) => K): (source: Observable<T>) => Observable<HttpRequestState<K>>;

/**
 * Given an array of HttpRequestState<T>, merge them together into a new single HttpRequestState<T>,
 * based on a supplied merging strategy of values (mergeValues) or of errors (mergeErrors).
 *
 * One can think of this function as allowing one to act "inside" the states, directly on the values or errors.
 *
 * Use this with combineLatest, instead of forkJoin, to get loading updates.
 *
 * @param states Array of states of the same type that should be merged together.
 * @param mergeValues Handles how to merge values together when all states have loaded.
 * @param mergeErrors Handles how to merge errors together in case one or more of the states end up in ErrorState.
 *    If not specified, the first error is simply used as the error of the merged state
 * @returns The merged HttpRequestState<T>
 */
declare function mergeStates<T>(states: HttpRequestState<T>[], mergeValues: (states: LoadedState<T>['value'][]) => LoadedState<T>['value'], mergeErrors?: (states: ErrorState<T>['error'][]) => ErrorState<T>['error']): HttpRequestState<T>;

export { errorState, httpRequestStates, isErrorState, isLoadedState, isLoadingState, loadedState, loadingState, mergeStates };
export type { ErrorState, ErrorStateWithValue, HttpRequestState, LoadedState, LoadingState, LoadingStateWithValue };
