/**
 * @bytelyst/field-encrypt — Azure Key Vault key provider
 *
 * Production provider — uses AKV RSA keys for DEK wrapping.
 * Requires @azure/keyvault-keys and @azure/identity as peer deps.
 */

import type { KeyProvider } from './types.js';

/**
 * Azure Key Vault key provider.
 *
 * Uses RSA-OAEP wrapping — the MEK never leaves AKV.
 * Requires:
 *   - @azure/keyvault-keys (CryptographyClient)
 *   - @azure/identity (DefaultAzureCredential)
 *   - AKV RBAC: Key Vault Crypto User role on the managed identity
 */
export class AkvKeyProvider implements KeyProvider {
  private readonly vaultUrl: string;
  private readonly mekName: string;
  private cryptoClient: unknown | null = null;

  constructor(vaultUrl: string, mekName: string) {
    if (!vaultUrl) throw new Error('AkvKeyProvider: vaultUrl is required');
    if (!mekName) throw new Error('AkvKeyProvider: mekName is required');
    this.vaultUrl = vaultUrl;
    this.mekName = mekName;
  }

  private async getClient(): Promise<{
    wrapKey(alg: string, key: Uint8Array): Promise<{ result: Uint8Array }>;
    unwrapKey(alg: string, key: Uint8Array): Promise<{ result: Uint8Array }>;
  }> {
    if (this.cryptoClient) return this.cryptoClient as never;

    // Dynamic import to keep peer deps optional
    const { KeyClient, CryptographyClient } = await import('@azure/keyvault-keys');
    const { DefaultAzureCredential } = await import('@azure/identity');

    const credential = new DefaultAzureCredential();
    const keyClient = new KeyClient(this.vaultUrl, credential);
    const key = await keyClient.getKey(this.mekName);

    if (!key.id) {
      throw new Error(`AkvKeyProvider: MEK '${this.mekName}' not found in ${this.vaultUrl}`);
    }

    this.cryptoClient = new CryptographyClient(key.id, credential);
    return this.cryptoClient as never;
  }

  async wrapKey(dek: Buffer): Promise<{ wrappedKey: string; mekVersion: string }> {
    const client = await this.getClient();
    const result = await client.wrapKey('RSA-OAEP-256', new Uint8Array(dek));
    return {
      wrappedKey: Buffer.from(result.result).toString('hex'),
      mekVersion: this.mekName,
    };
  }

  async unwrapKey(wrappedKeyHex: string, _mekVersion: string): Promise<Buffer> {
    const client = await this.getClient();
    const wrappedBytes = new Uint8Array(Buffer.from(wrappedKeyHex, 'hex'));
    const result = await client.unwrapKey('RSA-OAEP-256', wrappedBytes);
    return Buffer.from(result.result);
  }
}
