// Components
import { shallowMount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import tpaSubmit from '@/layouts/tpa-submit/tpa-submit.vue';

// Supporting Files
import { getEnumName, getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper.js';
import widgetFields from '@/constants/cms-widget-fields.js';
import { toTitleCase, formatAddress, toDisplayPhoneNumber, formatAmountInDollars } from '@/helpers/text-helper.js';
import { getDamageDisplayContent } from '@/helpers/damage-review-content-generator.js';
import baseMixin from '@/mixins/base-mixin';
import coverageStatuses from '@/constants/coverage-statuses';

// Mock fetchCmsContentForPage
jest.mock('@/helpers/cms-content-helper', () => ({
    fetchCmsContentForPage: jest.fn(),
    doesCopyContainRouterLink: jest.fn(),
    getStringWithCustomValues: jest.fn((str, customValueMap) => {
        let newString = str ?? '';
        if (customValueMap != null) {
            Object.keys(customValueMap).forEach((key) => {
                newString = newString.replaceAll(`{custom:${key}}`, customValueMap[key]);
            });
        }
        return newString;
    }),
    processIfStatements: jest.fn()
}));

jest.mock('@/helpers/text-helper.js', () => ({
    formatAddress: jest.fn(),
    toDisplayPhoneNumber: jest.fn(),
    formatAmountInDollars: jest.fn(),
    toTitleCase: jest.fn()
}));

jest.mock('@/helpers/damage-review-content-generator.js', () => ({
    getDamageDisplayContent: jest.fn(),
    getLocationAnswer: jest.fn()
}));

// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());

const mockMixin = {
    methods: {
        ...baseMixin.methods
    }
};

function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}) {
    const mountOptions = getMountOptions({
        router: {
            navigate: jest.fn()
        }
    });

    const testingPinia = createTestingPinia({
        initialState: {
            main: mainInitialState
        }
    });
    useMainStore(testingPinia);
    methodToRunAfterInitializingStore();

    mountOptions.global.plugins = [testingPinia];
    mountOptions.data = () => (initialData);
    mountOptions.mixins = [mockMixin];

    const apiResponses = { cmsContent: {} };

    settleAllPromises.mockImplementation(() => apiResponses);
    fetchCmsContentForPage.mockImplementation(() => Promise.resolve());

    const wrapper = shallowMount(tpaSubmit, mountOptions);
    wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {});
    wrapper.vm.setCmsContent = jest.fn();
    return { wrapper };
}

beforeEach(() => {
    formatAddress.mockClear();
    toDisplayPhoneNumber.mockClear();
    getDamageDisplayContent.mockClear();
    formatAmountInDollars.mockClear();
});

