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

import EnvConfig from '@/common/env.js'
import md5 from 'js-md5'
 
import { apiWhiteList } from '@/common/config.js'
import RouterManager from '@/common/router.js'
import { platform } from '@/common/platform/PlatformFactory'
class RequestManager {
  // Token 刷新状态标记
  static isRefreshing = false;
  
  // 等待刷新的请求队列
  static refreshQueue = [];

  // 缓存对象
  static cache = {
    data: new Map(),
    // 缓存过期时间（毫秒）
    expireTime: 5 * 60 * 1000, // 5分钟
    // 缓存时间戳
    timestamps: new Map()
  };

  /**
   * 检查缓存是否存在且未过期
   * @param {string} cacheKey 缓存键
   * @returns {boolean} 缓存是否有效
   */
  static isCacheValid(cacheKey) {
    const now = Date.now();
    if (this.cache.data.has(cacheKey)) {
      const timestamp = this.cache.timestamps.get(cacheKey);
      return now - timestamp < this.cache.expireTime;
    }
    return false;
  }

  /**
   * 更新缓存
   * @param {string} cacheKey 缓存键
   * @param {any} data 缓存数据
   */
  static updateCache(cacheKey, data) {
    this.cache.data.set(cacheKey, data);
    this.cache.timestamps.set(cacheKey, Date.now());
  }

  /**
   * 刷新 Token
   * 使用 Refresh Token 获取新的 Access Token
   * @returns {Promise<boolean>} 刷新是否成功
   */
  static async refreshToken() {
    const refreshToken = uni.getStorageSync('refreshToken');
    
    if (!refreshToken) {
      console.log('没有 refreshToken，无法刷新');
      return false;
    }
    
    try {
      const apiBaseUrl = EnvConfig.apiBaseUrl;
      const baseUrlWithSlash = apiBaseUrl.endsWith('/') ? apiBaseUrl : apiBaseUrl + '/';
      const requestUrl = baseUrlWithSlash + 'refresh';
      
      console.log('开始刷新 Token...');
      
      const response = await new Promise((resolve, reject) => {
        uni.request({
          url: requestUrl,
          method: 'POST',
          header: {
            'content-type': 'application/json'
          },
          data: {
            refreshToken: refreshToken
          },
          success: (res) => resolve(res),
          fail: (err) => reject(err)
        });
      });
      
      // 检查 HTTP 状态码
      if (response.statusCode === 200 && response.data && response.data.status === 1) {
        const data = response.data.data;
        
        // 更新本地存储的 Token
        if (data.accessToken) {
          uni.setStorageSync('token', data.accessToken);
          uni.setStorageSync('accessToken', data.accessToken);
        }
        
        if (data.refreshToken) {
          uni.setStorageSync('refreshToken', data.refreshToken);
        }
        
        if (data.expiresIn) {
          // 计算过期时间戳（当前时间 + expiresIn 秒）
          const expireTime = Date.now() + (data.expiresIn * 1000);
          uni.setStorageSync('tokenExpireTime', expireTime);
        }
        
        console.log('Token 刷新成功');
        return true;
      } else {
        console.log('Token 刷新失败:', response.data?.msg || '未知错误');
        return false;
      }
    } catch (error) {
      console.error('Token 刷新请求失败:', error);
      return false;
    }
  }

  /**
   * 将请求加入等待队列
   * @param {Function} resolve Promise resolve 函数
   * @param {Function} reject Promise reject 函数
   * @param {Object} param 原始请求参数
   */
  static addToRefreshQueue(resolve, reject, param) {
    this.refreshQueue.push({ resolve, reject, param });
  }

