/**
 * 网络请求工具类
 * 封装统一的网络请求方法
 */

import config from '@/common/env.js';
import md5 from 'js-md5';
import { getLocalStorage, setLocalStorage } from './cacheService';
import qs from 'qs';
import { clearUserStorage } from '@/common/server/user'
class request {
    /**
     * 生成唯一的nonce值
     * @returns {String} nonce值
     */
    static generateNonce() {
        return md5(Date.now() + Math.random().toString(36).substring(2, 15));
    }

    /**
     * 创建签名
     * @param {Object} data 请求数据
     * @param {String} host 主机名
     * @returns {Object} 带签名的数据和参数字符串
     * @private
     */
    static _createSignature(data, host) {
        // 添加时间戳
        data.timestamp = Math.floor(Date.now() / 1000);
        // 添加nonce随机字符串
        data.nonce = request.generateNonce();

        // 按照键名对参数进行排序
        const sortedParams = {};
        Object.keys(data).sort().forEach(key => {
            sortedParams[key] = data[key];
        });

        // 组合参数为字符串
        let signStr = '';
        for (const key in sortedParams) {
            if (typeof sortedParams[key] === 'object') {
                signStr += key + '=' + JSON.stringify(sortedParams[key]) + '&';
            } else {
                signStr += key + '=' + sortedParams[key] + '&';
            }
        }

        // 获取时间戳，组合为密钥
        const timestamp = data.timestamp;
        const appSecret = host + timestamp;

        // 添加密钥并去除最后的&
        signStr = signStr.substring(0, signStr.length - 1) + appSecret;

        // 使用MD5生成签名
        const sign = md5(signStr);
        data.sign = sign;

        return { data, signStr };
    }

    /**
     * 构建请求URL
     * @param {String} url 请求路径
     * @returns {Object} 包含请求URL和主机名的对象
     * @private
     */
    static _buildRequestUrl(url) {
        let requestUrl = '';

        if (url.startsWith('http://') || url.startsWith('https://')) {
            // 如果是完整的URL，直接使用
            requestUrl = url;
        } else {
            // 否则拼接基础URL和相对路径
            // 确保基础URL以/结尾，而请求路径不以/开头
            const apiBaseUrl = config.apiBaseUrl;
            const baseUrlWithSlash = apiBaseUrl.endsWith('/') ? apiBaseUrl : apiBaseUrl + '/';
            let routeMap = url;
            const path = routeMap.startsWith('/') ? routeMap.substring(1) : routeMap;
            requestUrl = baseUrlWithSlash + path;
        }

        // 使用正则表达式从URL中提取主机名
        const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i;
        const matches = requestUrl.match(hostRegex);
        const host = matches && matches[1] ? matches[1] : 'localhost';

        return { requestUrl, host };
    }

    /**
     * 发送请求
     * @param {String} url 请求地址
     * @param {Object} fromData 请求数据
     * @param {String} method 请求方式
     * @param {Boolean} showLoading 是否显示加载提示
     * @returns {Promise} 返回请求Promise
     */
    static request(url, fromData = {}, method = 'POST', showLoading = false) {
        return new Promise((resolve, reject) => {
            // 使用传入的method而不是重新声明
            const requestMethod = method.toUpperCase();

            let data = { ...fromData }; // 创建数据的深拷贝，避免修改原数据

            // 构建请求URL和提取主机名
            const { requestUrl, host } = request._buildRequestUrl(url);

            // 显示加载提示
            if (showLoading) {
                uni.showLoading({
                    title: '正在加载中...',
                    mask: true
                });
            }

            // 创建签名并准备请求数据和头信息
            const { data: signedData } = request._createSignature(data, host);
            data = signedData;
            // 根据请求方法设置不同的headers
            const header = {
                'content-type': 'application/json'
            };
            const tokenInfo = uni.getStorageSync('tokenInfo');
            if (tokenInfo != null && tokenInfo != "") {
                header['Authorization'] = 'Bearer ' + tokenInfo.token;
            }

            const startDate = Date.now();

            // 发起网络请求
            uni.request({
                url: requestUrl,
                method: requestMethod,
                header: header,
                data: data,
                timeout: 30000, // 设置30秒超时
                success: res => {
                    const endDate = Date.now();
                    console.log(requestUrl, "请求消耗时间", endDate - startDate);
                    if (res.data.code == 14007) {
                        //登录失效
                        clearUserStorage();
                    }
                    resolve(res.data);
                },
                fail: e => {
                    console.error('网络请求失败:', e);
                    uni.showToast({
                        title: e.errMsg || '发送请求失败，请稍后再试！',
                        icon: 'none'
                    });
                    reject(e);
                },
                complete: () => {
                    if (showLoading) {
                        uni.hideLoading();
                    }
                }
            });
        });
    }

    /**
     * 发送GET请求
     * @param {String} url 请求地址
     * @param {Object} data 请求参数
     * @param {Boolean} showLoading 是否显示加载提示
     * @returns {Promise} 返回请求Promise
     */
    static get(url, data = {}, showLoading = false) {
        return request.request(url, data, 'GET', showLoading);
    }

    /**
     * 发送POST请求
     * @param {String} url 请求地址
     * @param {Object} data 请求参数
     * @param {Boolean} showLoading 是否显示加载提示
     * @returns {Promise} 返回请求Promise
     */
    static post(url, data = {}, showLoading = false) {
        return request.request(url, data, 'POST', showLoading);
    }

    /**
     * 发送get请求，如果缓存存在，则返回缓存数据，否则发送请求，并缓存数据
     * @param {String} url 请求地址
     * @param {Object} data 请求参数
     * @param {Number} time 缓存时间，秒
     * @param {Boolean} showLoading 是否显示加载提示
     * @returns {Promise} 返回请求Promise
     */
    static async getOrCache(url, data = {}, time = 300, showLoading = false) {
        const cacheKey = 'cache_' + url + '_' + qs.stringify(data);
        // console.log('getOrCache', cacheKey, '查询缓存');
        const cacheData = getLocalStorage(cacheKey);
        if (cacheData) {
            console.log('getOrCache', cacheKey, '缓存命中');
            return cacheData;
        }

        const res = await request.request(url, data, 'GET', showLoading);
        setLocalStorage(cacheKey, res, time);
        return res;
    }

}

export default request; 