function getCrypto(): Crypto {
  if (typeof crypto !== 'undefined' && crypto.subtle) {
    // 浏览器环境或Node.js 19+的global.crypto
    return crypto;
  } else if (typeof global !== 'undefined') {
    // Node.js环境
    if (global.crypto && global.crypto.subtle) {
      // Node.js 19+
      return global.crypto;
    }
    // @ts-ignore
    else if (global.crypto && global.crypto.webcrypto && global.crypto.webcrypto.subtle) {
      // Node.js 16+
      // @ts-ignore
      return global.crypto.webcrypto;
    } else {
      // Node.js 16-18，使用crypto.webcrypto
      const nodeCrypto = require('crypto');
      if (nodeCrypto.webcrypto && nodeCrypto.webcrypto.subtle) {
        return nodeCrypto.webcrypto;
      }
      throw new Error('Node.js版本低于16或缺少webcrypto支持');
    }
  }
  throw new Error('当前环境不支持Web Crypto API');
}


class AsymmetricCrypto {
  /**
   * 生成RSA密钥对并返回字符串格式的公钥和私钥
   * @param {number} modulusLength - 密钥长度，默认2048
   * @returns {Promise<{publicKey: string, privateKey: string}>} PEM格式的密钥字符串
   */
  static async generateKeyPair(modulusLength: number = 2048): Promise<{ publicKey: string, privateKey: string }> {
    const crypto = getCrypto();
    try {
      // 生成密钥对
      const keyPair = await crypto.subtle.generateKey(
        {
          name: "RSA-OAEP",
          modulusLength,
          publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
          hash: "SHA-256",
        },
        true, // 是否可导出
        ["encrypt", "decrypt"]
      );

      // 导出公钥为PEM格式字符串
      const publicKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
      const publicKeyPem = this.derToPem(publicKeyDer, 'PUBLIC');

      // 导出私钥为PEM格式字符串
      const privateKeyDer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
      const privateKeyPem = this.derToPem(privateKeyDer, 'PRIVATE');

      return {
        publicKey: publicKeyPem,
        privateKey: privateKeyPem
      };
    } catch (error) {
      throw new Error(`生成密钥对失败: ${error.message}`);
    }
  }

  /**
   * 使用公钥加密字符串
   * @param {string} publicKeyPem - PEM格式的公钥字符串
   * @param {string} data - 要加密的字符串数据
   * @returns {Promise<string>} Base64编码的加密字符串
   */
  static async encrypt(publicKeyPem: string, data: string): Promise<string> {
    const crypto = getCrypto();
    try {
      // 导入公钥
      const publicKey = await this.importPublicKey(publicKeyPem);

      // 转换数据为Uint8Array
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(data);

      // 检查数据长度（RSA-OAEP有最大长度限制）
      const maxLength = this.getMaxEncryptLength(publicKeyPem);
      if (dataBuffer.length > maxLength) {
        throw new Error(`数据过长，最大支持${maxLength}字节，当前${dataBuffer.length}字节`);
      }

      // 加密数据
      const encrypted = await crypto.subtle.encrypt(
        {
          name: "RSA-OAEP"
        },
        publicKey,
        dataBuffer
      );

      // 转换为Base64字符串
      return this.arrayBufferToBase64(encrypted);
    } catch (error) {
      throw new Error(`加密失败: ${error.message}`);
    }
  }

  /**
   * 使用私钥解密字符串
   * @param {string} privateKeyPem - PEM格式的私钥字符串
   * @param {string} encryptedData - Base64编码的加密字符串
   * @returns {Promise<string>} 解密后的原始字符串
   */
  static async decrypt(privateKeyPem: string, encryptedData: string): Promise<string> {
    const crypto = getCrypto();
    try {
      // 导入私钥
      const privateKey = await this.importPrivateKey(privateKeyPem);

      // 转换Base64字符串为ArrayBuffer
      const encryptedBuffer = this.base64ToArrayBuffer(encryptedData);

      // 解密数据
      const decrypted = await crypto.subtle.decrypt(
        {
          name: "RSA-OAEP"
        },
        privateKey,
        encryptedBuffer
      );

      // 转换回字符串
      const decoder = new TextDecoder();
      return decoder.decode(decrypted);
    } catch (error) {
      throw new Error(`解密失败: ${error.message}`);
    }
  }

  /**
   * 导入PEM格式的公钥字符串为CryptoKey对象
   * @param {string} publicKeyPem - PEM格式的公钥字符串
   * @returns {Promise<CryptoKey>}
   */
  static async importPublicKey(publicKeyPem: string): Promise<CryptoKey> {
    const crypto = getCrypto();
    try {
      // PEM转DER
      const der = this.pemToDer(publicKeyPem);

      // 导入密钥
      return await crypto.subtle.importKey(
        "spki",
        der,
        {
          name: "RSA-OAEP",
          hash: "SHA-256"
        },
        true,
        ["encrypt"]
      );
    } catch (error) {
      throw new Error(`导入公钥失败: ${error.message}`);
    }
  }

