type HttpMethod =
    | 'GET'
    | 'get'
    | 'POST'
    | 'post'
    | 'PUT'
    | 'put'
    | 'DELETE'
    | 'delete'
    | 'CONNECT'
    | 'connect'
    | 'OPTIONS'
    | 'options'
    | 'TRACE'
    | 'trace';
interface RequestOptions {
    baseUrl?: string;
    url?: string;
    isLoading?: boolean;
    isShowMsg?: boolean;
    data?: Record<string, any>;
    method?: HttpMethod;
    header?: Record<string, string>;
    params?: Record<string, any>;
    timeout?: number,
    dataType?: string,
    responseType?: string,
    sslVerify?: boolean,
    withCredentials?: boolean,
    firstIpv4?: boolean,
    enableHttp2?: boolean,
    enableQuic?: boolean,
    enableCache?: boolean,
    enableHttpDNS?: boolean,
    httpDNSServiceId?: string,
    enableChunked?: boolean,
    forceCellularNetwork?: boolean,
    enableCookie?: boolean,
    cloudCache?: object | boolean,
    defer?: boolean,
    interceptor?: {
        request?: RequestInterceptor,
        response?: ResponseInterceptor
    }
}

interface UploadOptions {
    url: string,
    baseUrl?: string,
    files?: File[],
    fileType?: 'image' | 'video' | 'audio',
    file?: File,
    filePath?: string,
    name?: string,
    header?: object,
    timeout?: number,
    formData?: object,
    interceptor?: {
        request?: (config: UploadOptions) => UploadOptions,
        response?: (response: any) => any;
    }

}

type RequestInterceptor = (config: RequestOptions) => RequestOptions;
type ResponseInterceptor = (response: any) => any;

class uniRequest {
    baseUrl?: string;
    isLoading: boolean;
    isShowMsg: boolean;
    defaultHeader: Record<string, string>;
    interceptors: { request?: RequestInterceptor; response?: ResponseInterceptor };

    constructor(request: RequestOptions) {
        this.isLoading= request.isLoading??true
        this.isShowMsg= request.isShowMsg??true
        this.baseUrl = request.baseUrl;
        this.defaultHeader = {
            "Content-Type": "application/json;charset=UTF-8",
            ...request.header,
        };
        this.interceptors = {request: request.interceptor?.request, response: request.interceptor?.response};
    }

    setBaseUrl(baseUrl: string): void {
        this.baseUrl = baseUrl;
    }

    setDefaultHeader(header: Record<string, string>): void {
        this.defaultHeader = header;
    }
    static created(options: RequestOptions) {
        return new uniRequest(options);
    }
    request(options: RequestOptions): Promise<any> {
        options = this.buildRequestOptions(options)
        options = options || {};
        options.isLoading ??= this.isLoading;
        options.isShowMsg ??= this.isShowMsg;
        options.baseUrl = options.baseUrl || this.baseUrl;
        options.url = `${options.baseUrl}${options.url}`;
        options.data = options.data || {};
        options.method = (options.method?.toUpperCase() || "GET") as HttpMethod;
        options.header = options.header || this.defaultHeader;
        if (typeof options.interceptor?.request === 'function') {
            options = options.interceptor.request(options);
        } else if (typeof this.interceptors?.request === 'function') {
            options = this.interceptors.request(options);
        }
        return new Promise((resolve, reject) => {
            // @ts-ignore
            uni.request({
                ...options,
                success: (res) => {
                    const response = this.handleResponse(res, options);
                    resolve(response);
                },
                fail: (err) => {
                    const error = this.handleError(err);
                    reject(error);
                },
            });
        });
    }

    private handleResponse(response: any, options: RequestOptions): any {
        if (options.interceptor?.response) {
            response = options.interceptor?.response(response);
        } else if (this.interceptors?.response) {
            response = this.interceptors?.response(response);
        }
        return response
    }
    private handleUploadResponse(res: any, options: UploadOptions){
        if (options.interceptor?.response){
            res = options.interceptor?.response(res);
        }
        return res
    }
    private handleError(error: any): any {
        throw error;
    }

    setRequestInterceptor(interceptor: RequestInterceptor): void {
        this.interceptors.request = interceptor;
    }

    setResponseInterceptor(interceptor: ResponseInterceptor): void {
        this.interceptors.response = interceptor;
    }

    get(options: RequestOptions): Promise<any> {
        options = options || {};
        options.method = "GET";
        return this.request(this.buildRequestOptions(options));
    }
    upload(options: UploadOptions) {
        options.url = `${options.baseUrl ?? this.baseUrl}${options.url}`;
        if (typeof options.interceptor?.request==='function'){
            options = options.interceptor.request(options);
        }
        return new Promise((resolve, reject) => {
            uni.uploadFile({
                ...options,
                success: (res) => {
                    resolve(this.handleUploadResponse(res, options))
                },
                fail(res){
                    reject(res)
                }
            });
        })
    }

    private buildRequestOptions(options: RequestOptions): RequestOptions {
        if (options.params && Object.keys(options.params).length > 0) {
            const queryString = Object.keys(options.params)
                .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(options.params![key])}`)
                .join('&');
            options.url += `?${queryString}`;
        }
        delete options.params;
        return options;
    }

    post(options: RequestOptions): Promise<any> {
        options = options || {};
        options.method = "POST";
        return this.request(this.buildRequestOptions(options));
    }

    put(options: RequestOptions): Promise<any> {
        options = options || {};
        options.method = "PUT";
        return this.request(this.buildRequestOptions(options));
    }

    delete(options: RequestOptions): Promise<any> {
        options = options || {};
        options.method = "DELETE";
        return this.request(this.buildRequestOptions(options));
    }
}

export {uniRequest}