import { TaskQueue } from 'id-queue';

declare type CachedData = {
    value: any;
    expiresAt: number;
};

declare type CacheRequestor<P extends boolean, S extends boolean> = {
    key?: (config: UnifiedConfig) => string;
    duration?: Duration;
    sync?: S;
    persist?: P;
    name?: [P, S] extends [true, false] ? string : undefined;
    isValid?: S extends true ? (params: IsValidParams) => boolean : (params: IsValidParams) => boolean | Promise<boolean>;
};

declare class ConcurrentPool<T extends any = any> {
    parallelCount: number;
    tasks: TaskItemList<T>;
    runningCount: number;
    constructor(parallelCount?: number);
    add(id: string, task: Task<T>): Promise<unknown>;
    remove(id: string): void;
    execute(currentTask: TaskItem<T>): Promise<void>;
    _run(): void;
}

declare type Duration = number | (({ key, config, response }: {
    key: string;
    config: UnifiedConfig;
    response: any;
}) => number);

export declare const inject: (requestor: UnifiedRequestor, instanceKey?: string) => void;

declare type IsValidParams = {
    key: string;
    config: UnifiedConfig;
    cachedData: CachedData;
};

declare interface RequestConfig<D = any> {
    url?: string;
    method?: Exclude<keyof Requestor, "request">;
    baseURL?: string;
    headers?: Record<string, any>;
    params?: Record<string, any> | string;
    data?: D;
    timeout?: number;
    onUploadProgress?: <P extends ProgressEvent>(progressEvent: P) => void;
    onDownloadProgress?: <P extends ProgressEvent>(progressEvent: P) => void;
    validateStatus?: ((status: number) => boolean) | null;
    signal?: AbortSignal;
}

export declare const requestExtender: {
    cacheRequestor: <P extends boolean = false, S extends boolean = false>(config?: CacheRequestor<P, S>) => {
        requestor: Requestor;
        store: {
            has(key: string): boolean;
            get<T>(key: string): T | undefined;
            set<T>(key: string, value: T): T;
            remove(key: string): void;
            clear(): void;
        } | {
            has: (key: string) => Promise<boolean>;
            get: <T>(key: string) => Promise<T | null>;
            set: <T>(key: string, value: T) => Promise<T>;
            remove: (key: string) => Promise<void>;
            clear: () => Promise<void>;
        } | {
            has: (key: string) => boolean;
            get: (key: string) => any;
            set: <T>(key: string, value: T) => Map<string, any>;
            remove: (key: string) => boolean;
            clear: () => void;
        };
    };
    idempotencyRequestor: (genKey?: (config: UnifiedConfig) => string) => {
        requestor: Requestor;
    };
    retryRequestor: (config?: RetryOptions) => {
        requestor: Requestor;
    };
    concurrentPoolRequestor: (config?: {
        parallelCount?: number;
        createId?: (config: UnifiedConfig) => string;
    } & RetryOptions) => {
        requestor: Requestor;
        concurrentPool: ConcurrentPool<any>;
    };
    syncRequestor: (config?: {
        persist?: false;
        sync?: true;
    } & CacheRequestor<false, true>) => {
        requestor: Requestor;
    };
};

export declare interface Requestor {
    get<R = any, D = any>(url: string, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
    post<R = any, D = any>(url: string, data?: D, config?: RequestConfig<D>): Promise<R>;
    delete<R = any, D = any>(url: string, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
    put<R = any, D = any>(url: string, data?: D, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
    request<R = any, D = any>(config: WithDynamicProps<UnifiedConfig<D>>): Promise<R>;
}

declare type RetryOptions = {
    retries?: number;
    delay?: number | ((attempt: number) => number);
    retryCondition?: (error: any) => boolean;
};

declare type Task<T> = () => Promise<T>;

declare type TaskItem<T> = {
    task: Task<T>;
    resolve: (value: T) => void;
    reject: (reason: any) => void;
};

declare type TaskItemList<T> = TaskQueue<TaskItem<T>>;

export declare type UnifiedConfig<D = any> = RequestConfig<D> & {
    url: string;
    method: Exclude<keyof Requestor, "request">;
};

export declare type UnifiedRequestor = <R = any, D = any>(config: UnifiedConfig<D>) => Promise<R>;

export declare const useRequestor: (instanceKey?: string) => Requestor;

declare type WithDynamicProps<T, V = any> = T & Record<string, V>;

export { }
