import {
    getAllLineItems,
    getCartTotal,
    getDeductible,
    getLineItems,
    getMobileFeeLineItem, getNonServiceLineItems,
    getRecycleFeeLineItem, getSalesTax,
    getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
    isOrderUnverified
} from '@/helpers/cart-helper';
import partTypeStrings from '@/constants/part-type-strings';
import partNumberStrings from '@/constants/part-number-strings';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
import { getEnumName } from '@/helpers/unit-test-helper';

describe('cart-helper', () => {
    function createDummyItem(partNumber, price, salesTax) {
        return {
            partNumber,
            sellingPrice: price,
            kitPrice: 0,
            laborAmount: 0,
            salesTax
        };
    }

    function createDummyFeeItem(partNumber, partType, price, salesTax) {
        return {
            partNumber,
            partType,
            sellingPrice: price,
            kitPrice: 0,
            laborAmount: 0,
            salesTax
        };
    }

    const nullLineItems = {
        supportingItems: null,
        glassParts: null,
        otherParts: null,
        vaps: null,
        feeItems: null
    };

    const emptyLineItems = {
        supportingItems: [],
        glassParts: [],
        otherParts: [],
        vaps: [],
        feeItems: []
    };

    const supportingLineItem = createDummyItem('supporting', 30, 1);
    const glassLineItem = createDummyItem('glass', 40, 2);
    const otherLineItem = createDummyItem('other', 50, 3);
    const vapsLineItem = createDummyItem('vaps', 60, 4);
    const mobileFeeLineItem = createDummyFeeItem(null, partTypeStrings.MOBILE_FEE, 70, 5);
    const recycleFeeLineItem = createDummyFeeItem(partNumberStrings.RECYCLE_FEE, null, 80, 6);

    const defaultLineItems = {
        supportingItems: [supportingLineItem],
        glassParts: [glassLineItem],
        otherParts: [otherLineItem],
        vaps: [vapsLineItem],
        feeItems: [mobileFeeLineItem, recycleFeeLineItem]
    };

    describe('getLineItems', () => {
        test('Returns line items', () => {
            // Arrange
            const order = {
                lineItems: emptyLineItems
            };

            // Act
            const result = getLineItems(order);

            // Assert
            expect(result).toMatchObject(order.lineItems);
        });
    });

    describe('getServiceLineItems', () => {
        test('Returns only service line items', () => {
            // Arrange
            const order = {
                lineItems: defaultLineItems
            };

            // Act
            const result = getServiceLineItems(order);

            // Assert
            expect(result).toHaveLength(3);
            expect(result).toContain(supportingLineItem);
            expect(result).toContain(glassLineItem);
            expect(result).toContain(otherLineItem);
        });

        test('Returns empty array when line items are null', () => {
            // Arrange
            const order = {
                lineItems: nullLineItems
            };

            // Act
            const result = getServiceLineItems(order);

            // Assert
            expect(result).toHaveLength(0);
        });
    });

    describe('getNonServiceLineItems', () => {
        test('Returns only service line items', () => {
            // Arrange
            const order = {
                lineItems: defaultLineItems
            };

            // Act
            const result = getNonServiceLineItems(order);

            // Assert
            expect(result).toHaveLength(3);
            expect(result).toContain(vapsLineItem);
            expect(result).toContain(mobileFeeLineItem);
            expect(result).toContain(recycleFeeLineItem);
        });

        test('Returns empty array when line items are null', () => {
            // Arrange
            const order = {
                lineItems: nullLineItems
            };

            // Act
            const result = getNonServiceLineItems(order);

            // Assert
            expect(result).toHaveLength(0);
        });
    });

    describe('getAllLineItems', () => {
        test('Returns all line items', () => {
            // Arrange
            const order = {
                lineItems: defaultLineItems
            };

            // Act
            const result = getAllLineItems(order);

            // Assert
            expect(result).toHaveLength(6);
            expect(result).toContain(supportingLineItem);
            expect(result).toContain(glassLineItem);
            expect(result).toContain(otherLineItem);
            expect(result).toContain(vapsLineItem);
            expect(result).toContain(mobileFeeLineItem);
            expect(result).toContain(recycleFeeLineItem);
        });

        test('Returns empty array when line items are null', () => {
            // Arrange
            const order = {
                lineItems: nullLineItems
            };

            // Act
            const result = getAllLineItems(order);

            // Assert
            expect(result).toHaveLength(0);
        });
    });

    describe('getRecycleFee', () => {
        test('Returns recycle fee when present', () => {
            // Arrange
            const order = {
                lineItems: defaultLineItems
            };

            // Act
            const result = getRecycleFeeLineItem(order);

            // Assert
            expect(result).not.toBeNull();
            expect(result).toMatchObject(recycleFeeLineItem);
        });

        test('Returns undefined recycle fee when not present', () => {
            // Arrange
            const order = {
                lineItems: emptyLineItems
            };

            // Act
            const result = getRecycleFeeLineItem(order);

            // Assert
            expect(result).toBeUndefined();
        });
    });

    describe('getMobileFee', () => {
        test('Returns mobile fee when present', () => {
            // Arrange
            const order = {
                lineItems: defaultLineItems
            };

            // Act
            const result = getMobileFeeLineItem(order);

            // Assert
            expect(result).not.toBeNull();
            expect(result).toMatchObject(mobileFeeLineItem);
        });

        test('Returns undefined mobile fee when not present', () => {
            // Arrange
            const order = {
                lineItems: emptyLineItems
            };

            // Act
            const result = getMobileFeeLineItem(order);

            // Assert
            expect(result).toBeUndefined();
        });
    });

    describe('getDeductible', () => {
        test('Returns deductible on the order', () => {
            // Arrange
            const deductible = {
                replace: 100
            };
            const order = {
                damage: {
                    isRepair: false
                },
                currentDeductible: deductible
            };

            // Act
            const result = getDeductible(order);

            // Assert
            expect(result).not.toBeNull();
            expect(result).toBe(deductible.replace);
        });
    });

    describe('isOrderUnverified', () => {
        test.each([[false, false], [true, true]])('Returns %p when isUnverified is %p', (expected, isUnverified) => {
            // Arrange
            const order = {
                isUnverified
            };

            // Act
            const result = isOrderUnverified(order);

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

    describe('isOrderNoComp', () => {
        test('Returns false when insuranceCoverage is null', () => {
            // Arrange
            const order = {};

            // Act
            const result = isOrderNoComp(order);

            // Assert
            expect(result).toBeFalsy();
        });

        describe.each([
            [false, coverageType.NONE],
            [false, coverageType.ITAC],
            [true, coverageType.NO_COMP],
            [false, coverageType.Deductible]])('isOrderNoComp by coverageType', (expected, type) => {
            test(`Returns ${expected} when insuranceCoverage.coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    insuranceCoverage: {
                        coverageType: type
                    }
                };

                // Act
                const result = isOrderNoComp(order);

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

    describe('isOrderITAC', () => {
        test('Returns false when insuranceCoverage is null', () => {
            // Arrange
            const order = {};

            // Act
            const result = isOrderITAC(order);

            // Assert
            expect(result).toBeFalsy();
        });

        describe.each([
            [false, coverageType.NONE],
            [true, coverageType.ITAC],
            [false, coverageType.NO_COMP],
            [false, coverageType.Deductible]])('isOrderITAC by coverageType ', (expected, type) => {
            test(`Returns ${expected} when insuranceCoverage.coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    insuranceCoverage: {
                        coverageType: type
                    }
                };

                // Act
                const result = isOrderITAC(order);

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

    describe('getSubtotal', () => {
        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [100, coverageType.Deductible]])('getSubtotal null line items', (expected, type) => {
            test(`Returns ${expected} when there are null items and verified ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: nullLineItems
                };

                // Act
                const result = getSubtotal(order);

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

        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [100, coverageType.Deductible]])('getSubtotal no line items', (expected, type) => {
            test(`Returns ${expected} when there are no items and verified ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: emptyLineItems
                };

                // Act
                const result = getSubtotal(order);

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

        describe.each([
            [330, coverageType.ITAC],
            [330, coverageType.NO_COMP],
            [310, coverageType.Deductible]])('getSubtotal with line items', (expected, type) => {
            test(`Returns ${expected} cart subtotal when verified ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: defaultLineItems
                };

                // Act
                const result = getSubtotal(order);

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

    describe('getSalesTax', () => {
        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [0, coverageType.Deductible]])('getSalesTax null items', (expected, type) => {
            test(`Returns ${expected} when there are null items and coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    lineItems: nullLineItems
                };

                // Act
                const result = getSalesTax(order);

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

        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [0, coverageType.Deductible]])('getSalesTax with no items', (expected, type) => {
            test(`Returns ${expected} when there are no items and coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    lineItems: emptyLineItems
                };

                // Act
                const result = getSalesTax(order);

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

        describe.each([
            [21, coverageType.ITAC],
            [21, coverageType.NO_COMP],
            [15, coverageType.Deductible]])('getSalesTax with line items', (expected, type) => {
            test(`Returns ${expected} when coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    lineItems: defaultLineItems
                };

                // Act
                const result = getSalesTax(order);

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

    describe('getCartTotal', () => {
        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [100, coverageType.Deductible]
        ])('getCartTotal null items', (expected, type) => {
            test(`Returns ${expected} when there are null items and coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: nullLineItems
                };

                // Act
                const result = getCartTotal(order);

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

        describe.each([
            [0, coverageType.ITAC],
            [0, coverageType.NO_COMP],
            [100, coverageType.Deductible]
        ])('getCartTotal no items', (expected, type) => {
            test(`Returns ${expected} when there are no items and coverageType is ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: emptyLineItems
                };

                // Act
                const result = getCartTotal(order);

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

        describe.each([
            [351, coverageType.ITAC],
            [351, coverageType.NO_COMP],
            [325, coverageType.Deductible]
        ])('getCartTotal items', (expected, type) => {
            test(`Returns ${expected} cart total when verified ${getEnumName(coverageType, type)}`, () => {
                // Arrange
                const order = {
                    isUnverified: false,
                    insuranceCoverage: {
                        coverageStatus: coverageStatuses.VERIFIED,
                        coverageType: type
                    },
                    damage: {
                        isRepair: false
                    },
                    currentDeductible: {
                        replace: 100
                    },
                    lineItems: defaultLineItems
                };

                // Act
                const result = getCartTotal(order);

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