/*
 * @Description: axios统一调用工具类————升级版
 * @Author: liusheng
 * @Date: 2023-12-13 10:43:52
 */
import axios from 'axios'
import Constant from './CommonConst.js'
import DateUtil from './DateUtil.js'
import SysMaintain from '../utilcomp/sysmaintain/SysMaintain.vue'

// Vue实例，由init方法初始化
let vueInstance = null;

// IMS公告获取地址，由init方法初始化
let publicNoticeRequestUrl = null;

/*
 * Restful请求返回2012(未登录)时的回调函数，由init方法初始化
 * @param {object} vue Vue实例
 * @param {string} url 接口的请求地址
 * @return {boolean} true/false-通知VueRequest是否显示toast
 */
let notLoginCallbackFunc = null;

/*
 * Restful请求返回502时所调用的函数
 * @param {object} vue Vue实例
 * @return {boolean} true-已进行错误处理，false-未进行错误处理
 */
let http502CallbackFunc = async function trySysMaintainNotice(vue) {
    if (publicNoticeRequestUrl == null) return false;

    fetch(publicNoticeRequestUrl).then(res => {
        return res.json();
    }).then(result => {
        if (result.code != Constant.RESTFUL_SUCC || result.data == null) return false;

        let {content, timeRange} = {...result.data};
        vue.$router.push({ path: '/sysmaintain', query: {content: content, timeRange: timeRange} });
        return true;
    }).catch(err => {
        console.log(err);
        return false;
    });
};

/**
 * vueRequest初始化函数。各项目在调用vueRequest前，应首先调用本函数对其进行初始化
 * @Author: liusheng, fengal
 * @Date: 2022-04-03 08:43:12
 * @param {object} vue Vue实例
 * @param {function} notLoginCallback Restful请求返回2012(未登录)时的回调函数
 *     回调函数示例（以家庭医护为例）：
 *     function notLoginCallback(vue, _url) {
 *         let loginRouterParams = [Constant.SESSION_ADMIN_TYPE, Constant.SESSION_IDENTIFIER];
 *         vue.$router.push({ name: 'login', params: loginRouterParams });
 *         return false;
 *     }
 *     参数：{object} vue Vue实例
 *           {string} url 导致产生2012(未登录)的url地址
 *     返回值：{boolean} true/false-通知AppAxios是否显示toast
 * @param {string} router 路由实例
 * @param {string} publicNoticeUrl IMS公告获取地址
 */
function initVueRequest(vue, notLoginCallback, router, publicNoticeUrl) {
    vueInstance = vue;
    notLoginCallbackFunc = notLoginCallback;

    // 添加SysMaintain路由
    let newRoutes = [{
        path: '/sysmaintain',
        name: 'sysmaintain',
        component: SysMaintain,
    }]

    if (router != null && publicNoticeUrl != null) {
        router.options.routes.push(...newRoutes);
        router.addRoutes(newRoutes);
        publicNoticeRequestUrl = publicNoticeUrl;
    }

}

/*
 * @Description: AppAxios的"2012(未登录)"回调函数
 * @Author: liusheng
 * @Date: 2021-04-08 10:27:31
 * @param {object} vue Vue实例
 * @param {string} url 接口的请求地址
 * @return {boolean} true/false-通知AppAxios是否显示toast
 */
function notLoginCallback(vue, _url) {
    return false;
}

/**
 * Promise化函数。通用的restful接口请求方法，所有接口请求均须通过调用此方法来实现
 * @Author: liusheng
 * @Date: 2021-02-06 08:43:12
 * @param {object} request 接口请求数据对象，说明如下：<br/>
 * {<br/>
 * &emsp;method: GET|POST|PUT|DELETE，默认GET，<br/>
 * &emsp;url: string, url地址, <br/>
 * &emsp;header: object, HTTP请求头, <br/>
 * &emsp;param: object, 请求参数。当指定了files时，param应为{'key':参数名, 'value':object}，否则为普通object类型<br/>
 * &emsp;contentType: String MIME类型,<br/> 
 * &emsp;files: Map<参数名,文件对象> 与param同时上传的文件<br/> 
 * &emsp;showSuccMsg: boolean, true-请求成功时TOAST显示返回信息, 默认不显示<br/>
 * &emsp;showFailMsg: boolean, true-请求异常时TOAST显示返回信息, 默认显示<br/>
 * &emsp;loadingMsg: string, GET请求时非空则显示loading遮罩；对于POST/PUT/DELETE操作始终显示遮罩<br/>
 * }
 *
 * @return {resolve} 请求成功的数据(code=2000)，或null(code=2001)错误
 * @return {reject} 2009（用户不存在时，code=2009）
 * @return {reject} 2010（用户被禁用时，code=2010）
 * @return {redirect} 用户未登录(code=2012)，直接重定向到Login页面
 * @return {reject} 错误信息（网络请求错误时）
 */
