
const {ccclass, property} = cc._decorator;
import CryptoJS = require('./crypto-js.min.js');  //引用AES源码js

const BASE_URL = "https://api.sparkus.cn";
//只负责网络接口 次类只负责和后端交互，不负责处理数据 数据处理在GameTool
@ccclass
export default class HttpUtil extends cc.Component {
    //排行榜
    static async rankData(type,callback,data): Promise<any> {
      const time = Math.floor((new Date().getTime()) / 1000)
      const url = HttpUtil.apiSign(`/api/get/rank/data?gameId=${config.gameId}&dataType=${type}&time=${time}`, data)
      this.post(url,data,callback,0);
    }
  
    static async uploadUserLogData(data,callback): Promise<any> {
      const url = '/log/collect/data';
      this.post(url,data,callback,3);
    }
    //暂时用不到
    static async getUserRecord(data,callback): Promise<any> {
      const time = Math.floor((new Date().getTime()) / 1000)
      const url = HttpUtil.apiSign(`/api/get/user/data?gameId=${config.gameId}&time=${time}`, data)
      this.post(url,data,callback,0);
    }

  static async get(url, callback,count) {
    let repeat = count?count:0;
    const response = await this.fetchData(url, null, 'GET',repeat);
    callback && callback(response);
  }

  static async post(url, data, callback,count) {
    let repeat = count?count:0;
    const response = await this.fetchData(url, data, 'POST',repeat);
    callback && callback(response);
  }

  static async fetchData(url, data, method,repeat) {
    const fullUrl = `${BASE_URL}${url}`;
    const headers = { 'Content-Type': 'application/json' };
    const options = {
      method,
      headers,
      body: data ? JSON.stringify(data) : null,
    };

    try {
      var response = await this.fetchWithTimeout(fullUrl,options);
      if (!response.ok) {
          throw new Error(`HTTP_______________error! status: ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      console.error('Fetch_______________error:', error);
      if(repeat > 0){
        repeat -= 1;
        const timeOut = (3-repeat)*5000;
        setTimeout(async () => {
          response = await this.fetchData(url, data, method,repeat);
        }, timeOut);
      }
      else{
        return null;
      }
    }
  } 

  static async fetchWithTimeout(resource, options = {}) {
      const controller = new AbortController();
      const id = setTimeout(() => controller.abort(), 5000);
      const response = await fetch(resource, {
      ...options,
      signal: controller.signal
      });
      clearTimeout(id);

      return response;
  }
  /**
 * 
 * @param url {string} 接口地址
 * @param params {object} 需要加密的参数对象
 */
  static apiSign(url: string, params = {}) {

    let convertUrl = url.trim()
    if (convertUrl.indexOf('?') === -1) {
      convertUrl += '?'
    }
    
    // 传入参数转换拼接字符串
    let postStr =  getQueryString(params)
  
    const signedStr = genSignStr(convertUrl, postStr)
    const encryptStr = `sign=${signedStr}`

    let encryptSignStr = fxCry.encryptByDES(encryptStr, config.secretKey)
    encryptSignStr = encodeURIComponent(encryptSignStr)

    return `${urlencode(convertUrl)}&_p=${encryptSignStr}`
  }
}

function responseHandler(response: { data: any }) {
  return response.data
}
// 响应拦截器
// Rq.interceptors.response.use(responseHandler)
const config = {
  gameId: "100009",
  secretKey: "CMNhOzBA",
  EK:"hui231%1"
};

interface CrypotoType {
  encryptByDES: any
  decryptByDES: any
  hmacSha256: any
}
 
class Crypoto implements CrypotoType {
    // 加密的向明值，自己根据项目实际情况定，需要跟后端开发保持一致
    private keyHex = this.getHetKey()
    private getHetKey() {
        return CryptoJS.enc.Utf8.parse(config.EK);
    }

    /** DES加密 */
    encryptByDES(message: string, secret?: string) {
      if(!message) {
        return message
      }
      const key = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex
      const encrypted = CryptoJS.DES.encrypt(message, key, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
      });
      return encrypted.toString()
    }

    /** DES解密 */
    decryptByDES(message: string, secret?: string) {
      const key = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex

      const decrypted = CryptoJS.DES.decrypt({
        ciphertext: CryptoJS.enc.Base64.parse(message)
      }, key, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
      })

      return decrypted.toString(CryptoJS.enc.Utf8)
    }

    /** hmacSHA256加密 */
    hmacSha256(message: string, secret?: string) {
      const keyHex = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex
      
      const hash = CryptoJS.HmacSHA256(message, keyHex);
      return hash.toString()
    }

    /** hmacSHA256验证 */
    verifyHmacSha256(message: string, signature: string) {
      const hash = CryptoJS.HmacSHA256(message, this.keyHex);
      return hash.toString() === signature
    }
 
    /** CBC加密 */
    encryptCBC(word: string) {
        if (!word) {
            return word;
        }
        const srcs = CryptoJS.enc.Utf8.parse(word);
        const encrypted = CryptoJS.AES.encrypt(srcs, this.keyHex, {
            iv: this.keyHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.ZeroPadding
        });
        return encrypted.toString(); 
    }
 
    /** CBC解密 */
    decryptCBC(word: string) {
        if (!word) {
            return word;
        }
        const encryptedHexStr = CryptoJS.enc.Hex.parse(word);
        const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
        const decrypt = CryptoJS.AES.decrypt(srcs, this.keyHex, {
            iv: this.keyHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.ZeroPadding
        });
        const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
        return decryptedStr.toString();
    }
}
const fxCry = new Crypoto();

function isEmpty(data) {
  return data === "" || data === null || data === undefined || data.length === 0 || JSON.stringify(data) == "{}"
}

function getQueryString(obj) {
  // 首先对对象的键进行排序并过滤空值
  const sortedKeys = Object.keys(obj).sort();
  const sortedObj = {};
  for (let i = 0; i < sortedKeys.length; i++) {
    if (isEmpty(obj[sortedKeys[i]])) {
      continue;
    }
    sortedObj[sortedKeys[i]] = obj[sortedKeys[i]];
  }
  
  // 然后将排序后的对象转换为查询字符串
  const params = [];
  for (const key in sortedObj) {
    params.push(`${encodeURIComponent(key)}=${encodeURIComponent(sortedObj[key])}`);
  }
  return params.join('&');
}


/**
 * 组装签名字符串
 * @param string url: 请求地址 
 * @param string postStr: post参数的a=1&b=2 
 * @returns 
 */

function genSignStr(url: string, postStr: string): string {
  let lessUrl = url.replace('?', '')
  lessUrl = lessUrl + "&" + postStr
  return encodeURIComponent(fxCry.hmacSha256(lessUrl))
}
// 对参数进行统一urlencode
function urlencode(url: string): string {
  const [baseUrl, queryString] = url.split("?", 2);
  const params = new URLSearchParams(queryString);
  return `${baseUrl}?${params.toString()}`;
}


