/**
 * KMS Client Tests
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { KMSClient } from './kms';
import {
  KMSClient as AWSKMSClient,
  EncryptCommand,
  DecryptCommand,
  SignCommand,
  VerifyCommand,
} from '@aws-sdk/client-kms';

vi.mock('@aws-sdk/client-kms');

describe('KMSClient', () => {
  let client: KMSClient;
  const config = {
    provider: 'aws' as const,
    keyId: 'test-key-id',
    region: 'us-east-1',
  };

  beforeEach(() => {
    client = new KMSClient(config);
    vi.clearAllMocks();
  });

  describe('encrypt', () => {
    it('should encrypt plaintext', async () => {
      const plaintext = Buffer.from('test data');
      const ciphertext = Buffer.from('encrypted-data');

      const mockSend = vi.fn().mockResolvedValueOnce({
        CiphertextBlob: ciphertext,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      const result = await client.encrypt(plaintext);

      expect(result).toBeInstanceOf(Buffer);
      expect(mockSend).toHaveBeenCalledWith(
        expect.any(EncryptCommand)
      );
    });

    it('should throw error if encryption fails', async () => {
      const plaintext = Buffer.from('test data');

      const mockSend = vi.fn().mockResolvedValueOnce({
        CiphertextBlob: undefined,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      await expect(client.encrypt(plaintext)).rejects.toThrow(
        'Encryption failed: no ciphertext returned'
      );
    });
  });

  describe('decrypt', () => {
    it('should decrypt ciphertext', async () => {
      const ciphertext = Buffer.from('encrypted-data');
      const plaintext = Buffer.from('test data');

      const mockSend = vi.fn().mockResolvedValueOnce({
        Plaintext: plaintext,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      const result = await client.decrypt(ciphertext);

      expect(result).toBeInstanceOf(Buffer);
      expect(mockSend).toHaveBeenCalledWith(
        expect.any(DecryptCommand)
      );
    });

    it('should throw error if decryption fails', async () => {
      const ciphertext = Buffer.from('encrypted-data');

      const mockSend = vi.fn().mockResolvedValueOnce({
        Plaintext: undefined,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      await expect(client.decrypt(ciphertext)).rejects.toThrow(
        'Decryption failed: no plaintext returned'
      );
    });
  });

  describe('sign', () => {
    it('should sign data', async () => {
      const data = Buffer.from('test data');
      const signature = Buffer.from('signature');

      const mockSend = vi.fn().mockResolvedValueOnce({
        Signature: signature,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      const result = await client.sign(data);

      expect(result).toBeInstanceOf(Buffer);
      expect(mockSend).toHaveBeenCalledWith(
        expect.any(SignCommand)
      );
    });

    it('should throw error if signing fails', async () => {
      const data = Buffer.from('test data');

      const mockSend = vi.fn().mockResolvedValueOnce({
        Signature: undefined,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      await expect(client.sign(data)).rejects.toThrow(
        'Signing failed: no signature returned'
      );
    });
  });

  describe('verify', () => {
    it('should verify signature', async () => {
      const data = Buffer.from('test data');
      const signature = Buffer.from('signature');

      const mockSend = vi.fn().mockResolvedValueOnce({
        SignatureValid: true,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      const result = await client.verify(data, signature);

      expect(result).toBe(true);
      expect(mockSend).toHaveBeenCalledWith(
        expect.any(VerifyCommand)
      );
    });

    it('should return false for invalid signature', async () => {
      const data = Buffer.from('test data');
      const signature = Buffer.from('invalid-signature');

      const mockSend = vi.fn().mockResolvedValueOnce({
        SignatureValid: false,
      });

      (AWSKMSClient as any).mockImplementation(() => ({
        send: mockSend,
      }));

      const result = await client.verify(data, signature);

      expect(result).toBe(false);
    });
  });
});

