import datePicker from "./date-picker";

// Supporting Files
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import store from "@/store";

// Constants
const mockValuesForSetDate01 = {
    overrideDate: "2023-12-01",
    monthEnd: "2023-12-31",
    initialViewWeeks: [
        {
            weekNum: 1,
            weekStartDate: "2023-12-01",
            weekEndDate: "2023-12-02",
        },
        {
            weekNum: 2,
            weekStartDate: "2023-12-03",
            weekEndDate: "2023-12-09",
        },
        {
            weekNum: 3,
            weekStartDate: "2023-12-10",
            weekEndDate: "2023-12-16",
        },
        {
            weekNum: 4,
            weekStartDate: "2023-12-17",
            weekEndDate: "2023-12-23",
        },
        {
            weekNum: 5,
            weekStartDate: "2023-12-24",
            weekEndDate: "2023-12-30",
        },
    ],
    config: {
        todayDate: "2023-12-01",
        initialViewStartDate: "2023-12-01",
        initialViewEndDate: "2023-12-30",
        calendarViewDirection: "future",
        initialShopTimeSlotsResponse: {
            days: [
                {
                    date: "2023-12-30",
                    timeSlots: [
                        {
                            id: "06747-01820-S-B*20453*7 AM",
                            startTime: "07:00",
                            endTime: "08:00",
                            offerPremium: false,
                        },
                    ],
                },
            ],
            estimatedServiceMinutesMinimum: 90,
            estimatedServiceMinutesMaximum: 120,
        },
        hideSomeDaysForInitialView: true,
        hideSecondMonth: true,
        preSelectedDate: null,
    },
    monthsData: [
        {
            monthLabel: "December",
            monthIndex: 12,
            monthString: "December-2023",
            yearNum: 2023,
            dates: [
                {
                    dateNum: 1,
                    dayClasses: " current-day",
                    inputValue: "2023-12-01",
                    isSelectable: false,
                },
                {
                    dateNum: 2,
                    dayClasses: "",
                    inputValue: "2023-12-02",
                    isSelectable: false,
                },
                {
                    dateNum: 3,
                    dayClasses: " sunday",
                    inputValue: "2023-12-03",
                    isSelectable: false,
                },
                {
                    dateNum: 4,
                    dayClasses: "",
                    inputValue: "2023-12-04",
                    isSelectable: false,
                },
                {
                    dateNum: 5,
                    dayClasses: "",
                    inputValue: "2023-12-05",
                    isSelectable: false,
                },
                {
                    dateNum: 6,
                    dayClasses: "",
                    inputValue: "2023-12-06",
                    isSelectable: false,
                },
                {
                    dateNum: 7,
                    dayClasses: "",
                    inputValue: "2023-12-07",
                    isSelectable: false,
                },
                {
                    dateNum: 8,
                    dayClasses: "",
                    inputValue: "2023-12-08",
                    isSelectable: false,
                },
                {
                    dateNum: 9,
                    dayClasses: "",
                    inputValue: "2023-12-09",
                    isSelectable: false,
                },
                {
                    dateNum: 10,
                    dayClasses: " sunday",
                    inputValue: "2023-12-10",
                    isSelectable: false,
                },
                {
                    dateNum: 11,
                    dayClasses: "",
                    inputValue: "2023-12-11",
                    isSelectable: false,
                },
                {
                    dateNum: 12,
                    dayClasses: "",
                    inputValue: "2023-12-12",
                    isSelectable: false,
                },
                {
                    dateNum: 13,
                    dayClasses: "",
                    inputValue: "2023-12-13",
                    isSelectable: false,
                },
                {
                    dateNum: 14,
                    dayClasses: "",
                    inputValue: "2023-12-14",
                    isSelectable: false,
                },
                {
                    dateNum: 15,
                    dayClasses: "",
                    inputValue: "2023-12-15",
                    isSelectable: false,
                },
                {
                    dateNum: 16,
                    dayClasses: "",
                    inputValue: "2023-12-16",
                    isSelectable: false,
                },
                {
                    dateNum: 17,
                    dayClasses: " sunday",
                    inputValue: "2023-12-17",
                    isSelectable: false,
                },
                {
                    dateNum: 18,
                    dayClasses: "",
                    inputValue: "2023-12-18",
                    isSelectable: false,
                },
                {
                    dateNum: 19,
                    dayClasses: "",
                    inputValue: "2023-12-19",
                    isSelectable: false,
                },
                {
                    dateNum: 20,
                    dayClasses: "",
                    inputValue: "2023-12-20",
                    isSelectable: false,
                },
                {
                    dateNum: 21,
                    dayClasses: "",
                    inputValue: "2023-12-21",
                    isSelectable: false,
                },
                {
                    dateNum: 22,
                    dayClasses: "",
                    inputValue: "2023-12-22",
                    isSelectable: false,
                },
                {
                    dateNum: 23,
                    dayClasses: "",
                    inputValue: "2023-12-23",
                    isSelectable: false,
                },
                {
                    dateNum: 24,
                    dayClasses: " sunday",
                    inputValue: "2023-12-24",
                    isSelectable: false,
                },
                {
                    dateNum: 25,
                    dayClasses: "",
                    inputValue: "2023-12-25",
                    isSelectable: false,
                },
                {
                    dateNum: 26,
                    dayClasses: "",
                    inputValue: "2023-12-26",
                    isSelectable: false,
                },
                {
                    dateNum: 27,
                    dayClasses: "",
                    inputValue: "2023-12-27",
                    isSelectable: false,
                },
                {
                    dateNum: 28,
                    dayClasses: "",
                    inputValue: "2023-12-28",
                    isSelectable: false,
                },
                {
                    dateNum: 29,
                    dayClasses: "",
                    inputValue: "2023-12-29",
                    isSelectable: false,
                },
                {
                    dateNum: 30,
                    dayClasses: "",
                    inputValue: "2023-12-30",
                    isSelectable: true,
                },
                {
                    dateNum: 31,
                    dayClasses: " sunday day-hidden",
                    inputValue: "2023-12-31",
                    isSelectable: false,
                },
            ],
            startDateDayIndex: 5,
            monthClass: "",
            isMonthThatHidesSomeDaysForInitialView: true,
        },
        {
            monthLabel: "January",
            monthIndex: 1,
            monthString: "January-2024",
            yearNum: 2024,
            dates: [
                {
                    dateNum: 1,
                    dayClasses: "",
                    inputValue: "2024-01-01",
                    isSelectable: false,
                },
                {
                    dateNum: 2,
                    dayClasses: "",
                    inputValue: "2024-01-02",
                    isSelectable: false,
                },
                {
                    dateNum: 30,
                    dayClasses: "",
                    inputValue: "2024-01-30",
                    isSelectable: false,
                },
                {
                    dateNum: 31,
                    dayClasses: "",
                    inputValue: "2024-01-31",
                    isSelectable: false,
                },
            ],
            startDateDayIndex: 1,
            monthClass: " month-hidden",
        },
    ],
};

