import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus';
import { getToken, cleanSession } from '@/utils/auth';
import { LoginParm } from '@/api/user/UserModel';
import qs from 'qs'
import { UploadFileParams } from '@/types/utils';
import { isNumber } from '@/utils/is';
import { h } from 'vue';
import ErrorTable from './ErrorTable';
//返回值类型
export interface Result<T = any> {
    code: number;
    msg: string;
    data: T;
}
//返回的状态码
export enum StatusCode {
    NoAuth = 600,//无权限
    Success = 200, //返回成功
    ErrorTable = 20000//错误表格信息
}
class request {
    private instance: AxiosInstance; //axios实例
    //构造函数 给instance进行初始化
    constructor(config: AxiosRequestConfig) {
        //创建axios实例
        this.instance = axios.create(config);
        //拦截器配置
        this.interceptors();
    }
    //拦截器
    private interceptors() {
        //请求发送之前的拦截器：添加token
        // @ts-ignore
        this.instance.interceptors.request.use((config: AxiosRequestConfig) => {
            //配置token
            let token = getToken();
            //如果token存在，放到请求的头部
            if (token) {
                config.headers = {
                    ...config.headers,
                    token: token
                }
            }
            return config;
        }, (error: any) => {
            // 错误抛到业务代码
            error.data = {}
            error.data.msg = '服务器异常，请联系管理员！'
            return error
        })

        //请求返回之后的处理
        // @ts-ignore
        this.instance.interceptors.response.use((res: AxiosResponse) => {
            if (res && res.data) {
                const data = res.data;
                //后端接口会返回一些状态,code是根据后端定义的字段来写的
                if (data.code == StatusCode.NoAuth) { //token过期
                    //跳到登录
                    window.location.href = "/";
                    //清除缓存数据
                    cleanSession();
                    //跳转到登录
                } else if (data.code == StatusCode.Success || res.config.responseType === 'arraybuffer' || res.config.responseType === 'blob') { //返回成功
                    return res
                } else if(data.code === StatusCode.ErrorTable) {
                    ElMessage({
                        customClass: 'define-error',
                        message: () => h(ErrorTable, { dataSource: data.data })
                    })
                    return res || null;
                } else {
                    ElMessage.error(data.msg || '服务器出错!')
                    return res || null;
                }
            }
        }, (error) => {
            if (error && error.response) {
                error.data = {};
                switch (error.response.status) {
                    case 400:
                        error.data.msg = '错误请求';
                        ElMessage.error(error.data.msg)
                        break
                    case 401:
                        error.data.msg = '未授权，请重新登录';
                        ElMessage.error(error.data.msg)
                        break
                    case 403:
                        error.data.msg = '拒绝访问';
                        ElMessage.error(error.data.msg)
                        break
                    case 404:
                        error.data.msg = '请求错误,未找到该资源';
                        ElMessage.error(error.data.msg)
                        break
                    case 405:
                        error.data.msg = '请求方法未允许';
                        ElMessage.error(error.data.msg)
                        break
                    case 408:
                        error.data.msg = '请求超时';
                        ElMessage.error(error.data.msg)
                        break
                    case 500:
                        error.data.msg = '服务器端出错';
                        ElMessage.error(error.data.msg)
                        break
                    case 501:
                        error.data.msg = '网络未实现';
                        ElMessage.error(error.data.msg)
                        break
                    case 502:
                        error.data.msg = '网络错误';
                        ElMessage.error(error.data.msg)
                        break
                    case 503:
                        error.data.msg = '服务不可用';
                        ElMessage.error(error.data.msg)
                        break
                    case 504:
                        error.data.msg = '网络超时';
                        ElMessage.error(error.data.msg)
                        break
                    case 505:
                        error.data.msg = 'http版本不支持该请求';
                        ElMessage.error(error.data.msg)
                        break
                    default:
                        error.data.msg = `连接错误${error.response.status}`;
                        ElMessage.error(error.data.msg)
                }
            } else {
                error.data.msg = "连接到服务器失败";
                ElMessage.error(error.data.msg)
            }
            return Promise.reject(error)

        })
    }

    get<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.get<T>(url, {
                params: parms,
                paramsSerializer: (parms) => {
                    return qs.stringify(parms)
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    //参数的处理
    getParms(parms: any) {
        let _parms = '';
        if (Object.is(parms, undefined || null)) {
            _parms = '';
        }
        else {
            for (const key in parms) {
                if (parms.hasOwnProperty(key) && parms[key]) {
                    _parms += `${parms[key]}/`
                }
            }
        }
        if (_parms) _parms = _parms.substr(0, _parms.length - 1);
        return _parms;
    }

    getRestApi<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.get<T>(this.getParms(parms) ? `${url}/${this.getParms(parms)}` : url)
                .then((res) => {
                    resolve(res.data as any)
                }).catch((error) => {
                    reject(error)
                })
        })
    }

    //post请求
    post<T = any>(url: string, parms: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post(url, parms, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    // 上传
    uploadFile<T = any>(url: string, params: UploadFileParams): Promise<Result<T>> {
        const formData = new window.FormData();
        const customFilename = params.name || 'file';

        if (params.filename) {
            if (params.files && params.files.length > 0) {
                params.files.forEach(file => {
                    formData.append(`${params.filename}`, file);
                })
            } else {
                params.file && formData.append(customFilename, params.file, params.filename);
            }
        } else {
            params.file && formData.append(customFilename, params.file);
        }
        if (params.data) {
            Object.keys(params.data).forEach((key) => {
                const value = params.data![key]
                if(isNumber(value) || value) {
                    formData.append(key, params.data![key]);
                }
            });
        }
        return new Promise((resolve, reject) => {
            this.instance.request({
                url,
                data: formData,
                method: "POST",
                headers: {
                    'Content-Type': 'multipart/form-data;charset=UTF-8'
                }
            }).then((res) => {
                resolve(res.data as Result<T>)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    //下载文件
    download<T extends any>(url: string, data?: T): Promise<T> {
        return new Promise((resolve, reject) => {
            this.instance.post<T>(url, data, {
                responseType: "blob",
            }).then(res => {
                resolve(res as T)
            }).catch(error => {
                reject(error)
            })
        })
    }

    //put请求
    put<T = any>(url: string, parms: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.put(url, parms, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    //删除
    // {userId : 10}  ==>  10
    //http:localhost:8080/api/deleteById/10
    delete<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.delete<T>(this.getParms(parms) ? `${url}/${this.getParms(parms)}` : url)
                .then((res) => {
                    resolve(res.data as any)
                }).catch((error) => {
                    reject(error)
                })
        })
    }

    //获取验证码
    getImage(url: string) {
        return this.instance.post(url, null, {
            responseType: 'arraybuffer'
        })
    }
    //登录
    login<T = any>(url: string, parms: LoginParm): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post<T>(url, parms, {
                transformRequest: [(parms) => {
                    return qs.stringify(parms)
                }],
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }).then((res) => {
                resolve(res as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

}
export default request;