  /**
   * 处理等待队列中的请求
   * 刷新成功后，使用新 Token 重新执行队列中的所有请求
   * @param {boolean} success 刷新是否成功
   */
  static processRefreshQueue(success) {
    console.log(`处理刷新队列，共 ${this.refreshQueue.length} 个请求，刷新${success ? '成功' : '失败'}`);
    
    this.refreshQueue.forEach(({ resolve, reject, param }) => {
      if (success) {
        // 刷新成功，重新执行请求
        this.request(param)
          .then(resolve)
          .catch(reject);
      } else {
        // 刷新失败，拒绝所有等待的请求
        reject({ status: -1, msg: '登录已过期' });
      }
    });
    
    // 清空队列
    this.refreshQueue = [];
  }

  /**
   * 清除所有 Token 并跳转登录页
   */
  static clearTokensAndRedirect() {
    // 清除所有 Token 相关存储
    uni.removeStorageSync('token');
    uni.removeStorageSync('accessToken');
    uni.removeStorageSync('refreshToken');
    uni.removeStorageSync('tokenExpireTime');
    uni.removeStorageSync('userinfo');
    
    // 保存当前页面用于登录后跳转
    var pages = getCurrentPages();
    var currentPage = pages[pages.length - 1];
    if (currentPage) {
      var currentRoute = currentPage.route;
      var currentParams = currentPage.options || {};
      if (currentRoute && currentRoute !== 'pages/user/login') {
        var redirectPath = '/' + currentRoute;
        if (Object.keys(currentParams).length > 0) {
          var paramString = Object.keys(currentParams)
            .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(currentParams[key])}`)
            .join('&');
          redirectPath += '?' + paramString;
        }
        uni.setStorageSync('redirect', redirectPath);
      }
    }
    
    setTimeout(() => {
      uni.showToast({
        title: '登录已过期，请重新登录',
        icon: 'none'
      });
    }, 100);
    
    RouterManager.navigateTo('/pages/user/login', {}, 'navigateTo')
      .catch(err => {
        console.error('登录页面跳转失败:', err);
      });
  }

  /**
   * 发送带缓存的GET请求
   * @param {String} url 请求地址
   * @param {Object} data 请求参数
   * @param {Boolean} showLoading 是否显示加载提示
   * @returns {Promise} 返回请求Promise
   */
  static getCache(url, data = {}, showLoading = false) {
    // 生成缓存键
    const cacheKey = url + JSON.stringify(data);

    // 检查缓存是否有效
    if (this.isCacheValid(cacheKey)) {
      console.log(cacheKey + '缓存有效');
      return Promise.resolve(this.cache.data.get(cacheKey));
    }

    // 如果缓存无效，发起新请求
    return this.get(url, data, showLoading).then(result => {
      // 更新缓存
      this.updateCache(cacheKey, result);
      return result;
    });
  }

  /**
   * 判断URL是否在白名单中
   * @param {String} url 请求地址
   * @returns {Boolean} 是否在白名单中
   */
  static isUrlInWhitelist(url) {

    // 检查URL是否包含白名单中的任一项
    return apiWhiteList.some(whiteItem => url.indexOf(whiteItem) > -1);
  }

  /**
   * 生成唯一的nonce值
   * @returns {String} nonce值
   */
  static generateNonce() {
    return md5(Date.now() + Math.random().toString(36).substring(2, 15));
  }

  /**
   * 发送网络请求
   * @param {Object} param 请求参数
   * @param {String} param.url 请求地址
   * @param {Object} param.data 请求数据
   * @param {Function} param.success 成功回调
   * @param {Function} param.fail 失败回调
   * @param {Function} param.complete 完成回调
   * @param {Boolean} param.Loading 是否显示加载提示
   * @param {String} backpage 返回页面
   * @param {String} backtype 返回类型
   * @returns {Promise} 返回请求Promise
   */
  static request(param, backpage, backtype) {

    return new Promise((resolve, reject) => {
      // 参数检查
      if (!param || typeof param !== 'object') {
        reject(new Error('请求参数错误'))
        return
      }

      uni.getNetworkType({
        success: function (res) {
          if (res.networkType == 'none') {
            uni.showToast({
              title: '网络连接异常，请检查网络',
              icon: 'none'
            })
            reject(new Error('网络连接异常'))
            return
          }
        }
      })


      const url = param.url || ''
      const method = param.method || 'POST'
      const data = param.data || {}
      const Loading = param.Loading || false
      const token = uni.getStorageSync('token')
      let client = platform.code
      // 获取API基础URL
      const apiBaseUrl = EnvConfig.apiBaseUrl

      // 拼接完整请求地址，确保URL格式正确
      let requestUrl = ''

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

      // console.log('请求URL:', requestUrl)

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

      let header = {}

      // 添加签名和防重放攻击参数
      // 1. 添加时间戳
      data.timestamp = Math.floor(Date.now() / 1000)
      // 2. 添加nonce随机字符串
      data.nonce = RequestManager.generateNonce()
      console.log(requestUrl);

      if (method.toUpperCase() == 'POST') {
        // 按照键名对参数进行排序
        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
        // console.log('签名字符串:', signStr)

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

        header = {
          'content-type': 'application/json',
          client: client,
          'Authorization': token ? `Bearer ${token}` : '',
          adid: uni.getStorageSync('_ad_id'),
          clickid: uni.getStorageSync('_click_id')
        }
      } else {
        // GET请求，添加签名
        // 按照键名对参数进行排序
        const sortedParams = {}
        Object.keys(data).sort().forEach(key => {
          sortedParams[key] = data[key]
        })

        // 组合参数为字符串
        let signStr = ''
        for (const key in sortedParams) {
          signStr += key + '=' + sortedParams[key] + '&'
        }

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

        // 添加密钥并去除最后的&
        signStr = signStr.substring(0, signStr.length - 1) + appSecret
        // console.log('签名字符串:', signStr)

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

        // 添加签名到请求参数
        data.sign = sign

        header = {
          'content-type': 'application/json',
          'Authorization': token ? `Bearer ${token}` : '',
          client: client,
        }
      }

      // 显示加载提示
      if (!Loading) {
        uni.showLoading({
          title: '加载中...'
        })
      }

      // 发起网络请求
      uni.request({
        url: requestUrl,
        method: method.toUpperCase(),
        header: header,
        data: data,
        success: res => {
          console.log("res.data.status", res.data.status, "res.statusCode", res.statusCode)
          
          // 处理 HTTP 401 未授权（Token 过期或无效）
          if (res.statusCode === 401) {
            console.log('Token过期或无效');
            
            // 白名单接口直接返回错误，不处理
            if (RequestManager.isUrlInWhitelist(requestUrl)) {
              reject({ status: -1, msg: '登录已过期' });
              return;
            }
            
            // 检查是否有 token（用户是否曾经登录过）
            const currentToken = uni.getStorageSync('token');
            if (!currentToken) {
              // 用户从未登录过，直接返回错误，不跳转
              console.log('用户未登录，返回错误');
              reject({ status: -1, msg: '请先登录' });
              return;
            }
            
            // 检查是否有 refreshToken
            const refreshToken = uni.getStorageSync('refreshToken');
            if (!refreshToken) {
              console.log('没有 refreshToken，清除token并返回错误');
              // 清除过期的token，但不跳转登录页
              uni.removeStorageSync('token');
              uni.removeStorageSync('accessToken');
              uni.removeStorageSync('tokenExpireTime');
              uni.removeStorageSync('userinfo');
              reject({ status: -1, msg: '登录已过期' });
              return;
            }
            
            // 如果正在刷新中，将当前请求加入队列等待
            if (RequestManager.isRefreshing) {
              console.log('Token 正在刷新中，将请求加入队列');
              RequestManager.addToRefreshQueue(resolve, reject, param);
              return;
            }
            
            // 标记开始刷新
            RequestManager.isRefreshing = true;
            
            // 尝试刷新 Token
            RequestManager.refreshToken()
              .then(success => {
                // 刷新完成，重置标记
                RequestManager.isRefreshing = false;
                
                if (success) {
                  console.log('Token 刷新成功，重试原请求');
                  // 刷新成功，重试原请求
                  RequestManager.request(param)
                    .then(resolve)
                    .catch(reject);
                  
                  // 处理队列中的其他请求
                  RequestManager.processRefreshQueue(true);
                } else {
                  console.log('Token 刷新失败');
                  // 刷新失败，清除 Token，但不跳转登录页
                  uni.removeStorageSync('token');
                  uni.removeStorageSync('accessToken');
                  uni.removeStorageSync('refreshToken');
                  uni.removeStorageSync('tokenExpireTime');
                  uni.removeStorageSync('userinfo');
                  reject({ status: -1, msg: '登录已过期' });
                  
                  // 拒绝队列中的所有请求
                  RequestManager.processRefreshQueue(false);
                }
              })
              .catch(error => {
                console.error('Token 刷新异常:', error);
                RequestManager.isRefreshing = false;
                uni.removeStorageSync('token');
                uni.removeStorageSync('accessToken');
                uni.removeStorageSync('refreshToken');
                uni.removeStorageSync('tokenExpireTime');
                uni.removeStorageSync('userinfo');
                reject({ status: -1, msg: '登录已过期' });
                RequestManager.processRefreshQueue(false);
              });
            
            return;
          }
          
          var pages = getCurrentPages()
          if (res.data.status == 1) {
            // 请求成功
            resolve(res.data)
          } else if (res.data.status == 2222) {
            // 特殊状态码处理
            resolve(res.data)
          } else if (res.data.status == -9) {
            let pages = getCurrentPages()
            console.log(pages[pages.length - 1].route)
            setTimeout(() => {
              uni.showToast({
                title: res.data.msg,
                icon: 'none',
                success() {
                  setTimeout(() => {
                    // #ifdef H5
                    uni.navigateTo({
                      url: '/pages/user/bangdingweb'
                    })
                    // #endif
                    // #ifdef MP-WEIXIN
                    uni.navigateTo({
                      url: '/pages/user/bangding'
                    })
                    // #endif
                  }, 1500)
                }
              })
            }, 100)
            reject(res.data)
          } else if (res.data.status == 0) {
            setTimeout(function () {
              uni.showToast({
                title: res.data.msg,
                icon: 'none'
              })
            }, 100)

            reject(res.data)
          } else if (res.data.status < 0) {
            var pages = getCurrentPages()
            if (res.data.msg != null) {
              if (res.data.msg.indexOf("没有找到用户信息") > -1) {
                // 清除所有Token相关存储
                uni.removeStorageSync('token');
                uni.removeStorageSync('accessToken');
                uni.removeStorageSync('refreshToken');
                uni.removeStorageSync('tokenExpireTime');
                uni.removeStorageSync('userinfo');
              }
            }

            // 白名单接口直接返回错误，不跳转登录页
            console.log(requestUrl);
            if (RequestManager.isUrlInWhitelist(requestUrl)) {
              reject(res.data)
              return;
            }

            // 清除所有Token相关存储
            uni.removeStorageSync('token');
            uni.removeStorageSync('accessToken');
            uni.removeStorageSync('refreshToken');
            uni.removeStorageSync('tokenExpireTime');
            uni.removeStorageSync('userinfo');

            // 不再自动跳转登录页，只返回错误让调用方处理
            // 调用方可以根据业务需要决定是否跳转登录页
            reject(res.data)
          } else {
            reject(res.data)
          }
          typeof param.success == 'function' && param.success(res.data)
        },
        fail: e => {
          console.error('网络请求失败:', e)
          uni.showToast({
            title: e.errMsg || '请求失败',
            icon: 'none'
          })
          typeof param.fail == 'function' && param.fail(e)
          reject(e)
        },
        complete: () => {
          uni.hideLoading()
          typeof param.complete == 'function' && param.complete()
        }
      })
    })
  }

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

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

export default RequestManager; 