import { describe, it, expect } from 'vitest';
import {
  shouldSetPayer,
  needsDestinationUpdate,
  determinePaymentType,
  PaymentTypes,
} from './checkout-business-logic.helpers';
import {
  CustomerTypeAnalysis,
  OrderOptionsAnalysis,
  ShoppingCartItem,
} from '../models';

describe('Checkout Business Logic Helpers', () => {
  describe('shouldSetPayer', () => {
    it('should return true for B2B customers', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: true,
        isOnline: false,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true when order has B2B delivery', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: true,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: true,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true when order has standard delivery', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: true,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true when order has digital delivery', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: true,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: true,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true when order has download items', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: true,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: true,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });

    it('should return false for in-store pickup only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: false,
        isGuest: false,
        hasCustomerCard: true,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

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

    it('should return false for take away only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: false,
        isOnline: false,
        isGuest: false,
        hasCustomerCard: false,
        isStaff: true,
      };

      // Act
      const result = shouldSetPayer(options, customer);

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

    it('should return true when multiple conditions are met', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: true,
        hasDownload: true,
        hasDelivery: true,
        hasDigDelivery: true,
        hasB2BDelivery: true,
        items: [],
      };
      const customer: CustomerTypeAnalysis = {
        isB2B: true,
        isOnline: true,
        isGuest: false,
        hasCustomerCard: true,
        isStaff: false,
      };

      // Act
      const result = shouldSetPayer(options, customer);

      // Assert
      expect(result).toBe(true);
    });
  });

  describe('needsDestinationUpdate', () => {
    it('should return true for download orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: true,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true for standard delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true for digital delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: true,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

      // Assert
      expect(result).toBe(true);
    });

    it('should return true for B2B delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: true,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

      // Assert
      expect(result).toBe(true);
    });

    it('should return false for pickup only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

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

    it('should return false for take away only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

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

    it('should return true when any delivery/download type is present', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = needsDestinationUpdate(options);

      // Assert
      expect(result).toBe(true);
    });
  });

  describe('determinePaymentType', () => {
    it('should return FREE (2) when all items have non-zero loyalty value', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [
          { loyalty: { value: 500 } } as ShoppingCartItem,
          { loyalty: { value: 300 } } as ShoppingCartItem,
          { loyalty: { value: 100 } } as ShoppingCartItem,
        ],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.FREE);
      expect(result).toBe(2);
    });

    it('should return FREE even when order has delivery (loyalty takes precedence)', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [
          { loyalty: { value: 500 } } as ShoppingCartItem,
          { loyalty: { value: 300 } } as ShoppingCartItem,
        ],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.FREE);
    });

    it('should return INVOICE (128) for standard delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.INVOICE);
      expect(result).toBe(128);
    });

    it('should return INVOICE (128) for download orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: true,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.INVOICE);
    });

    it('should return INVOICE (128) for digital delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: true,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.INVOICE);
    });

    it('should return INVOICE (128) for B2B delivery orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: true,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.INVOICE);
    });

    it('should return CASH (4) for pickup only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
      expect(result).toBe(4);
    });

    it('should return CASH (4) for take away only orders', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: false,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
    });

    it('should return CASH (4) when items have zero loyalty value', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [
          { loyalty: { value: 0 } } as ShoppingCartItem,
          { loyalty: { value: 0 } } as ShoppingCartItem,
        ],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
    });

    it('should return CASH when some items lack loyalty value', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [
          { loyalty: { value: 500 } } as ShoppingCartItem,
          { loyalty: undefined } as ShoppingCartItem,
        ],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
    });

    it('should return CASH when items have no loyalty property', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{} as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
    });

    it('should return CASH for empty items array', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: false,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: false,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.CASH);
    });

    it('should return INVOICE when mixed order types include delivery', () => {
      // Arrange
      const options: OrderOptionsAnalysis = {
        hasTakeAway: true,
        hasPickUp: true,
        hasDownload: false,
        hasDelivery: true,
        hasDigDelivery: false,
        hasB2BDelivery: false,
        items: [{ loyalty: { value: 0 } } as ShoppingCartItem],
      };

      // Act
      const result = determinePaymentType(options);

      // Assert
      expect(result).toBe(PaymentTypes.INVOICE);
    });
  });

  describe('PaymentTypes constants', () => {
    it('should have correct FREE value', () => {
      expect(PaymentTypes.FREE).toBe(2);
    });

    it('should have correct CASH value', () => {
      expect(PaymentTypes.CASH).toBe(4);
    });

    it('should have correct INVOICE value', () => {
      expect(PaymentTypes.INVOICE).toBe(128);
    });
  });
});