const mockInitialData = {
    todayDate: "2023-07-04",
    initialViewStartDate: "2023-07-05",
    initialViewEndDate: "2023-08-19",
    calendarViewDirection: "future",
    initialShopTimeSlotsResponse: {
        days: [
            {
                date: "2023-07-25",
                timeSlots: [
                    {
                        id: "06747-01820-S-B*20295*7 AM",
                        startTime: "07:00",
                        endTime: "08:00",
                        offerPremium: false,
                    },
                    {
                        id: "06747-01820-S-B*20295*730 AM",
                        startTime: "07:30",
                        endTime: "08:30",
                        offerPremium: false,
                    },
                ],
            },
            {
                date: "2023-07-26",
                timeSlots: [
                    {
                        id: "06747-01820-S-B*20296*7 AM",
                        startTime: "07:00",
                        endTime: "08:00",
                        offerPremium: false,
                    },
                ],
            },
        ],
        estimatedServiceMinutesMinimum: 90,
        estimatedServiceMinutesMaximum: 120,
    },
    hideSomeDaysForInitialView: true,
    hideSecondMonth: false,
    preSelectedDate: null,
};

const mockCallbackResponse = {
    days: [
        {
            date: "2024-12-30",
            timeSlots: [
                {
                    id: "06747-01820-S-B*20453*7 AM",
                    startTime: "07:00",
                    endTime: "08:00",
                    offerPremium: false,
                },
            ],
        },
        {
            date: "2024-01-02",
            timeSlots: [
                {
                    id: "06747-01820-S-B*20453*7 AM",
                    startTime: "07:00",
                    endTime: "08:00",
                    offerPremium: false,
                },
            ],
        },
    ],
    estimatedServiceMinutesMinimum: 90,
    estimatedServiceMinutesMaximum: 120,
};

