export const CryptoUtil = {
  bufToBase64(buf) {
    const bytes = new Uint8Array(buf);
    let binary = '';
    for (let i = 0; i < bytes.length; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  },

  base64ToUint8(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  },

  async hashPassword(password) {
    const encoder = new TextEncoder();
    const data = encoder.encode(password);
    const hash = await crypto.subtle.digest('SHA-256', data);
    return this.bufToBase64(hash);
  },

  async deriveAESKey(password, salt = 'secure-messaging-app') {
    const encoder = new TextEncoder();
    const key = await crypto.subtle.importKey(
      'raw',
      encoder.encode(password),
      { name: 'PBKDF2' },
      false,
      ['deriveBits', 'deriveKey']
    );
    return await crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: encoder.encode(salt),
        iterations: 100000,
        hash: 'SHA-256'
      },
      key,
      { name: 'AES-GCM', length: 256 },
      true,
      ['encrypt', 'decrypt']
    );
  },

  async generateRSAKeyPair(name, hash) {
    return await crypto.subtle.generateKey(
      {
        name: name,
        modulusLength: 2048,
        publicExponent: new Uint8Array([1, 0, 1]),
        hash: hash
      },
      true,
      name === 'RSA-OAEP' ? ['encrypt', 'decrypt'] : ['sign', 'verify']
    );
  },

  async exportPublicKey(publicKey) {
    const spki = await crypto.subtle.exportKey('spki', publicKey);
    return this.bufToBase64(spki);
  },

  async exportPrivateKey(privateKey) {
    const pkcs8 = await crypto.subtle.exportKey('pkcs8', privateKey);
    return this.bufToBase64(pkcs8);
  },

  async importPublicKey(base64Key, type, hash) {
    const keyData = this.base64ToUint8(base64Key).buffer;
    return await crypto.subtle.importKey(
      'spki',
      keyData,
      { name: type, hash: hash },
      true,
      type === 'RSA-OAEP' ? ['encrypt'] : ['verify']
    );
  },

  async importPrivateKey(base64Key, type, hash) {
    const keyData = this.base64ToUint8(base64Key).buffer;
    return await crypto.subtle.importKey(
      'pkcs8',
      keyData,
      { name: type, hash: hash },
      true,
      type === 'RSA-OAEP' ? ['decrypt'] : ['sign']
    );
  },

  async encryptMessage(message, aesKey) {
    const encoder = new TextEncoder();
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const ciphertext = await crypto.subtle.encrypt(
      { name: 'AES-GCM', iv },
      aesKey,
      encoder.encode(message)
    );
    return {
      ciphertext: this.bufToBase64(ciphertext),
      iv: this.bufToBase64(iv)
    };
  },

  async decryptMessage(ciphertext, iv, aesKey) {
    const decrypted = await crypto.subtle.decrypt(
      { name: 'AES-GCM', iv: this.base64ToUint8(iv) },
      aesKey,
      this.base64ToUint8(ciphertext)
    );
    return new TextDecoder().decode(decrypted);
  },

  async encryptAESKey(aesKey, recipientPublicKey) {
    const rawKey = await crypto.subtle.exportKey('raw', aesKey);
    const encrypted = await crypto.subtle.encrypt(
      { name: 'RSA-OAEP' },
      recipientPublicKey,
      rawKey
    );
    return this.bufToBase64(encrypted);
  },

  async decryptAESKey(encryptedKey, privateKey) {
    const decrypted = await crypto.subtle.decrypt(
      { name: 'RSA-OAEP' },
      privateKey,
      this.base64ToUint8(encryptedKey)
    );
    return await crypto.subtle.importKey(
      'raw',
      decrypted,
      { name: 'AES-GCM' },
      true,
      ['encrypt', 'decrypt']
    );
  },

  async signMessage(message, privateKey) {
    const encoder = new TextEncoder();
    const signature = await crypto.subtle.sign(
      { name: 'RSA-PSS', saltLength: 32 },
      privateKey,
      encoder.encode(message)
    );
    return this.bufToBase64(signature);
  },

  async verifySignature(message, signature, publicKeyB64) {
      try {
          const publicKey = await this.importPublicKey(publicKeyB64, 'RSA-PSS', 'SHA-512');
          const encoder = new TextEncoder();
          const verified = await crypto.subtle.verify(
              { name: 'RSA-PSS', saltLength: 32 },
              publicKey,
              this.base64ToUint8(signature),
              encoder.encode(message)
          );
          return verified;
      } catch (e) {
          console.error('Signature verification failed:', e);
          return false;
      }
  },

  async hashMessage(message) {
      const encoder = new TextEncoder();
      const hash = await crypto.subtle.digest('SHA-512', encoder.encode(message));
      const bytes = new Uint8Array(hash);
      return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
  }
};
