import * as _angular_core from '@angular/core';
import { Observable, mergeMap } from 'rxjs';

type RequestType = 'create' | 'update' | 'remove';
type Behavior = 'concat' | 'merge' | 'switch' | 'exhaust';
/**
 *  Strategy for handling resource requests
 *
 * - `'pessimistic'` - wait for server response, then reload the entire collection
 * - `'optimistic'` - update UI immediately and assume success
 * - `'incremental'` - patch returned item into the existing collection after server confirms
 */
type Strategy = 'optimistic' | 'pessimistic' | 'incremental';
interface RestResourceOptions<T, ID> {
    /**
     * Whether to log verbose information to the console.
     * This is useful for debugging and understanding the resource's behavior.
     *
     * @default false
     */
    verbose?: boolean;
    /**
     * A reactive function which determines the request to be made.
     * Whenever the params change, the loader will be triggered to fetch a new value for the resource.
     *
     * (works the same way as Angular's `resource`)
     *
     * @returns A string containing URL query parameters or undefined
     */
    params?: () => string | undefined;
    /**
     * Function to extract the ID from an item when the ID is not stored in the `id` field.
     * Used to identify items for update and remove operations.
     *
     * @param item The item from which to extract the ID
     * @returns The ID of the item
     */
    idSelector?: (item: T) => ID;
    /**
     * The default strategy for the resource for every request type,
     * it can be overridden by the request-type specific strategy
     *
     * - `'pessimistic'` - wait for server response, then reload the entire collection
     * - `'optimistic'` - update UI immediately and assume success
     * - `'incremental'` - patch returned item into the existing collection after server confirms
     *
     * @default 'pessimistic'
     */
    strategy?: Strategy;
    create?: {
        /**
         * Defines how create requests are handled when multiple requests are made.
         * Controls the RxJS flattening operator used for the HTTP request.
         *
         * @default 'concat'
         */
        behavior?: Behavior;
        /**
         * Determines whether changes are applied optimistically (before server confirmation)
         * or pessimistically (after server confirmation) for create operations.
         *
         * @default The value of the global strategy option
         */
        strategy?: Strategy;
        /**
         * Optionally generate a new item ID (if the target API does not handle this)
         * and set it on the item.
         *
         * @property generator A function that returns a new ID (e.g., UUID or auto-increment).
         *                     Used when the backend does not assign one automatically.
         *
         * @property setter (Optional) A function that sets the generated ID onto the item,
         *                  especially useful when the item’s ID field is not named `id`.
         *
         * @example
         * {
         *   id: {
         *     generator: () => uuid(),
         *     setter: (id, item) => {
         *       item.nonstandardId = id
         *     }
         *   }
         * }
         *
         */
        id?: {
            /**
             * A function that generates a new unique ID.
             * @example
             * () => uuid()
             */
            generator: () => ID;
            /**
             * (Optional) Custom logic for assigning the generated ID to the item.
             *
             * @param id The generated ID
             * @param item The item object to modify
             *
             * @example
             * setter: (id, item) => {
             *   item.nonstandardId = id
             * }
             */
            setter?: (id: ID, item: Partial<T>) => void;
        };
    };
    update?: {
        /**
         * Defines how update requests are handled when multiple requests are made.
         * Controls the RxJS flattening operator used for the HTTP request.
         *
         * @default 'concat'
         */
        behavior?: Behavior;
        /**
         * Determines whether changes are applied optimistically (before server confirmation)
         * or pessimistically (after server confirmation) for update operations.
         *
         * @default The value of the global strategy option
         */
        strategy?: Strategy;
    };
    remove?: {
        /**
         * Defines how remove requests are handled when multiple requests are made.
         * Controls the RxJS flattening operator used for the HTTP request.
         *
         * @default 'concat'
         */
        behavior?: Behavior;
        /**
         * Determines whether changes are applied optimistically (before server confirmation)
         * or pessimistically (after server confirmation) for remove operations.
         *
         * @default The value of the global strategy option
         */
        strategy?: Strategy;
    };
}
declare const LOG_PREFIX = "[@angular-experts/resource]";

declare function restResource<T, ID, E extends Error = Error>(apiEndpoint: string, options?: RestResourceOptions<T, ID>): {
    loadingInitial: _angular_core.Signal<boolean>;
    loading: _angular_core.Signal<boolean>;
    loadingCreate: _angular_core.WritableSignal<boolean>;
    loadingUpdate: _angular_core.WritableSignal<boolean>;
    loadingRemove: _angular_core.WritableSignal<boolean>;
    errorRead: _angular_core.Signal<Error | undefined>;
    errorCreate: _angular_core.WritableSignal<E | undefined>;
    errorUpdate: _angular_core.WritableSignal<E | undefined>;
    errorRemove: _angular_core.WritableSignal<E | undefined>;
    /**
     * The value of the resource, ONLY if the resource is a single item.
     *
     * This is useful when building a resource to manage a single entity, eg detail view.
     *
     * @returns Signal of single item or undefined (also returns undefined if resource has more than one item)
     */
    value: _angular_core.Signal<T | undefined>;
    /**
     * The values of the resource (eg list of 0 to n items).
     *
     * @returns Signal of a list of items or undefined (also returns undefined if resource has no items)
     */
    values: _angular_core.WritableSignal<T[] | undefined>;
    hasValue: _angular_core.Signal<boolean>;
    hasValues: _angular_core.Signal<boolean>;
    reload: () => boolean;
    create: (item: Partial<T>) => void;
    update: (item: T) => void;
    remove: (item: T) => void;
    destroy: () => void;
};

declare function streamify<T extends unknown[]>(impl: (stream: Observable<T>) => Observable<unknown>): (...args: T) => void;
declare function behaviorToOperator(behavior?: Behavior): typeof mergeMap;

export { LOG_PREFIX, behaviorToOperator, restResource, streamify };
export type { Behavior, RequestType, RestResourceOptions, Strategy };
