import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';

import { CompressMethod } from '@/config/types';

// request 方法 opts 参数的接口
export interface IRequestOptions extends AxiosRequestConfig {
    requestInterceptors?: IRequestInterceptorTuple[];
    responseInterceptors?: IResponseInterceptorTuple[];
    [key: string]: any;
}

export interface IRequestOptionsWithResponse extends IRequestOptions {
    getResponse: true;
}

export interface IRequestOptionsWithoutResponse extends IRequestOptions {
    getResponse: false;
}

export interface IRequest {
    <T = any>(url: string, opts: IRequestOptionsWithResponse): Promise<AxiosResponse<T>>;
    <T = any>(url: string, opts: IRequestOptionsWithoutResponse): Promise<T>;
    <T = any>(url: string, opts?: IRequestOptions): Promise<T>; // getResponse 默认是 false， 因此不提供该参数时，只返回 data，不提供 opts 时，默认使用 'GET' method，并且默认返回 data
}

export type RequestError = AxiosError | Error;

export interface IErrorHandler {
    (error: RequestError, opts: IRequestOptions): void;
}
export type WithPromise<T> = T | Promise<T>;

export type IRequestInterceptorAxios = (config: IRequestOptions) => WithPromise<IRequestOptions>;
export type IRequestInterceptorRequest = (
    url: string,
    config: IRequestOptions
) => WithPromise<{ url: string; options: IRequestOptions }>;
export type IRequestInterceptor = IRequestInterceptorAxios | IRequestInterceptorRequest;
export type IErrorInterceptor = (error: Error) => Promise<Error>;
export type IResponseInterceptor = <T = any>(
    response: AxiosResponse<T>
) => WithPromise<AxiosResponse<T>>;
export type IRequestInterceptorTuple =
    | [IRequestInterceptor, IErrorInterceptor]
    | [IRequestInterceptor]
    | IRequestInterceptor;
export type IResponseInterceptorTuple =
    | [IResponseInterceptor, IErrorInterceptor]
    | [IResponseInterceptor]
    | IResponseInterceptor;

export interface RequestConfig<T = any> extends IRequestOptions {
    skipErrorHandler?: boolean;
    encryption?: {
        enabled: boolean;
        key: string;
        compressMethod?: CompressMethod;
    };
    errorConfig?: {
        errorHandler?: IErrorHandler;
        errorThrower?: (res: T) => void;
    };
}
