import { describe, it, expect } from 'vitest';
import { CustomerAdapter } from './customer.adapter';
import { CustomerDTO, AssignedPayerDTO } from '@generated/swagger/crm-api';

describe('CustomerAdapter', () => {
  describe('toBuyer', () => {
    it('should convert customer with full data to buyer', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 123,
        customerNumber: 'CUST-123',
        customerType: 2,
        gender: 2,
        title: 'Dr.',
        firstName: 'John',
        lastName: 'Doe',
        dateOfBirth: '1980-01-15',
        communicationDetails: {
          email: 'john.doe@example.com',
          phone: '+49 123 456789',
          mobile: '+49 170 1234567',
        },
        organisation: {
          name: 'ACME Corp',
          department: 'IT',
        },
        address: {
          street: 'Main St',
          streetNumber: '123',
          zipCode: '12345',
          city: 'Berlin',
          country: 'DE',
        },
      } as CustomerDTO;

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer).toEqual({
        source: 123,
        reference: { id: 123 },
        buyerType: 2,
        buyerNumber: 'CUST-123',
        gender: 2,
        title: 'Dr.',
        firstName: 'John',
        lastName: 'Doe',
        dateOfBirth: '1980-01-15',
        communicationDetails: {
          email: 'john.doe@example.com',
          phone: '+49 123 456789',
          mobile: '+49 170 1234567',
        },
        organisation: {
          name: 'ACME Corp',
          department: 'IT',
        },
        address: {
          street: 'Main St',
          streetNumber: '123',
          zipCode: '12345',
          city: 'Berlin',
          country: 'DE',
        },
      });
    });

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

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer.source).toBe(456);
      expect(buyer.reference).toEqual({ id: 456 });
      expect(buyer.buyerNumber).toBe('CUST-456');
      expect(buyer.firstName).toBe('Jane');
      expect(buyer.lastName).toBe('Smith');
      expect(buyer.communicationDetails).toBeUndefined();
      expect(buyer.organisation).toBeUndefined();
      expect(buyer.address).toBeUndefined();
    });

    it('should handle missing optional fields gracefully', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 789,
        customerNumber: 'CUST-789',
        firstName: 'Bob',
        lastName: 'Johnson',
        gender: undefined,
        title: undefined,
        dateOfBirth: undefined,
      } as CustomerDTO;

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer.source).toBe(789);
      expect(buyer.gender).toBeUndefined();
      expect(buyer.title).toBeUndefined();
      expect(buyer.dateOfBirth).toBeUndefined();
    });

    it('should copy communication details (not reference)', () => {
      // Arrange
      const communicationDetails = {
        email: 'test@example.com',
        mobile: '+49 170 1234567',
      };

      const customer: CustomerDTO = {
        id: 100,
        customerNumber: 'CUST-100',
        firstName: 'Test',
        lastName: 'User',
        communicationDetails,
      } as CustomerDTO;

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer.communicationDetails).toEqual(communicationDetails);
      expect(buyer.communicationDetails).not.toBe(communicationDetails);
    });

    it('should copy organisation (not reference)', () => {
      // Arrange
      const organisation = {
        name: 'Test Company',
        department: 'Sales',
      };

      const customer: CustomerDTO = {
        id: 200,
        customerNumber: 'CUST-200',
        firstName: 'Test',
        lastName: 'User',
        organisation,
      } as CustomerDTO;

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer.organisation).toEqual(organisation);
      expect(buyer.organisation).not.toBe(organisation);
    });

    it('should copy address (not reference)', () => {
      // Arrange
      const address = {
        street: 'Test St',
        streetNumber: '1',
        zipCode: '11111',
        city: 'TestCity',
        country: 'DE',
      };

      const customer: CustomerDTO = {
        id: 300,
        customerNumber: 'CUST-300',
        firstName: 'Test',
        lastName: 'User',
        address,
      } as CustomerDTO;

      // Act
      const buyer = CustomerAdapter.toBuyer(customer);

      // Assert
      expect(buyer.address).toEqual(address);
      expect(buyer.address).not.toBe(address);
    });
  });

  describe('toPayerFromAssignedPayer', () => {
    it('should convert assigned payer with full data to payer', () => {
      // Arrange
      const assignedPayer: AssignedPayerDTO = {
        payer: {
          data: {
            id: 456,
            payerNumber: 'PAY-456',
            payerType: 4,
            payerStatus: 1,
            gender: 2,
            title: 'Prof.',
            firstName: 'Alice',
            lastName: 'Brown',
            communicationDetails: {
              email: 'alice.brown@example.com',
              phone: '+49 987 654321',
            },
            organisation: {
              name: 'Brown Ltd',
            },
            address: {
              street: 'Billing St',
              streetNumber: '99',
              zipCode: '54321',
              city: 'Hamburg',
              country: 'DE',
            },
          },
        },
      } as AssignedPayerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(assignedPayer);

      // Assert
      expect(payer).toEqual({
        reference: { id: 456 },
        source: 456,
        payerType: 4,
        payerNumber: 'PAY-456',
        payerStatus: 1,
        gender: 2,
        title: 'Prof.',
        firstName: 'Alice',
        lastName: 'Brown',
        communicationDetails: {
          email: 'alice.brown@example.com',
          phone: '+49 987 654321',
        },
        organisation: {
          name: 'Brown Ltd',
        },
        address: {
          street: 'Billing St',
          streetNumber: '99',
          zipCode: '54321',
          city: 'Hamburg',
          country: 'DE',
        },
      });
    });

    it('should return null when payer data is missing', () => {
      // Arrange
      const assignedPayer: AssignedPayerDTO = {
        payer: {
          data: undefined,
        },
      } as any;

      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(assignedPayer);

      // Assert
      expect(payer).toBeNull();
    });

    it('should return null when payer container is missing', () => {
      // Arrange
      const assignedPayer: AssignedPayerDTO = {
        payer: undefined,
      } as any;

      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(assignedPayer);

      // Assert
      expect(payer).toBeNull();
    });

    it('should return null when assignedPayer is undefined', () => {
      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(undefined as any);

      // Assert
      expect(payer).toBeNull();
    });

    it('should handle payer with minimal data', () => {
      // Arrange
      const assignedPayer: AssignedPayerDTO = {
        payer: {
          data: {
            id: 789,
            payerNumber: 'PAY-789',
            firstName: 'Bob',
            lastName: 'Wilson',
          },
        },
      } as AssignedPayerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(assignedPayer);

      // Assert
      expect(payer?.reference).toEqual({ id: 789 });
      expect(payer?.source).toBe(789);
      expect(payer?.payerNumber).toBe('PAY-789');
      expect(payer?.firstName).toBe('Bob');
      expect(payer?.lastName).toBe('Wilson');
      expect(payer?.communicationDetails).toBeUndefined();
      expect(payer?.organisation).toBeUndefined();
      expect(payer?.address).toBeUndefined();
    });

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

      const assignedPayer: AssignedPayerDTO = {
        payer: {
          data: {
            id: 999,
            payerNumber: 'PAY-999',
            firstName: 'Test',
            lastName: 'Payer',
            communicationDetails,
            organisation,
            address,
          },
        },
      } as AssignedPayerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromAssignedPayer(assignedPayer);

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

  describe('toPayerFromCustomer', () => {
    it('should convert customer to payer with full data', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 123,
        customerNumber: 'CUST-123',
        customerType: 2,
        gender: 4,
        title: 'Ms.',
        firstName: 'Sarah',
        lastName: 'Connor',
        communicationDetails: {
          email: 'sarah.connor@example.com',
          mobile: '+49 170 9876543',
        },
        organisation: {
          name: 'Cyberdyne Systems',
        },
        address: {
          street: 'Skynet Ave',
          streetNumber: '1984',
          zipCode: '90210',
          city: 'Los Angeles',
          country: 'US',
        },
      } as CustomerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromCustomer(customer);

      // Assert
      expect(payer).toEqual({
        reference: { id: 123 },
        payerType: 2,
        payerNumber: 'CUST-123',
        payerStatus: 0,
        gender: 4,
        title: 'Ms.',
        firstName: 'Sarah',
        lastName: 'Connor',
        communicationDetails: {
          email: 'sarah.connor@example.com',
          mobile: '+49 170 9876543',
        },
        organisation: {
          name: 'Cyberdyne Systems',
        },
        address: {
          street: 'Skynet Ave',
          streetNumber: '1984',
          zipCode: '90210',
          city: 'Los Angeles',
          country: 'US',
        },
      });
    });

    it('should set payerStatus to 0 (default)', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 456,
        customerNumber: 'CUST-456',
        firstName: 'Test',
        lastName: 'User',
      } as CustomerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromCustomer(customer);

      // Assert
      expect(payer.payerStatus).toBe(0);
    });

    it('should map customerType to payerType', () => {
      // Arrange
      const customer: CustomerDTO = {
        id: 789,
        customerNumber: 'CUST-789',
        customerType: 4, // B2B
        firstName: 'Business',
        lastName: 'Customer',
      } as CustomerDTO;

      // Act
      const payer = CustomerAdapter.toPayerFromCustomer(customer);

      // Assert
      expect(payer.payerType).toBe(4);
    });

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

      // Act
      const payer = CustomerAdapter.toPayerFromCustomer(customer);

      // Assert
      expect(payer.reference).toEqual({ id: 111 });
      expect(payer.payerNumber).toBe('CUST-111');
      expect(payer.firstName).toBe('Min');
      expect(payer.lastName).toBe('Customer');
      expect(payer.payerStatus).toBe(0);
      expect(payer.communicationDetails).toBeUndefined();
      expect(payer.organisation).toBeUndefined();
      expect(payer.address).toBeUndefined();
    });

    it('should copy nested objects (not reference)', () => {
      // Arrange
      const communicationDetails = { email: 'test@customer.com' };
      const organisation = { name: 'Customer Org' };
      const address = { street: 'Customer St', zipCode: '11111' };

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

      // Act
      const payer = CustomerAdapter.toPayerFromCustomer(customer);

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

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

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

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

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

    it('should return false for invalid types', () => {
      // Act & Assert
      expect(CustomerAdapter.isValidCustomer(null)).toBe(false);
      expect(CustomerAdapter.isValidCustomer(undefined)).toBe(false);
      expect(CustomerAdapter.isValidCustomer('string')).toBe(false);
      expect(CustomerAdapter.isValidCustomer([])).toBe(false);
      expect(CustomerAdapter.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(CustomerAdapter.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(CustomerAdapter.isValidCustomer(customer)).toBe(false);
      });
    });
  });

  describe('isValidAssignedPayer', () => {
    it('should return true for valid AssignedPayer', () => {
      // Arrange
      const validAssignedPayer: AssignedPayerDTO = {
        payer: {
          id: 123,
          data: {
            id: 123,
            payerNumber: 'PAY-123',
          },
        },
      } as AssignedPayerDTO;

      // Act & Assert
      expect(CustomerAdapter.isValidAssignedPayer(validAssignedPayer)).toBe(
        true,
      );
    });

    it('should return true for assigned payer without data', () => {
      // Arrange
      const validAssignedPayer = {
        payer: {
          id: 456,
        },
      };

      // Act & Assert
      expect(CustomerAdapter.isValidAssignedPayer(validAssignedPayer)).toBe(
        true,
      );
    });

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

    it('should return false for missing payer container', () => {
      // Arrange
      const invalidAssignedPayer = {
        someField: 'value',
      };

      // Act & Assert
      expect(CustomerAdapter.isValidAssignedPayer(invalidAssignedPayer)).toBe(
        false,
      );
    });

    it('should return false for incorrect payer structure', () => {
      // Arrange
      const invalidAssignedPayers = [
        { payer: null }, // payer is null
        { payer: 'string' }, // payer is not an object
        { payer: { data: {} } }, // payer missing id
        { payer: { id: 'string' } }, // id is not a number
      ];

      // Act & Assert
      invalidAssignedPayers.forEach((assignedPayer) => {
        expect(CustomerAdapter.isValidAssignedPayer(assignedPayer)).toBe(false);
      });
    });
  });
});