  /**
   * 导入PEM格式的私钥字符串为CryptoKey对象
   * @param {string} privateKeyPem - PEM格式的私钥字符串
   * @returns {Promise<CryptoKey>}
   */
  static async importPrivateKey(privateKeyPem: string): Promise<CryptoKey> {
    const crypto = getCrypto();
    try {
      // PEM转DER
      const der = this.pemToDer(privateKeyPem);

      // 导入密钥
      return await crypto.subtle.importKey(
        "pkcs8",
        der,
        {
          name: "RSA-OAEP",
          hash: "SHA-256"
        },
        true,
        ["decrypt"]
      );
    } catch (error) {
      throw new Error(`导入私钥失败: ${error.message}`);
    }
  }

  /**
   * 获取公钥支持的最大加密长度（字节数）
   * @param {string} publicKeyPem - PEM格式的公钥字符串
   * @returns {number} 最大加密字节数
   */
  static getMaxEncryptLength(publicKeyPem: string): number {

    // 简单估算最大加密长度
    // RSA-OAEP最大加密长度 = modulusLength/8 - 2*hashLength/8 - 2
    // 这里通过PEM字符串长度来估算密钥长度

    // 提取Base64内容
    const base64Data = publicKeyPem
      .replace(/-----BEGIN PUBLIC KEY-----/, '')
      .replace(/-----END PUBLIC KEY-----/, '')
      .replace(/\s+/g, '');

    // Base64解码获取DER长度
    const derLength = atob(base64Data).length;

    // 典型长度对应关系：
    if (derLength <= 294) { // 2048位密钥DER长度约294字节
      return 190; // 2048位：190字节
    } else if (derLength <= 422) { // 3072位密钥DER长度约422字节
      return 318; // 3072位：318字节
    } else { // 4096位
      return 446; // 4096位：446字节
    }
  }

  /**
   * 将DER格式转换为PEM格式字符串
   * @param {ArrayBuffer} der - DER格式的密钥数据
   * @param {string} type - 密钥类型 ('PUBLIC' 或 'PRIVATE')
   * @returns {string} PEM格式的密钥字符串
   */
  static derToPem(der: ArrayBuffer, type: 'PUBLIC' | 'PRIVATE'): string {

    const base64 = this.arrayBufferToBase64(der);

    // 将Base64字符串分割为64字符一行
    const wrapped = base64.match(/.{1,64}/g).join('\n');

    if (type === 'PUBLIC') {
      return `-----BEGIN PUBLIC KEY-----\n${wrapped}\n-----END PUBLIC KEY-----`;
    } else {
      return `-----BEGIN PRIVATE KEY-----\n${wrapped}\n-----END PRIVATE KEY-----`;
    }
  }

  /**
   * 将PEM格式字符串转换为DER格式
   * @param {string} pem - PEM格式的密钥字符串
   * @returns {ArrayBuffer} DER格式的密钥数据
   */
  static pemToDer(pem: string): ArrayBuffer {
    // 移除PEM头部、尾部和所有空白字符
    const base64 = pem
      .replace(/-----BEGIN (?:PUBLIC|PRIVATE) KEY-----/, '')
      .replace(/-----END (?:PUBLIC|PRIVATE) KEY-----/, '')
      .replace(/\s+/g, '');

    return this.base64ToArrayBuffer(base64);
  }

  /**
   * ArrayBuffer转Base64字符串
   * @param {ArrayBuffer} buffer
   * @returns {string}
   */
  static arrayBufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }

  /**
   * Base64字符串转ArrayBuffer
   * @param {string} base64
   * @returns {ArrayBuffer}
   */
  static base64ToArrayBuffer(base64: string): ArrayBuffer {
    const binaryString = atob(base64);
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
  }

  /**
   * 验证PEM格式的密钥字符串
   * @param {string} pem - PEM格式的密钥字符串
   * @param {'PUBLIC'|'PRIVATE'} expectedType - 期望的密钥类型
   * @returns {boolean} 是否为有效的PEM格式
   */
  static validatePemFormat(pem: string, expectedType: 'PUBLIC' | 'PRIVATE'): boolean {
    if (typeof pem !== 'string') return false;

    const publicKeyPattern = /^-----BEGIN PUBLIC KEY-----\n([A-Za-z0-9+/=\n]+)\n-----END PUBLIC KEY-----$/;
    const privateKeyPattern = /^-----BEGIN PRIVATE KEY-----\n([A-Za-z0-9+/=\n]+)\n-----END PRIVATE KEY-----$/;

    if (expectedType === 'PUBLIC') {
      return publicKeyPattern.test(pem);
    } else {
      return privateKeyPattern.test(pem);
    }
  }
}


export {
  AsymmetricCrypto
}
