import { describe, it, expect } from 'vitest';
import { ShippingAddressAdapter } from './shipping-address.adapter';
import {
  ShippingAddressDTO as CrmShippingAddressDTO,
  CustomerDTO,
} from '@generated/swagger/crm-api';

describe('ShippingAddressAdapter', () => {
  describe('fromCrmShippingAddress', () => {
    it('should convert CRM shipping address to checkout format', () => {
      // Arrange
      const crmAddress: CrmShippingAddressDTO = {
        id: 789,
        gender: 2,
        title: 'Dr.',
        firstName: 'Delivery',
        lastName: 'Address',
        communicationDetails: {
          phone: '+49 123 111111',
          mobile: '+49 170 2222222',
        },
        organisation: {
          name: 'Delivery Company',
          department: 'Receiving',
        },
        address: {
          street: 'Delivery Lane',
          streetNumber: '42',
          zipCode: '67890',
          city: 'Munich',
          country: 'DE',
        },
        // CRM-specific fields (should be dropped)
        type: 1 as any,
        validated: '2024-01-01',
        validationResult: 100,
        agentComment: 'Verified address',
        isDefault: '2024-01-01',
      } as CrmShippingAddressDTO;

      // Act
      const checkoutAddress =
        ShippingAddressAdapter.fromCrmShippingAddress(crmAddress);

      // Assert
      expect(checkoutAddress).toEqual({
        reference: { id: 789 },
        source: 789,
        gender: 2,
        title: 'Dr.',
        firstName: 'Delivery',
        lastName: 'Address',
        communicationDetails: {
          phone: '+49 123 111111',
          mobile: '+49 170 2222222',
        },
        organisation: {
          name: 'Delivery Company',
          department: 'Receiving',
        },
        address: {
          street: 'Delivery Lane',
          streetNumber: '42',
          zipCode: '67890',
          city: 'Munich',
          country: 'DE',
        },
      });

      // Verify CRM-specific fields are not included
      expect((checkoutAddress as any).type).toBeUndefined();
      expect((checkoutAddress as any).validated).toBeUndefined();
      expect((checkoutAddress as any).validationResult).toBeUndefined();
      expect((checkoutAddress as any).agentComment).toBeUndefined();
      expect((checkoutAddress as any).isDefault).toBeUndefined();
    });

    it('should handle shipping address with minimal data', () => {
      // Arrange
      const crmAddress: CrmShippingAddressDTO = {
        id: 321,
        firstName: 'Simple',
        lastName: 'Address',
      } as CrmShippingAddressDTO;

      // Act
      const checkoutAddress =
        ShippingAddressAdapter.fromCrmShippingAddress(crmAddress);

      // Assert
      expect(checkoutAddress.reference).toEqual({ id: 321 });
      expect(checkoutAddress.source).toBe(321);
      expect(checkoutAddress.firstName).toBe('Simple');
      expect(checkoutAddress.lastName).toBe('Address');
      expect(checkoutAddress.communicationDetails).toBeUndefined();
      expect(checkoutAddress.organisation).toBeUndefined();
      expect(checkoutAddress.address).toBeUndefined();
    });

    it('should copy nested objects (not reference)', () => {
      // Arrange
      const communicationDetails = { email: 'shipping@example.com' };
      const organisation = { name: 'Shipping Org' };
      const address = { street: 'Ship St', zipCode: '99999' };

      const crmAddress: CrmShippingAddressDTO = {
        id: 555,
        firstName: 'Test',
        lastName: 'Shipping',
        communicationDetails,
        organisation,
        address,
      } as CrmShippingAddressDTO;

      // Act
      const checkoutAddress =
        ShippingAddressAdapter.fromCrmShippingAddress(crmAddress);

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

  describe('fromCustomer', () => {
    it('should convert customer to shipping address with full data', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 999,
        customerNumber: 'CUST-999',
        gender: 1,
        title: 'Mx.',
        firstName: 'Alex',
        lastName: 'Taylor',
        communicationDetails: {
          email: 'alex.taylor@example.com',
          phone: '+49 555 123456',
        },
        organisation: {
          name: 'Taylor Industries',
        },
        address: {
          street: 'Primary St',
          streetNumber: '100',
          zipCode: '10115',
          city: 'Berlin',
          country: 'DE',
        },
      } as CustomerDTO;

      // Act
      const shippingAddress = ShippingAddressAdapter.fromCustomer(customer);

      // Assert
      expect(shippingAddress).toEqual({
        reference: { id: 999 },
        gender: 1,
        title: 'Mx.',
        firstName: 'Alex',
        lastName: 'Taylor',
        communicationDetails: {
          email: 'alex.taylor@example.com',
          phone: '+49 555 123456',
        },
        organisation: {
          name: 'Taylor Industries',
        },
        address: {
          street: 'Primary St',
          streetNumber: '100',
          zipCode: '10115',
          city: 'Berlin',
          country: 'DE',
          additionalInfo: undefined,
        },
      });

      // No source field when derived from customer
      expect((shippingAddress as any).source).toBeUndefined();
    });

    it('should handle customer with minimal data', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 777,
        customerNumber: 'CUST-777',
        firstName: 'Min',
        lastName: 'Address',
      } as CustomerDTO;

      // Act
      const shippingAddress = ShippingAddressAdapter.fromCustomer(customer);

      // Assert
      expect(shippingAddress.reference).toEqual({ id: 777 });
      expect(shippingAddress.firstName).toBe('Min');
      expect(shippingAddress.lastName).toBe('Address');
      expect(shippingAddress.communicationDetails).toBeUndefined();
      expect(shippingAddress.organisation).toBeUndefined();
      expect(shippingAddress.address).toBeUndefined();
    });

    it('should copy nested objects (not reference)', () => {
      // Arrange
      const communicationDetails = { email: 'customer@address.com' };
      const organisation = { name: 'Address Org' };
      const address = {
        street: 'Address St',
        zipCode: '88888',
        streetNumber: undefined,
        city: undefined,
        country: undefined,
        additionalInfo: undefined,
      };

      const customer: CustomerDTO = {
        id: 888,
        customerNumber: 'CUST-888',
        firstName: 'Test',
        lastName: 'Customer',
        communicationDetails,
        organisation,
        address,
      } as CustomerDTO;

      // Act
      const shippingAddress = ShippingAddressAdapter.fromCustomer(customer);

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

    it('should not include source field (different from CRM shipping address)', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 666,
        customerNumber: 'CUST-666',
        firstName: 'No',
        lastName: 'Source',
      } as CustomerDTO;

      // Act
      const shippingAddress = ShippingAddressAdapter.fromCustomer(customer);

      // Assert
      expect(shippingAddress).not.toHaveProperty('source');
    });
  });

  describe('isValidCrmShippingAddress', () => {
    it('should return true for valid CRM shipping address', () => {
      // Arrange
      const validAddress: CrmShippingAddressDTO = {
        id: 123,
        firstName: 'Valid',
        lastName: 'Address',
      } as CrmShippingAddressDTO;

      // Act & Assert
      expect(
        ShippingAddressAdapter.isValidCrmShippingAddress(validAddress),
      ).toBe(true);
    });

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

    it('should return false for missing required id', () => {
      // Arrange
      const invalidAddress = {
        firstName: 'Invalid',
        lastName: 'Address',
      };

      // Act & Assert
      expect(
        ShippingAddressAdapter.isValidCrmShippingAddress(invalidAddress),
      ).toBe(false);
    });
  });

  describe('isValidCustomer', () => {
    it('should return true for valid Customer', () => {
      // Arrange
      const validCustomer: CustomerDTO = {
        id: 456,
        customerNumber: 'CUST-456',
        firstName: 'Valid',
        lastName: 'Customer',
      } as CustomerDTO;

      // Act & Assert
      expect(ShippingAddressAdapter.isValidCustomer(validCustomer)).toBe(true);
    });

    it('should return true for customer without optional customerNumber', () => {
      // Arrange
      const validCustomer = {
        id: 789,
        firstName: 'Valid',
        lastName: 'Customer',
      };

      // Act & Assert
      expect(ShippingAddressAdapter.isValidCustomer(validCustomer)).toBe(true);
    });

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

    it('should return false for missing required id', () => {
      // Arrange
      const invalidCustomer = {
        customerNumber: 'CUST-123',
        firstName: 'Invalid',
        lastName: 'Customer',
      };

      // Act & Assert
      expect(ShippingAddressAdapter.isValidCustomer(invalidCustomer)).toBe(
        false,
      );
    });

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

      // Act & Assert
      invalidCustomers.forEach((customer) => {
        expect(ShippingAddressAdapter.isValidCustomer(customer)).toBe(false);
      });
    });
  });
});