const mockPromiseResolve = (returnValue) =>
    jest.fn(async () => Promise.resolve({ valid: returnValue }));

describe("date-picker.vue", () => {
    describe("initial setup", () => {
        test("Expect initializeComponent to be run", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });
            wrapper.vm.initializeComponent = jest.fn().mockImplementation();
            wrapper.vm.initializeComponent(mockInitialData);

            // Assert
            expect(wrapper.vm.initializeComponent).toHaveBeenCalled();

            wrapper.unmount();
        });

        test("When initializeComponent runs, expect will run setCalendarData method", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });
            const spy = jest.spyOn(wrapper.vm, "setCalendarData");

            wrapper.vm.$nextTick();

            // Act
            wrapper.vm.initializeComponent(mockInitialData);

            // Assert
            expect(spy).toHaveBeenCalled();

            wrapper.unmount();
        });

        test("If prop customComponentId is supplied, expect it will be used for componentId in setup method", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    customComponentId: "dateQuestion",
                },
            });

            // Act
            const componentIdValue = wrapper.vm.componentId;

            // Assert
            expect(componentIdValue).toEqual("dateQuestion");

            wrapper.unmount();
        });

        test("If prop customSelectableDatesCallback is not supplied, expect the default function will return empty array", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });

            // Act
            const callbackResponse = wrapper.vm.customSelectableDatesCallback(
                "2023-07-24",
                "2023-08-19",
                "Inshop",
                "006747"
            );

            // Assert
            expect(callbackResponse).toEqual([]);

            wrapper.unmount();
        });

        test("expect a date string created from today", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });
            const todayDate = new Date();
            const todayDateString =
                todayDate.getFullYear() +
                "-" +
                ("0" + (todayDate.getMonth() + 1)).slice(-2) +
                "-" +
                ("0" + todayDate.getDate()).slice(-2);

            // Assert
            expect(wrapper.vm.todayString).toEqual(todayDateString);

            wrapper.unmount();
        });

        test("expect a date string created from todayOverrideDateString", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-02",
                },
            });

            // Assert
            expect(wrapper.vm.todayString).toEqual("2023-07-02");

            wrapper.unmount();
        });

        test("expect computed todayDayIndex returns the correct day index", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-02",
                },
            });

            // Assert
            expect(wrapper.vm.todayDayIndex).toEqual(0);

            wrapper.unmount();
        });

        test("expect computed todayDateNum returns the correct date number", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-02",
                },
            });

            // Assert
            expect(wrapper.vm.todayDateNum).toEqual(2);

            wrapper.unmount();
        });

        test("expect computed currentWeekStartDateNum returns the correct date number when todayDayIndex is < todayDateNum", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });

            // Assert
            expect(wrapper.vm.currentWeekStartDateNum).toEqual(2);

            wrapper.unmount();
        });

        test("expect computed currentWeekStartDateNum returns 1 when todayDayIndex is >= todayDateNum", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-01",
                },
            });

            // Assert
            expect(wrapper.vm.currentWeekStartDateNum).toEqual(1);

            wrapper.unmount();
        });

        test("expect computed currentWeekEndDateNum returns correct value", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });

            // Assert
            expect(wrapper.vm.currentWeekEndDateNum).toEqual(8);

            wrapper.unmount();
        });

        test("expect computed calendarViewDirection returns 'past' when prop selectableDatesSetting is 'past'", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "past",
                    todayOverrideDateString: "2023-07-05",
                },
            });

            // Assert
            expect(wrapper.vm.calendarViewDirection).toEqual("past");

            wrapper.unmount();
        });

        test("expect computed calendarViewDirection returns 'future' when prop selectableDatesSetting is 'custom'", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });

            // Assert
            expect(wrapper.vm.calendarViewDirection).toEqual("future");

            wrapper.unmount();
        });

        describe("when loadInitialData is run...", () => {
            test("and this.todayString exists, expect it should have a todayDate matching today", async () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const todayDate = new Date();
                const todayDateString =
                    todayDate.getFullYear() +
                    "-" +
                    ("0" + (todayDate.getMonth() + 1)).slice(-2) +
                    "-" +
                    ("0" + todayDate.getDate()).slice(-2);

                // Act
                const testResult = await wrapper.vm.loadInitialData({
                    selectableDatesSetting: "custom",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                    preSelectedDate: null,
                });

                // Assert
                expect(testResult).toMatchObject({
                    todayDate: todayDateString,
                });

                wrapper.unmount();
            });

            test("and overrride date exists, expect it should have a todayDate matching the override", async () => {
                // Arrange
                const overrideDate = mockValuesForSetDate01.overrideDate;
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: overrideDate,
                    },
                });

                // Act
                const localThis = {
                    todayString: null,
                    todayOverrideDateString: overrideDate,
                    getMonthEnd: () => mockValuesForSetDate01.monthEnd,
                    getInitialViewWeeks: () => mockValuesForSetDate01.initialViewWeeks,
                };
                const testResult = await datePicker.methods.loadInitialData.call(localThis, {
                    selectableDatesSetting: "custom",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                    preSelectedDate: null,

                    todayOverrideDateString: overrideDate,
                });

                // Assert
                expect(testResult).toMatchObject({
                    todayDate: overrideDate,
                });

                wrapper.unmount();
            });

            test("with no this.todayString or overrride date, expect it should have a todayDate matching today", async () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const todayDate = new Date();
                const todayDateString =
                    todayDate.getFullYear() +
                    "-" +
                    ("0" + (todayDate.getMonth() + 1)).slice(-2) +
                    "-" +
                    ("0" + todayDate.getDate()).slice(-2);

                // Act
                const localThis = {
                    todayString: null,
                    getMonthEnd: () => mockValuesForSetDate01.monthEnd,
                    getInitialViewWeeks: () => mockValuesForSetDate01.initialViewWeeks,
                };

                //Act
                const testResult = await datePicker.methods.loadInitialData.call(localThis, {
                    selectableDatesSetting: "custom",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                    preSelectedDate: null,
                });

                // Assert
                expect(testResult).toMatchObject({
                    todayDate: todayDateString,
                });

                wrapper.unmount();
            });

            test("and config.selectableDatesSetting is PAST, expect it should have a calendarViewDirection of PAST", async () => {
                // Arrange
                const { wrapper } = setupMocks({});

                // Act
                const testResult = await wrapper.vm.loadInitialData({
                    selectableDatesSetting: "past",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                });

                // Assert
                expect(testResult).toMatchObject({
                    calendarViewDirection: "past",
                });

                wrapper.unmount();
            });

            test("and config.selectableDatesSetting is CUSTOM, expect it should have a calendarViewDirection of FUTURE", async () => {
                // Arrange
                const { wrapper } = setupMocks({});

                // Act
                const testResult = await wrapper.vm.loadInitialData({
                    selectableDatesSetting: "custom",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                });

                // Assert
                expect(testResult).toMatchObject({
                    calendarViewDirection: "future",
                });

                wrapper.unmount();
            });
        });

        describe("when setCalendarData is run...", () => {
            test("expect that data elements are updated properly", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });

                // Act
                await wrapper.vm.setCalendarData(mockValuesForSetDate01.config);

                // Assert
                expect(wrapper.vm.hideSomeDaysForInitialView).toBe(true);
                expect(wrapper.vm.selectableDatesData[0]).toMatchObject({
                    date: "2023-12-30",
                });
                expect(wrapper.vm.selectableDatesData[0].timeSlots.length).toBeGreaterThan(0);
                expect(wrapper.vm.months.length).toBe(7);
                expect(wrapper.vm.months[0]).toMatchObject({
                    monthLabel: "December",
                    monthIndex: 12,
                    monthString: "December-2023",
                    yearNum: 2023,
                    startDateDayIndex: 5,
                    monthClass: "",
                    isMonthThatHidesSomeDaysForInitialView: true,
                });

                wrapper.unmount();
            });

            test("expect that this.months are reversed if direction is past", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "past",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });
                const modifiedConfig = { ...mockValuesForSetDate01.config };
                modifiedConfig.calendarViewDirection = "past";

                // Act
                await wrapper.vm.setCalendarData(modifiedConfig);

                // Assert
                expect(wrapper.vm.months.length).toBe(37);
                expect(wrapper.vm.months[0]).toMatchObject({
                    monthString: "December-2020",
                    startDateDayIndex: 2,
                    monthClass: " month-hidden last-available-month",
                });

                wrapper.unmount();
            });

            test("expect that if there's a pre-selected date that it will scroll to it", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });
                const modifiedConfig = { ...mockValuesForSetDate01.config };
                modifiedConfig.preSelectedDate = "2023-12-25";
                wrapper.vm.scrollToElement = jest.fn().mockImplementation();

                // Act
                await wrapper.vm.setCalendarData(modifiedConfig);
                await wrapper.vm.$nextTick();

                // Assert
                expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023");

                wrapper.unmount();
            });

            test("expect that if there's a pre-selected date within the last month available then the View More Dates button will be hidden", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });
                const modifiedConfig = { ...mockValuesForSetDate01.config };
                modifiedConfig.preSelectedDate = "2024-06-11";
                wrapper.vm.scrollToElement = jest.fn().mockImplementation();

                // Act
                await wrapper.vm.setCalendarData(modifiedConfig);
                await wrapper.vm.$nextTick();

                // Assert
                expect(wrapper.vm.disableViewMoreDatesButton).toBe(true);

                wrapper.unmount();
            });

            test("expect that this.months are reversed if direction is past", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "past",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });
                const modifiedConfig = { ...mockValuesForSetDate01.config };
                modifiedConfig.calendarViewDirection = "past";

                // Act
                await wrapper.vm.setCalendarData(modifiedConfig);

                // Assert
                expect(wrapper.vm.months.length).toBe(37);
                expect(wrapper.vm.months[0]).toMatchObject({
                    monthString: "December-2020",
                    startDateDayIndex: 2,
                    monthClass: " month-hidden last-available-month",
                });

                wrapper.unmount();
            });

            test("expect that if there's a pre-selected date that it will scroll to it", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    },
                });
                const modifiedConfig = { ...mockValuesForSetDate01.config };
                modifiedConfig.preSelectedDate = "2023-12-25";
                wrapper.vm.scrollToElement = jest.fn().mockImplementation();

                // Act
                await wrapper.vm.setCalendarData(modifiedConfig);
                await wrapper.vm.$nextTick();

                // Assert
                expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023");

                wrapper.unmount();
            });
        });

        describe("when getInitialViewWeeks is run...", () => {
            test("expect that 5 weeks will be returned when no preselected date", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const todayString = "2023-02-01";

                // Act
                const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5);

                // Assert
                expect(testResult.length).toBe(5);

                wrapper.unmount();
            });

            test("expect that today will be in the start/end range of dates of the first week when no preselected date", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const todayString = "2023-02-01";

                // Act
                const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5);

                // Assert
                expect(
                    testResult[0].weekStartDate <= todayString &&
                        todayString <= testResult[0].weekEndDate
                ).toBe(true);

                wrapper.unmount();
            });

            test("expect that single weeks that span 2 months will be split into separate shorter 'week' spans", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const todayString = "2023-02-15";

                // Act
                const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5);

                // Assert
                expect(testResult.length).toBe(5);
                expect(testResult[0]).toMatchObject({
                    weekStartDate: "2023-02-12",
                });
                expect(testResult[testResult.length - 1]).toMatchObject({
                    weekEndDate: "2023-03-11",
                });

                wrapper.unmount();
            });

            test("expect that if today is 7/31/23 and there's a preselected date of 10/1/23 that there will be 14 initial weeks returned", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const mockPreSelectedDate = "2023-10-01";

                // Act
                const testResult = wrapper.vm.getInitialViewWeeks(
                    "2023-07-31",
                    5,
                    mockPreSelectedDate
                );

                // Assert
                expect(testResult.length).toBe(14);

                wrapper.unmount();
            });

            test("expect that if there's a preselected date that the last week end date will be the end of the month of the preselected date", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                const mockPreSelectedDate = "2023-10-01";

                // Act
                const testResult = wrapper.vm.getInitialViewWeeks(
                    "2023-07-31",
                    5,
                    mockPreSelectedDate
                );

                // Assert
                expect(testResult[testResult.length - 1]).toMatchObject({
                    weekEndDate: "2023-10-31",
                });

                wrapper.unmount();
            });
        });

        describe("when there's a pre-selected date...", () => {
            test("and loadInitialData runs, expect hideSomeDaysForInitialView and hideSecondMonth to be false", async () => {
                // Arrange
                // uses "2023-12-01" which would set both hideSomeDaysForInitialView and hideSecondMonth to true, unless there was a pre-selected date
                const overrideDate = mockValuesForSetDate01.overrideDate;
                const mockMonthEnd = mockValuesForSetDate01.monthEnd;
                const mockInitialViewWeeks = mockValuesForSetDate01.initialViewWeeks;
                const { wrapper } = setupMocks({
                    propsData: {
                        selectableDatesSetting: "custom",
                        todayOverrideDateString: overrideDate,
                    },
                });

                // Act
                const localThis = {
                    todayString: null,
                    todayOverrideDateString: overrideDate,
                    getMonthEnd: () => mockMonthEnd,
                    getInitialViewWeeks: () => mockInitialViewWeeks,
                };
                const testResult = await datePicker.methods.loadInitialData.call(localThis, {
                    selectableDatesSetting: "custom",
                    initialViewRowsToShow: 5,
                    customSelectableDatesCallback: mockPromiseResolve,
                    preSelectedDate: "2024-01-01",
                    todayOverrideDateString: overrideDate,
                });

                // Assert
                expect(testResult).toMatchObject({
                    hideSomeDaysForInitialView: false,
                    hideSecondMonth: false,
                });

                wrapper.unmount();
            });
        });
    });

    describe("showAnotherMonth method...", () => {
        test("expect that when hideSomeDaysForInitialView is true that it scrolls to correct month", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                },
            });
            wrapper.vm.months = mockValuesForSetDate01.monthsData;
            wrapper.vm.updateSelectableDates = jest.fn().mockImplementation();
            wrapper.vm.scrollToElement = jest.fn().mockImplementation();
            wrapper.vm.hideSomeDaysForInitialView = true;

            // Act
            await wrapper.vm.showAnotherMonth();

            // Assert
            expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
                "2023-12-31",
                "2023-12-31"
            );
            expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023");

            wrapper.unmount();
        });

        test("expect that when hideSomeDaysForInitialView is false and direction is FUTURE that it scrolls to correct month", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                },
            });
            wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original
            wrapper.vm.updateSelectableDates = jest.fn().mockImplementation();
            wrapper.vm.scrollToElement = jest.fn().mockImplementation();
            wrapper.vm.hideSomeDaysForInitialView = false;

            // Act
            await wrapper.vm.showAnotherMonth();

            // Assert
            expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
                "2024-01-01",
                "2024-01-31"
            );
            expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("January-2024");

            wrapper.unmount();
        });

        test("expect that when hideSomeDaysForInitialView is false and direction is PAST that it scrolls to correct month", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "past",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                },
            });
            // Force correct state for this test. // TODO - figure out why original array is getting mutated here, making next line necessary
            mockValuesForSetDate01.monthsData[1].monthClass = " month-hidden";

            wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original
            wrapper.vm.updateSelectableDates = jest.fn().mockImplementation();
            wrapper.vm.scrollToElement = jest.fn().mockImplementation();
            wrapper.vm.hideSomeDaysForInitialView = false;

            // Act
            await wrapper.vm.showAnotherMonth();

            // Assert
            expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
                "2024-01-01",
                "2024-01-31"
            );
            expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("January-2024");

            wrapper.unmount();
        });

        test("expect that when the month to scroll to is the last available month that it will disable the View More Dates button", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                },
            });
            wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original
            wrapper.vm.updateSelectableDates = jest.fn().mockImplementation();
            wrapper.vm.scrollToElement = jest.fn().mockImplementation();
            wrapper.vm.disableViewMoreDatesButton = false;
            mockValuesForSetDate01.monthsData[1].monthClass = " month-hidden last-available-month"; // Force correct state for this test.

            // Act
            await wrapper.vm.showAnotherMonth();

            // Assert
            expect(wrapper.vm.disableViewMoreDatesButton).toBe(true);

            wrapper.unmount();
        });
    });

    describe("updateSelectableDates method...", () => {
        test("expect that new dates from callback are added to selectableDatesData", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    customSelectableDatesCallback: jest.fn(() => mockCallbackResponse),
                },
            });
            wrapper.vm.selectableDatesData = [
                {
                    date: "2023-12-30",
                    timeSlots: [
                        {
                            id: "06747-01820-S-B*20453*7 AM",
                            startTime: "07:00",
                            endTime: "08:00",
                            offerPremium: false,
                        },
                    ],
                },
            ];
            wrapper.vm.months = mockValuesForSetDate01.monthsData;

            // Act
            await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05");
            const isFoundInArray = wrapper.vm.selectableDatesData.find((obj) => {
                return obj.date === "2024-01-02";
            });

            // Assert
            expect(isFoundInArray).not.toBeUndefined();

            wrapper.unmount();
        });

        test("expect that isSelectable flag on selectable dates is set to true", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                    customSelectableDatesCallback: jest.fn(() => mockCallbackResponse),
                },
            });
            wrapper.vm.selectableDatesData = [
                {
                    date: "2023-12-30",
                    timeSlots: [
                        {
                            id: "06747-01820-S-B*20453*7 AM",
                            startTime: "07:00",
                            endTime: "08:00",
                            offerPremium: false,
                        },
                    ],
                },
            ];
            wrapper.vm.months = mockValuesForSetDate01.monthsData;

            // Act
            await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05");
            const targetMonth = wrapper.vm.months.find((month) => {
                return month.monthString === "January-2024";
            });
            const dateInArray = targetMonth.dates.find((date) => {
                return date.inputValue === "2024-01-02";
            });

            // Assert
            expect(dateInArray).toMatchObject({
                isSelectable: true,
            });

            wrapper.unmount();
        });
    });

    describe("scrollToElement method...", () => {
        test("expect that new dates from callback are added to selectableDatesData", () => {
            // Arrange
            const container = document.createElement("div");
            container.classList.add("page-container-grouped-styles");
            const targetElement = document.createElement("div");
            targetElement.setAttribute("id", "January-2024");
            container.appendChild(targetElement);
            document.body.appendChild(container);
            window.requestAnimationFrame = jest.fn();
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: mockValuesForSetDate01.overrideDate,
                },
            });

            // Act
            wrapper.vm.scrollToElement("January-2024");

            // Assert
            expect(window.requestAnimationFrame).toBeCalled();

            wrapper.unmount();
        });
    });

    describe("If user selects a date...", () => {
        test("expect it will emit update:modelValue", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });
            const dateToSelect = "2023-07-07";

            // Act
            wrapper.setValue({ selectedDate: dateToSelect });

            // Assert
            expect(wrapper.emitted()["update:modelValue"][0]).toEqual([
                { selectedDate: "2023-07-07" },
            ]);

            wrapper.unmount();
        });

        test("expect watched modelValue will be triggered", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });
            const dateToSelect = "2023-07-07";
            wrapper.vm.resetField = jest.fn();

            // Act
            wrapper.vm.$options.watch.modelValue.call(wrapper.vm, dateToSelect);

            // Assert
            expect(wrapper.vm.resetField).toBeCalled();
            expect(wrapper.vm.resetField).toBeCalledWith({ value: dateToSelect });

            wrapper.unmount();
        });
    });

    describe("If fireDateSelectedEvent runs... ", () => {
        test("expect it will emit date-clicked on mouse clicks ", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });

            // Act
            wrapper.vm.fireDateSelectedEvent({});
            const flattenedEmitted = JSON.stringify(wrapper.emitted());

            // Assert
            expect(wrapper.emitted()).toHaveProperty("date-clicked");
            expect(flattenedEmitted.includes("date-clicked")).toBeTruthy;

            wrapper.unmount();
        });

        test("expect it will NOT emit date-clicked on keyboard arrow navigation ", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                },
            });

            // Act
            wrapper.vm.fireDateSelectedEvent({ screenX: 0, screenY: 0 });
            const flattenedEmitted = JSON.stringify(wrapper.emitted());

            // Assert
            expect(wrapper.emitted()).not.toHaveProperty("date-clicked");
            expect(flattenedEmitted.includes("date-clicked")).toBe(false);

            wrapper.unmount();
        });
    });

    describe("If user triggers a validation error...", () => {
        test("expect that watched errorMessage will be triggered", () => {
            // Arrange
            const { wrapper } = setupMocks({
                propsData: {
                    selectableDatesSetting: "custom",
                    todayOverrideDateString: "2023-07-05",
                },
            });
            wrapper.vm.scrollToElement = jest.fn();

            // Act
            wrapper.vm.$options.watch.errorMessage.call(wrapper.vm, "anything");

            // Assert
            expect(wrapper.vm.scrollToElement).toBeCalled();
            expect(wrapper.vm.scrollToElement).toBeCalledWith("date-of-month-error");

            wrapper.unmount();
        });
    });
});

function setupMocks(mountOptionsMockData = {}) {
    store.getters = {
        order: {
            serviceLocation: { appointmentType: "Inshop", provider: { providerNumber: "12345" } },
        },
    };
    const initialMountOptionsMockData = {
        store: {
            getters: store.getters,
        },
    };
    const mountOptions = getMountOptions(
        Object.assign(initialMountOptionsMockData, mountOptionsMockData)
    );

    const wrapper = shallowMount(datePicker, mountOptions);

    return { wrapper };
}
