import { describe, it, expect } from 'vitest';
import { PayerAdapter } from './payer.adapter';
import { PayerDTO as CrmPayerDTO } from '@generated/swagger/crm-api';

describe('PayerAdapter', () => {
  describe('toCheckoutFormat', () => {
    it('should convert CRM payer to checkout payer with full data', () => {
      // Arrange
      const crmPayer: CrmPayerDTO = {
        id: 999,
        payerNumber: 'PAY-999',
        payerType: 4,
        payerStatus: 1,
        gender: 2,
        title: 'Dr.',
        firstName: 'Direct',
        lastName: 'Payer',
        communicationDetails: {
          email: 'direct.payer@example.com',
          phone: '+49 111 222333',
        },
        organisation: {
          name: 'Direct Payer Ltd',
        },
        address: {
          street: 'Direct St',
          streetNumber: '10',
          zipCode: '98765',
          city: 'Frankfurt',
          country: 'DE',
        },
        // CRM-specific fields (should be dropped)
        agentComment: 'VIP payer',
        deactivationComment: undefined,
        defaultPaymentPeriod: 30,
        isGuestAccount: false,
        payerGroup: 'PREMIUM',
        standardInvoiceText: 'Net 30',
      } as CrmPayerDTO;

      // Act
      const checkoutPayer = PayerAdapter.toCheckoutFormat(crmPayer);

      // Assert
      expect(checkoutPayer).toEqual({
        reference: { id: 999 },
        source: 999,
        payerType: 4,
        payerNumber: 'PAY-999',
        payerStatus: 1,
        gender: 2,
        title: 'Dr.',
        firstName: 'Direct',
        lastName: 'Payer',
        communicationDetails: {
          email: 'direct.payer@example.com',
          phone: '+49 111 222333',
        },
        organisation: {
          name: 'Direct Payer Ltd',
        },
        address: {
          street: 'Direct St',
          streetNumber: '10',
          zipCode: '98765',
          city: 'Frankfurt',
          country: 'DE',
        },
      });

      // Verify CRM-specific fields are not included
      expect((checkoutPayer as any).agentComment).toBeUndefined();
      expect((checkoutPayer as any).defaultPaymentPeriod).toBeUndefined();
      expect((checkoutPayer as any).isGuestAccount).toBeUndefined();
      expect((checkoutPayer as any).payerGroup).toBeUndefined();
      expect((checkoutPayer as any).standardInvoiceText).toBeUndefined();
    });

    it('should handle CRM payer with minimal data', () => {
      // Arrange
      const crmPayer: CrmPayerDTO = {
        id: 123,
        payerNumber: 'PAY-123',
        firstName: 'Min',
        lastName: 'Payer',
      } as CrmPayerDTO;

      // Act
      const checkoutPayer = PayerAdapter.toCheckoutFormat(crmPayer);

      // Assert
      expect(checkoutPayer.reference).toEqual({ id: 123 });
      expect(checkoutPayer.source).toBe(123);
      expect(checkoutPayer.payerNumber).toBe('PAY-123');
      expect(checkoutPayer.firstName).toBe('Min');
      expect(checkoutPayer.lastName).toBe('Payer');
      expect(checkoutPayer.communicationDetails).toBeUndefined();
      expect(checkoutPayer.organisation).toBeUndefined();
      expect(checkoutPayer.address).toBeUndefined();
    });

    it('should copy nested objects (not reference)', () => {
      // Arrange
      const communicationDetails = { email: 'crm@payer.com' };
      const organisation = { name: 'CRM Payer Org' };
      const address = { street: 'CRM St', zipCode: '55555' };

      const crmPayer: CrmPayerDTO = {
        id: 456,
        payerNumber: 'PAY-456',
        firstName: 'Test',
        lastName: 'Payer',
        communicationDetails,
        organisation,
        address,
      } as CrmPayerDTO;

      // Act
      const checkoutPayer = PayerAdapter.toCheckoutFormat(crmPayer);

      // Assert
      expect(checkoutPayer.communicationDetails).toEqual(communicationDetails);
      expect(checkoutPayer.communicationDetails).not.toBe(communicationDetails);
      expect(checkoutPayer.organisation).toEqual(organisation);
      expect(checkoutPayer.organisation).not.toBe(organisation);
      expect(checkoutPayer.address).toEqual(address);
      expect(checkoutPayer.address).not.toBe(address);
    });

    it('should set both reference.id and source to the payer id', () => {
      // Arrange
      const crmPayer: CrmPayerDTO = {
        id: 789,
        payerNumber: 'PAY-789',
        firstName: 'Source',
        lastName: 'Test',
      } as CrmPayerDTO;

      // Act
      const checkoutPayer = PayerAdapter.toCheckoutFormat(crmPayer);

      // Assert
      expect(checkoutPayer.reference).toEqual({ id: 789 });
      expect(checkoutPayer.source).toBe(789);
    });
  });

  describe('isValidCrmPayer', () => {
    it('should return true for valid CRM payer', () => {
      // Arrange
      const validPayer: CrmPayerDTO = {
        id: 123,
        payerNumber: 'PAY-123',
        firstName: 'Valid',
        lastName: 'Payer',
      } as CrmPayerDTO;

      // Act & Assert
      expect(PayerAdapter.isValidCrmPayer(validPayer)).toBe(true);
    });

    it('should return true for payer without optional payerNumber', () => {
      // Arrange
      const validPayer = {
        id: 456,
        firstName: 'Valid',
        lastName: 'Payer',
      };

      // Act & Assert
      expect(PayerAdapter.isValidCrmPayer(validPayer)).toBe(true);
    });

    it('should return false for invalid types', () => {
      // Act & Assert
      expect(PayerAdapter.isValidCrmPayer(null)).toBe(false);
      expect(PayerAdapter.isValidCrmPayer(undefined)).toBe(false);
      expect(PayerAdapter.isValidCrmPayer('string')).toBe(false);
      expect(PayerAdapter.isValidCrmPayer([])).toBe(false);
      expect(PayerAdapter.isValidCrmPayer(123)).toBe(false);
    });

    it('should return false for missing required id', () => {
      // Arrange
      const invalidPayer = {
        payerNumber: 'PAY-123',
        firstName: 'Invalid',
        lastName: 'Payer',
      };

      // Act & Assert
      expect(PayerAdapter.isValidCrmPayer(invalidPayer)).toBe(false);
    });

    it('should return false for incorrect field types', () => {
      // Arrange
      const invalidPayers = [
        { id: 'string', payerNumber: 'PAY-123' }, // id should be number
        { id: 123, payerNumber: 456 }, // payerNumber should be string
      ];

      // Act & Assert
      invalidPayers.forEach((payer) => {
        expect(PayerAdapter.isValidCrmPayer(payer)).toBe(false);
      });
    });
  });
});