let vueRequest = function restfulRequest(request) {
    request.method = request.method.toUpperCase();
    request.showSuccMsg = (request.showSuccMsg === true) ? true : false;
    request.showFailMsg = (request.showFailMsg === false) ? false : true;
    request.contentType == (!request.contentType && request.method != 'GET') 
        ? Constant.CNTYPE_JSON : request.contentType;
    console.log("Calling interface:", request.method, request.url);
   
    let loading = showLoadingMsg(request);
    let config = makeAxiosRequestConfig(request);
    return new Promise((resolve, reject) => {
        axios.request(config).then(response => {
            if (loading) loading.close();
            console.log(response.data.time, request.method, request.url, response.data);

            let result = processResult(request, response.data);
            switch (result.code) {
                case Constant.RESTFUL_LOGIN_USER_NOT_EXIST:
                    reject(Constant.RESTFUL_LOGIN_USER_NOT_EXIST);
                    return;
                case Constant.RESTFUL_FORBID_LOGIN:
                    reject(Constant.RESTFUL_FORBID_LOGIN);
                    return;
                default:
                    resolve((result.code == Constant.RESTFUL_SUCC) ? result : null);
                    return;
            }
        }).catch(err => {
            if (loading) loading.close();
            processError(request, err);
            reject(err);
        });
    });
}

function showLoadingMsg(request) {
    let showMsg = (request.loadingMsg || request.method != 'GET');
    return !showMsg ? null : vueInstance.$loading({
        lock: true,
        text: '正在请求服务器，请稍候',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.5)'
    });
}

function makeAxiosRequestConfig(request) {
    let config = { url: request.url, method: request.method, timeout: 120000 };
    switch (request.method) {
        case 'GET':
            config.params = request.param;
            break;
        case 'POST':
        case 'PUT':
        case 'DELETE':
            config.data = (request.files) 
                ? buildRequestPartData(request) 
                : buildRequestParamData(request);
            break;
    }
    config.headers = buildRequestHeader(request);
    return config;
}

/**
 * 构建RequestPart格式的请求数据（数据+文件同时上传）
 * @param {object} request 请求对象
 * @returns 构建后的请求数据
 */
function buildRequestPartData(request) {
    let data = new FormData();
    for (const [key, value] of request.files) {
        if (Array.isArray(value)) {
            for (let file of value) {
                data.append(key, file);
            }
        } else {
            data.append(key, value);
        }
    }
    request.contentType = Constant.CNTYPE_FORM_DATA;
    data.append(request.param.name, new Blob(
        [JSON.stringify(request.param.value)], {type: Constant.CNTYPE_JSON}));
    return data;
}

/**
 * 构建RequestParam格式的请求数据（根据contentType来决定数据格式）
 * @param {object} request 请求对象
 * @returns 构建后的请求数据
 */
function buildRequestParamData(request) {
    let data = null;
    switch (request.contentType) {
        case Constant.CNTYPE_JSON:
            data = request.param;
            break;
        case Constant.CNTYPE_URLENCODED: 
            if (request.param) {
                data = vueInstance.qs.stringify(request.param);
            }
            break;
        case Constant.CNTYPE_FORM_DATA:
            if (request.param) {
                data = new FormData();
                for (let key in request.param) {
                    data.append(key, request.param[key]);
                }
            }
            break;
    }
    return data;
}

function buildRequestHeader(request) {
	let curHeader = {};
    if (request.contentType) {
        curHeader['content-type'] = request.contentType;
    }
    
	if (request.header) {
		for (let name in request.header) {
			curHeader[name] = request.header[name];
		}
	}

	return curHeader;
}

/*
 * @Description: 处理接口请求返回结果
 * @Author: liusheng
 * @Date: 2021-02-06 09:02:14
 * @param {object} request 接口请求数据对象
 * @param {object} result 接口的直接返回结果(ResponseResult)
 * @return {*} 除result.code==RESTFUL_NOT_LOGIN跳转首页外，原样返回result
 */
function processResult(request, result) {
    switch (result.code) {
        case Constant.RESTFUL_SUCC:
            if (request.showSuccMsg) {
                vueInstance.$message({ message: result.data, type: 'success' });
            }
            break;
        case Constant.RESTFUL_NOT_LOGIN:
            let showToast = false;
            if (notLoginCallbackFunc != null) {
                showToast = notLoginCallbackFunc(vueInstance, request.url);
            } else {
                vueInstance.$router.push('login');
            }
            // 如果url中包含'common',表示是pageframe中的请求，不显示toast
            if ((request.url.split("/").indexOf("common") == -1 || showToast) && request.showFailMsg) {
                vueInstance.$message({ message: result.msg, type: 'warning' });
            }
            break;
        case Constant.RESTFUL_FAIL:
        case Constant.RESTFUL_LOGIN_USER_NOT_EXIST:
        case Constant.RESTFUL_FORBID_LOGIN:
            if (request.showFailMsg) vueInstance.$message.error(result.msg);
            break;
        default:
            if (request.showFailMsg) vueInstance.$message.error("未知结果Code：" + result.code);
            break;
    }

    return result;
}

/*
 * @Description: 处理接口请求时发生的错误
 * @Author: liusheng, fengal
 * @Date: 2021-02-06 09:06:38
 * @param {object} request 接口请求数据对象
 * @param {*} err 错误信息
 */
async function processError(request, err) {
    console.log('调用processError', request.method, request.url, err);
    
    let processed = false;
    if (err.response.status == 502) {
        processed = (await http502CallbackFunc(vueInstance));
    }
    if (processed) return;

    let time = DateUtil.dateTimeFormat(new Date());
    console.error(time, request.method, request.url, err);
    vueInstance.$message.error("服务器错误:" + err);
}

export { initVueRequest, vueRequest }