describe('tpa-submit', () => {
    test('returns the initial data', () => {
        // Arrange
        const companyName = 'Frederick Jones';
        const mainInitialState = {
            order: {
                serviceLocation: {
                    provider: { companyName }
                }
            }
        };
        const { wrapper } = getMountedComponent(mainInitialState);

        // Assert
        expect(wrapper.vm.$data).toMatchSnapshot();
    });
    describe('should render', () => {
        test('tpa submit form', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const form = wrapper.findComponent({ ref: 'tpaSubmitFormRef' });

            // Assert
            expect(form.exists()).toBeTruthy();
            expect(form.classes()).toContain('tpa-submit');
        });
        test('site header', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());
            const expectedWidgetName = 'SiteHeaderWidget';

            // Act
            const siteHeader = wrapper.findComponent({ ref: 'siteHeader' });

            // Assert
            expect(siteHeader.exists()).toBeTruthy();
            expect(siteHeader.props().cmsWidgetName).toBe(expectedWidgetName);
        });
        test('sub header', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const subHeader = wrapper.findComponent({ ref: 'subHeaderTitle' });

            // Assert
            expect(subHeader.exists()).toBeTruthy();
            expect(subHeader.classes()).toContain('tpa-submit-title');
        });
        test('sub header body one', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const subHeaderBodyOne = wrapper.findComponent({ ref: 'tpaSubmitSubHeaderBodyOne' });

            // Assert
            expect(subHeaderBodyOne.exists()).toBeTruthy();
        });
        test('service summary section', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const serviceSummarySection = wrapper.find('#serviceSummarySection');

            // Assert
            expect(serviceSummarySection.exists()).toBeTruthy();
        });
        describe('review blocks', () => {
            const title1 = 'Section 1';
            const title2 = 'Another Section';
            const lines1 = ['apple', 'banana', 'cherry'];
            const lines2 = ['soccer', 'golf'];
            const sections = [
                {
                    title: title1,
                    lines: lines1,
                    onClickEdit: () => {}
                },
                {
                    title: title2,
                    lines: lines2,
                    onClickEdit: () => {}
                }
            ];
            const initialData = { sections };
            const { wrapper } = getMountedComponent({}, initialData);
            test.each([
                [0, true, title1, lines1],
                [1, true, title2, lines2],
                [2, false, null, null],
                [-1, false, null, null]
            ])('section %p exists value is %p with header text %p and lines %p', (index, exists, headerText, lines) => {
                // Act
                const reviewBlocks = wrapper.findComponent(`#review-block-${index}`);

                // Assert
                expect(reviewBlocks.exists()).toBe(exists);
                if (exists) {
                    expect(reviewBlocks.props().customHeaderText).toBe(headerText);
                    expect(reviewBlocks.props().lines).toStrictEqual(lines);
                }
            });
        });
        test('submit order details section', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const submitOrderDetailsSection = wrapper.find({ ref: 'submitOrderDetailsSection' });

            // Assert
            expect(submitOrderDetailsSection.exists()).toBeTruthy();
        });
        test('submit order details title', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const submitOrderDetailsTitle = wrapper.findComponent({ ref: 'tpaSubmitOrderDetailsTitle' });

            // Assert
            expect(submitOrderDetailsTitle.exists()).toBeTruthy();
            expect(submitOrderDetailsTitle.classes()).toContain('fw-bold');
            expect(submitOrderDetailsTitle.classes()).toContain('order-details-title');
        });
        test('deductible box', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const deductibleBox = wrapper.findComponent({ ref: 'deductibleBox' });

            // Assert
            expect(deductibleBox.exists()).toBeTruthy();
        });
        test('site footer', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const siteFooter = wrapper.findComponent({ ref: 'siteFooter' });

            // Assert
            expect(siteFooter.exists()).toBeTruthy();
            expect(siteFooter.props().cmsWidgetName).toBe('SiteFooterWidget');
            expect(siteFooter.classes()).toContain('mt-5');
        });
        test('contact details drawer', () => {
            // Arrange
            const wrapper = shallowMount(tpaSubmit, getMountOptions());

            // Act
            const contactDetailsDrawer = wrapper.findComponent({ ref: 'contactDetailsDrawer' });

            // Assert
            expect(contactDetailsDrawer.exists()).toBeTruthy();
        });
    });
    describe('before route enter', () => {
        test('produces 2 sections', async () => {
            // Arrange
            const { wrapper } = getMountedComponent();
            expect(wrapper.vm.sections.length).toBe(0);

            // Act
            await tpaSubmit.beforeRouteEnter.call(
                wrapper.vm,
                { query: { issPage: 'tpa-submit' } },
                undefined,
                (c) => c(wrapper.vm)
            );

            // Assert
            expect(wrapper.vm.sections.length).toBe(2);
        });
        describe('preferred shop section', () => {
            test('has two lines', async () => {
                // Arrange
                const { wrapper } = getMountedComponent();
                const preferredShopSectionIndex = 0;

                // Act
                await tpaSubmit.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { issPage: 'tpa-submit' } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                // Assert
                const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
                expect(preferredShopSection.lines.length).toBe(2);
            });
            test('title is value returned from toTitleCase method', async () => {
                // Arrange
                const initialData = { companyName: 'some value' };
                const { wrapper } = getMountedComponent({}, initialData);
                const expectedName = 'some expected name';
                toTitleCase.mockImplementationOnce(() => expectedName);
                const preferredShopSectionIndex = 0;

                // Act
                await tpaSubmit.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { issPage: 'tpa-submit' } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                // Assert
                const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
                expect(preferredShopSection.title).toBe(expectedName);
            });
            test('first line is expected and formatAddress called', async () => {
                // Arrange
                const address = {
                    streetAddress: '123 South Ln',
                    city: 'Oneida',
                    state: 'FL',
                    zipCode: '78226'
                };
                const initialStore = {
                    order: {
                        serviceLocation: {
                            provider: { address }
                        }
                    }
                };
                const { wrapper } = getMountedComponent(initialStore);
                const line = 'some returned line';
                formatAddress.mockImplementationOnce(() => line);
                const preferredShopSectionIndex = 0;

                // Act
                await tpaSubmit.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { issPage: 'tpa-submit' } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                // Assert
                const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
                expect(preferredShopSection.lines[0]).toBe(line);
                expect(formatAddress).toHaveBeenCalledTimes(1);
                expect(formatAddress).toHaveBeenCalledWith(
                    address.streetAddress,
                    null,
                    address.city,
                    address.state,
                    address.zipCode
                );
            });
            test('second line is expected and toDisplayPhoneNumber called', async () => {
                // Arrange
                const phoneNumber = '9998887777';
                const initialStore = {
                    order: {
                        serviceLocation: {
                            provider: { phoneNumber }
                        }
                    }
                };
                const { wrapper } = getMountedComponent(initialStore);
                const expectedLine = 'returned from to display phone num';
                toDisplayPhoneNumber.mockImplementationOnce(() => expectedLine);
                const preferredShopSectionIndex = 0;

                // Act
                await tpaSubmit.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { issPage: 'tpa-submit' } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                // Assert
                const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
                expect(preferredShopSection.lines[1]).toBe(expectedLine);
                expect(toDisplayPhoneNumber).toHaveBeenCalledWith(phoneNumber);
            });
        });
        test('contact info section has expected content', async () => {
            // Arrange
            const firstName = 'Jones';
            const lastName = 'Eddison';
            const emailAddress = 'myname@gmail.com';
            const servicePhone = '0001112222';
            const providerCompanyName = 'Some Provider LLC';
            const initialStore = {
                order: {
                    serviceLocation: {
                        provider: {
                            companyName: providerCompanyName
                        }
                    },
                    contactInfo: {
                        firstName,
                        lastName,
                        emailAddress,
                        servicePhone
                    }
                }
            };
            const { wrapper } = getMountedComponent(initialStore);
            const expectedEmail = emailAddress;
            const expectedPhone = 'some value returned';
            toDisplayPhoneNumber.mockImplementation((number) => (number === servicePhone ? expectedPhone : ''));
            const contactInfoSectionIndex = 1;
            wrapper.vm.getCmsContent.mockImplementation(() => '{custom:contactPhone} or {custom:contactEmail}');

            // Act
            await tpaSubmit.beforeRouteEnter.call(
                wrapper.vm,
                { query: { issPage: 'tpa-submit' } },
                undefined,
                (c) => c(wrapper.vm)
            );
            wrapper.vm.setSections();

            // Assert
            const contactInfoSection = wrapper.vm.sections[contactInfoSectionIndex];
            expect(contactInfoSection.lines[0]).toContain(expectedEmail);
            expect(contactInfoSection.lines[0]).toContain(expectedPhone);
            expect(toDisplayPhoneNumber).toHaveBeenCalledWith(servicePhone);
        });
        test('contact info section has expected content with extension', async () => {
            // Arrange
            const firstName = 'Jones';
            const lastName = 'Eddison';
            const emailAddress = 'myname@gmail.com';
            const servicePhone = '0001112222';
            const extension = '12345';
            const providerCompanyName = 'Some Provider LLC';
            const initialStore = {
                order: {
                    serviceLocation: {
                        provider: {
                            companyName: providerCompanyName
                        }
                    },
                    contactInfo: {
                        firstName,
                        lastName,
                        emailAddress,
                        servicePhone,
                        extension
                    }
                }
            };
            const { wrapper } = getMountedComponent(initialStore);
            const expectedEmail = emailAddress;
            const expectedPhone = 'some value returned';
            toDisplayPhoneNumber.mockImplementation((number) => (number === servicePhone ? expectedPhone : ''));
            const contactInfoSectionIndex = 1;
            wrapper.vm.getCmsContent.mockImplementation(() => '{custom:contactPhone} or {custom:contactEmail}');

            // Act
            await tpaSubmit.beforeRouteEnter.call(
                wrapper.vm,
                { query: { issPage: 'tpa-submit' } },
                undefined,
                (c) => c(wrapper.vm)
            );
            wrapper.vm.setSections();

            // Assert
            const contactInfoSection = wrapper.vm.sections[contactInfoSectionIndex];
            expect(contactInfoSection.lines[0]).toContain(expectedEmail);
            expect(contactInfoSection.lines[0]).toContain(expectedPhone);
            expect(contactInfoSection.lines[0]).toContain(`Ext. ${extension}`);
            expect(toDisplayPhoneNumber).toHaveBeenCalledWith(servicePhone);
        });
    });
    describe('computed', () => {
        test.each([
            ['subHeaderTitle', 'SiteSubHeaderWidget', widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT, 'site sub header'],
            ['subHeaderBodyOne', 'SiteSubHeaderWidget', widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT, 'sub header body one'],
            ['orderDetailsTitle', 'OrderDetailsContent', widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT, 'order details title'],
            ['forwardButtonText', 'SiteFooterWidget', widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT, 'forward button text']
        ])('computed %p returns expected value', (computedName, widgetLabel, fieldLabel, expected) => {
            // Arrange
            const mountOptions = getMountOptions();
            mountOptions.mixins = [{
                methods: {
                    getCmsContent: jest.fn().mockImplementation((widget, field) =>
                        (widget === widgetLabel && field === fieldLabel ? expected : ''))
                }
            }];
            const wrapper = shallowMount(tpaSubmit, mountOptions);

            // Act
            const result = wrapper.vm[computedName];

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

        describe.each([
            [false, coverageStatuses.PENDING],
            [true, coverageStatuses.VERIFIED],
            [false, coverageStatuses.NO_COVERAGE]
        ])('isVerified', (expected, status) => {
            test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)}`, () => {
                // Arrange
                const initialStore = {
                    order: {
                        insuranceCoverage: {
                            coverageStatus: status
                        }
                    }
                };
                const { wrapper } = getMountedComponent(initialStore);

                // Assert
                expect(wrapper.vm.isVerified).toBe(expected);
            });
        });
        test.each([
            [5, 5],
            [-1, -1],
            [0, 0],
            [null, null],
            [undefined, undefined]
        ])('currentDeductible returns %p when store value %p', (expected, storeValue) => {
            // Arrange
            const initialStore = {
                order: {
                    currentDeductible: {
                        replace: storeValue
                    }
                }
            };
            const { wrapper } = getMountedComponent(initialStore);

            // Assert
            expect(wrapper.vm.currentDeductible).toBe(expected);
        });
        describe.each([
            ['Verifying coverage', coverageStatuses.PENDING],
            ['$123.46', coverageStatuses.VERIFIED],
            ['Verifying coverage', coverageStatuses.NO_COVERAGE]
        ])('deductibleBoxValue', (expected, status) => {
            test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)}`, () => {
                // Arrange
                const initialStore = {
                    order: {
                        insuranceCoverage: {
                            coverageStatus: status
                        },
                        currentDeductible: 123.456
                    }
                };
                formatAmountInDollars.mockReturnValue('$123.46');
                const { wrapper } = getMountedComponent(initialStore);

                // Assert
                expect(wrapper.vm.deductibleBoxValue).toBe(expected);
            });
        });
    });
    describe('methods', () => {
        describe('getCustomValueFromString', () => {
            describe.each([
                [false, coverageStatuses.PENDING, 0],
                [false, coverageStatuses.PENDING, 100],
                [false, coverageStatuses.VERIFIED, 0],
                [true, coverageStatuses.VERIFIED, 100],
                [false, coverageStatuses.NO_COVERAGE, 0],
                [false, coverageStatuses.NO_COVERAGE, 100]
            ])('with argument deductibleAboveZero', (expected, status, deductible) => {
                test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)} and currentDeductible is ${deductible}`, () => {
                    // Arrange
                    const initialStore = {
                        order: {
                            insuranceCoverage: {
                                coverageStatus: status
                            },
                            currentDeductible: {
                                replace: deductible,
                                repair: deductible
                            }
                        }
                    };
                    const { wrapper } = getMountedComponent(initialStore);
                    const argument = 'deductibleAboveZero';

                    // Act
                    const result = wrapper.vm.getCustomValueFromString(argument);

                    // Assert
                    expect(result).toBe(expected);
                });
            });
            describe.each([
                [false, coverageStatuses.PENDING, 0],
                [false, coverageStatuses.PENDING, 100],
                [true, coverageStatuses.VERIFIED, 0],
                [false, coverageStatuses.VERIFIED, 100],
                [false, coverageStatuses.NO_COVERAGE, 0],
                [false, coverageStatuses.NO_COVERAGE, 100]
            ])('with argument zeroDeductible', (expected, status, deductible) => {
                test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)} and currentDeductible is ${deductible}`, () => {
                    // Arrange
                    const initialStore = {
                        order: {
                            insuranceCoverage: {
                                coverageStatus: status
                            },
                            currentDeductible: {
                                replace: deductible,
                                repair: deductible
                            }
                        }
                    };
                    const { wrapper } = getMountedComponent(initialStore);
                    const argument = 'zeroDeductible';

                    // Act
                    const result = wrapper.vm.getCustomValueFromString(argument);

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

            describe.each([
                [true, coverageStatuses.PENDING],
                [false, coverageStatuses.VERIFIED],
                [true, coverageStatuses.NO_COVERAGE]
            ])('with argument verifyingCoverage', (expected, status) => {
                test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)}`, () => {
                    // Arrange
                    const initialStore = {
                        order: {
                            insuranceCoverage: {
                                coverageStatus: status
                            }
                        }
                    };
                    const { wrapper } = getMountedComponent(initialStore);
                    const argument = 'verifyingCoverage';

                    // Act
                    const result = wrapper.vm.getCustomValueFromString(argument);

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

            test('with unknown argument returns null', () => {
                // Arrange
                const initialStore = {
                    order: {
                        payment: {
                            insuranceCoverage: { isVerified: true }
                        },
                        currentDeductible: 26
                    }
                };
                const { wrapper } = getMountedComponent(initialStore);
                const argument = 'some random string';

                // Act
                const result = wrapper.vm.getCustomValueFromString(argument);

                // Assert
                expect(result).toBe(null);
            });
        });
        test.each([
            [[1, 5, 6], [1, 5, 6]],
            [[], []],
            [undefined, []],
            [null, []]
        ])('getInputQuestionWidgetAnswersNullSafe', (rawAnswers, expected) => {
            // Arrange
            const widgetName = 'WidgetName';
            const { wrapper } = getMountedComponent();
            wrapper.vm.getCmsContent = jest.fn().mockImplementationOnce(() => rawAnswers);

            // Act
            const result = wrapper.vm.getInputQuestionWidgetAnswersNullSafe(widgetName);

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