<template>
    <Form
        ref="theForm"
        @submit="onSubmit"
        @invalidSubmit="onInvalidSubmit">
        <div class="fade-on-route-transition">
            <div class="justify-content-center">
                <siteHeader
                    class="header"
                    cmsWidgetName="SiteHeaderWidget" />
            </div>
            <div class="iss-heritage-container-width">
                <div class="schedule-page-container iss-heritage-content-container-width">
                    <siteSubHeader
                        cmsWidgetName="ScheduleSubHeaderWidget"
                        secondaryTextClasses="text-center small sub-text"
                        class="mt-4" />
                    <div class="service-location-content-container">
                        <serviceLocation
                            ref="serviceLocation"
                            @appointmentTypeChanged="appointmentTypeChangedFromServiceLocation"
                            @cityUpdated="cityUpdatedFromServiceLocation"
                            @mobileZipUpdated="mobileZipUpdatedFromServiceLocation"
                            @providerChanged="providerChangedFromServiceLocation" />
                    </div>
                </div>
                <div
                    v-show="hasNeededServiceLocationData"
                    class="date-picker-container iss-heritage-content-container-width">
                    <div class="date-picker-content-container">
                        <locationAlerts
                            ref="locationAlerts"
                            cmsWidgetPrefix="LocationAlert-"
                            :city="selectedServiceLocationCity" />
                        <datePicker
                            ref="datePicker"
                            v-model="selectedTimeSlotInfo"
                            customComponentId="dateQuestion"
                            selectableDatesSetting="custom"
                            class="text-link-small"
                            :activeAppointmentType="selectedAppointmentType"
                            :isMobileView="isMobileView"
                            :showTimeSlotError="showDatePickerError"
                            :customSelectableDatesCallback="getAvailableDatesMethod"
                            @dateSelected="dateSelectedFromPicker"
                            @timeSlotSelected="timeSlotSelectedFromPicker" />
                    </div>
                </div>
                <div class="site-footer-container iss-heritage-content-container-width">
                    <siteFooter
                        ref="navbar"
                        class="mt-5"
                        cmsWidgetName="SiteFooterWidget"
                        :isForwardButtonHidden="!hasNeededServiceLocationData"
                        :isForwardButtonNavigationDisabled="!isFormValid"
                        @backClicked="navigateBack(this, navigateBackScenario)"
                        @forwardClicked="forwardButtonAction" />
                </div>
            </div>
        </div>
    </Form>
</template>
<script>
// Components
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
import locationAlerts from '@/layouts/schedule-page/location-alerts/location-alerts.vue';
import datePicker from '@/digital-components/date-picker/date-picker.vue';
import serviceLocation from '@/layouts/schedule-page/service-location/service-location.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';

// Supporting files
import {
    AppointmentTypeStrings,
    GET_MOBILE_TIME_SLOTS,
    GET_SHOP_TIME_SLOTS,
    PREMIUM_FEE_PART_TYPE
} from '@/constants/schedule-constants.js';
import {
    fetchCmsContentForPage,
    splitCopyOnCMSPlaceHolder
} from '@/helpers/cms-content-helper';
import {
    calcDaysBetweenDates,
    convertDateToDateString,
    sumDateString
} from '@/helpers/date-helper';
import settleAllPromises from '@/helpers/layout-helper';
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
import {
    getPricedMobileFeePart,
    getServiceabilityDetails,
    getZipCodeData
} from '@/helpers/service-location-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store';

// Define constants
const TIME_SLOTS_CALL_DAYS_LIMIT = 15;

const getAvailableDates = async (
    startDateString,
    endDateString,
    appointmentType,
    providerNumber,
    mobileZipCodeOverride = null
) => {
    const apiEndDateLimit = sumDateString(
        startDateString,
        TIME_SLOTS_CALL_DAYS_LIMIT - 1
    );
    const difference = calcDaysBetweenDates(startDateString, endDateString);
    const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
    const storeActionConfigs = [];
    let apiStartDate = startDateString;
    let apiEndDate = endDateString;

    for (let i = 1; i <= apiCallsCount; i++) {
        let storeActionConfig;

        if (i > 1) {
            apiStartDate = sumDateString(apiEndDate, 1);
            apiEndDate = sumDateString(
                apiStartDate,
                TIME_SLOTS_CALL_DAYS_LIMIT - 1
            );

            if (i === apiCallsCount) {
                apiEndDate = endDateString;
            }
        } else if (apiEndDate > apiEndDateLimit) {
            apiEndDate = apiEndDateLimit;
        }

        if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
            storeActionConfig = {
                storeAction: GET_MOBILE_TIME_SLOTS,
                payload: {
                    startDate: apiStartDate,
                    endDate: apiEndDate,
                    zipCodeOverride: mobileZipCodeOverride
                }
            };
        } else {
            storeActionConfig = {
                storeAction: GET_SHOP_TIME_SLOTS,
                payload: {
                    startDate: apiStartDate,
                    endDate: apiEndDate,
                    shopAppointmentType: appointmentType,
                    providerNumber
                }
            };
        }
        if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig);
    }

    const timeSlotsResponsesData = {
        days: []
    };

    function compareDayStrings(a, b) {
        if (a.date < b.date) return -1;
        if (a.date > b.date) return 1;
        return 0;
    }

    const makeParallelCalls = async () => {
        await Promise.all(storeActionConfigs.map(async (storeAction) => {
            let timeSlotsResponse = null;
            if (storeAction.storeAction === GET_SHOP_TIME_SLOTS) {
                timeSlotsResponse = await useMainStore().getShopTimeSlots(
                    storeAction.payload.startDate,
                    storeAction.payload.endDate,
                    storeAction.payload.shopAppointmentType,
                    storeAction.payload.providerNumber
                );
            } else if (storeAction.payload?.zipCodeOverride) {
                timeSlotsResponse = await useMainStore().getMobileTimeSlots(
                    storeAction.payload.startDate,
                    storeAction.payload.endDate,
                    storeAction.payload.zipCodeOverride
                ).catch((error) => {
                    console.log('Error fetching mobile time slots:', error);
                });
            }

            if (!timeSlotsResponse || !timeSlotsResponse.data) {
                return;
            }

            timeSlotsResponsesData.estimatedServiceMinutesMinimum = timeSlotsResponse.data.estimatedServiceMinutesMinimum;
            timeSlotsResponsesData.estimatedServiceMinutesMaximum = timeSlotsResponse.data.estimatedServiceMinutesMaximum;
            timeSlotsResponsesData.days = [
                ...timeSlotsResponsesData.days,
                ...timeSlotsResponse.data.days
            ];
        }));
    };

    return makeParallelCalls().then(() => {
        // sort days chronologically
        timeSlotsResponsesData.days.sort(compareDayStrings);
        return timeSlotsResponsesData;
    });
};

export default {
    name: 'schedule-page',
    components: {
        siteHeader,
        siteSubHeader,
        locationAlerts,
        datePicker,
        serviceLocation,
        siteFooter,
        // eslint-disable-next-line vue/no-reserved-component-names
        Form
    },
    mixins: [BaseFormMixin],
    async beforeRouteEnter(to, from, next) {
        // Call APIs
        const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
        const storeServiceLocation = useMainStore().order.serviceLocation;
        const storeSelectedAppointmentType = storeServiceLocation?.appointmentType;
        const storeSelectedProvider = storeServiceLocation?.provider;
        const serviceZipCode = storeServiceLocation?.zipCode || useMainStore().order.customer.address.zipCode;
        const zipCodeData = getZipCodeData(serviceZipCode);
        const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
        const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode);
        const getGlassFeesPromise = useMainStore().getGlassFees();
        const providersPromise = useMainStore().getProviders(serviceZipCode);

        const premiumFeePromise = useMainStore().getMobilePremiumFee();
        const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
            if (result.data) {
                return useMainStore().getCombinedQuote(result.data);
            }
            return result.data;
        });

        // Settle promises and get results
        const promiseResultMap = [
            {
                resultKey: 'cmsContent',
                promise: cmsContentPromise
            },
            {
                resultKey: 'mobileFeePart',
                promise: mobileFeePartPromise
            },
            {
                resultKey: 'glassFees',
                promise: getGlassFeesPromise
            },
            {
                resultKey: 'serviceabilityDetails',
                promise: serviceabilityDetailsPromise
            },
            {
                resultKey: 'zipCodeData',
                promise: zipCodeData
            },
            {
                resultKey: 'providers',
                promise: providersPromise
            },
            {
                resultKey: 'premiumFeeWithPrice',
                promise: premiumFeeWithPricePromise
            }
        ];

        // use resultMap to populate layout content.
        const resultMap = await settleAllPromises(promiseResultMap);
        const serviceLocationData = {
            glassFees: resultMap.glassFees,
            mobileFeePart: resultMap.mobileFeePart,
            providers: resultMap.providers,
            serviceabilityDetails: resultMap.serviceabilityDetails,
            zipCodeData: resultMap.zipCodeData
        };
        useMainStore().updateIsSafeliteProvider(true);
        next(async (vm) => {
            const providerToUse = resultMap.providers?.shopProviders
                .find((provider) => provider.providerNumber === storeSelectedProvider?.providerNumber) || resultMap.providers?.shopProviders[0];
            const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE
                || storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;
            const initialServiceLocationObj = {
                mobileProviderNumber: isMobileAppointment
                    ? storeSelectedProvider?.providerNumber
                    : null,
                provider: isMobileAppointment ? null : providerToUse,
                zipCode: useMainStore().order.serviceLocation.zipCode || useMainStore().order.customer.address.zipCode,
                zipCodeCtu: resultMap.zipCodeData?.zipCodeCtu
            };

            vm.setCmsContent(resultMap.cmsContent);
            vm.$refs.serviceLocation.initializeComponent(serviceLocationData);
            await vm.setData(resultMap.premiumFeeWithPrice, initialServiceLocationObj);
            showIssLoadingModal(false);
        });
    },
    setup() {
        const mainStore = useMainStore();
        return { mainStore };
    },
    data() {
        return {
            inShopDatesData: [],
            isMobileView: false,
            mobileDatesData: [],
            mobileFeePart: null,
            mobilePremiumAppointmentFee: null,
            mobileProviderNumber: null,
            mobileZipCodeOverride: null,
            selectableDatesData: [],
            selectedAppointmentType: this.getAppointmentType(),
            selectedDate: this.getSelectedDate(),
            selectedProvider: this.getSelectedProvider(),
            selectedServiceLocation: this.getServiceLocation(),
            selectedServiceLocationCity: this.getServiceLocationCity(),
            selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
            showDatePickerError: false
        };
    },
    computed: {
        hasNeededServiceLocationData() {
            const serviceLocationToUse = this.selectedServiceLocation;
            if (!serviceLocationToUse || !this.selectedAppointmentType) {
                return false;
            }

            const serviceLocationPreReqs = serviceLocationToUse.zipCode !== null
                && serviceLocationToUse.zipCodeCtu !== null
                && serviceLocationToUse.appointmentType !== null
                && (((this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
                        || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP)
                        && serviceLocationToUse.mobileProviderNumber)
                    || (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP
                        && (this.selectedProvider?.providerNumber || serviceLocationToUse.provider?.providerNumber)));

            const damageInfo = useMainStore().order.damage.isRepair
                || (useMainStore().order.lineItems?.glassParts != null && useMainStore().order.lineItems.glassParts.length > 0);

            const supportingItems = this.supportingItems !== null;

            return (serviceLocationPreReqs && supportingItems && damageInfo);
        },
        isFormValid() {
            const hasTimeSlotSelected = this.selectedTimeSlotInfo?.timeSlot?.routeCode != null;
            return hasTimeSlotSelected;
        },
        navigateBackScenario() {
            const { isNoComp, isITAC } = useMainStore();
            return isNoComp || isITAC
                ? this.navigationScenarios.CLICKED_BACK_CANNOT_REACH_TPA_FLOW
                : this.navigationScenarios.CLICKED_BACK_CAN_REACH_TPA_FLOW;
        },
        supportingItems() {
            return useMainStore().lineItems.supportingItems;
        }
    },
    mounted() {
        showIssLoadingModal(true);
        this.mql = window.matchMedia('(min-width: 1200px)');
        this.isMobileView = !this.mql.matches;
        this.mql.addEventListener('change', this.handleMqlChange);
    },
    unmounted() {
        if (this.mql) {
            this.mql.removeEventListener('change', this.handleMqlChange);
        }
    },
    methods: {
        splitCopyOnCMSPlaceHolder,
        arePagePrerequisitesValid() {
            return useMainStore().order.serviceLocation.zipCode !== null;
        },
        async setData(premiumFeeWithPriceResponse, initialServiceLocationObj) {
            this.selectedServiceLocation = initialServiceLocationObj;
            this.mobilePremiumAppointmentFee = premiumFeeWithPriceResponse
                ? premiumFeeWithPriceResponse[0]
                : null;

            if (initialServiceLocationObj?.zipCodeCtu) {
                this.$refs.locationAlerts.loadInitialData(
                    initialServiceLocationObj.zipCodeCtu,
                    initialServiceLocationObj.provider?.ctu
                ).then((alertReasons) => {
                    this.$refs.locationAlerts.initializeComponent(alertReasons.data);
                });
            }

            if (this.selectedAppointmentType !== AppointmentTypeStrings.MOBILE
                && this.selectedAppointmentType !== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
                await this.getDatePickerInitialData(initialServiceLocationObj.provider?.providerNumber).then((initialData) => {
                    this.selectableDatesData = initialData.initialShopTimeSlotsResponse;
                    this.$refs.datePicker.setCalendarData(initialData);
                });
            } else {
                this.mobileProviderNumber = initialServiceLocationObj.mobileProviderNumber;
                await this.getDatePickerInitialData().then((initialData) => {
                    this.selectableDatesData = initialData.initialShopTimeSlotsResponse;
                    this.$refs.datePicker.setCalendarData(initialData);
                });
            }
        },
        appointmentTypeChangedFromServiceLocation(newAppointmentType) {
            this.selectedAppointmentType = newAppointmentType.appointmentType;
            if (newAppointmentType.appointmentType === AppointmentTypeStrings.IN_SHOP) {
                if (this.inShopDatesData?.initialShopTimeSlotsResponse?.days?.length > 0) {
                    this.selectableDatesData = this.inShopDatesData.initialShopTimeSlotsResponse;
                    this.$refs.datePicker.initializeComponent(this.inShopDatesData);
                }
            } else if (this.mobileDatesData?.initialShopTimeSlotsResponse?.days?.length > 0) {
                this.selectableDatesData = this.mobileDatesData.initialShopTimeSlotsResponse;
                this.$refs.datePicker.initializeComponent(this.mobileDatesData);
            }
        },
        cityUpdatedFromServiceLocation(newCity) {
            this.selectedServiceLocationCity = newCity;
        },
        dateSelectedFromPicker(date) {
            this.selectedDate = date;
            this.showDatePickerError = false;
        },
        getAppointmentType() {
            return this.mainStore.order?.serviceLocation?.appointmentType;
        },
        async getAvailableDatesMethod(startDate, endDate) {
            const newShopTimeSlots = await getAvailableDates(
                startDate,
                endDate,
                this.selectedAppointmentType,
                this.selectedProvider.providerNumber
            );
            // ADD API CALL RESULTS TO EXISTING DATE DATA
            this.selectableDatesData.days =
                this.selectableDatesData.days.concat(newShopTimeSlots.days);
            return newShopTimeSlots;
        },
        getAvailableDates,
        async getDatePickerInitialData(defaultProviderNumber = null) {
            this.inShopDatesData = [];
            this.mobileDatesData = [];

            let todayDateString;
            const todayDateObject = new Date();
            const calendarViewDirection = 'future';
            const initialDays = 15;

            if (this.todayString) {
                todayDateString = this.todayString;
            } else {
                todayDateString = convertDateToDateString(todayDateObject);
            }

            const initialViewStartDate = todayDateString;
            const initialEndDate = new Date();
            initialEndDate.setDate(initialEndDate.getDate() + (initialDays - 1));
            let initialViewEndDate = convertDateToDateString(initialEndDate);
            const preSelectedDate = this.selectedDate;

            const endDateObject = this.getEndDateForExistingDate(
                todayDateObject,
                preSelectedDate,
                initialDays
            );

            if (endDateObject.endDateString !== initialViewEndDate) {
                initialViewEndDate = endDateObject.endDateString;
            }

            let mobileProviderNumberToUse = defaultProviderNumber;
            if (this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
                || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
                mobileProviderNumberToUse = this.selectedServiceLocation.mobileProviderNumber;
            }

            const initialData = await getAvailableDates(
                initialViewStartDate,
                initialViewEndDate,
                this.selectedAppointmentType || AppointmentTypeStrings.IN_SHOP,
                this.selectedProvider?.providerNumber || mobileProviderNumberToUse,
                this.selectedServiceLocation.zipCode
            ).then((response) => ({
                todayDate: todayDateString,
                initialViewStartDate,
                initialViewEndDate,
                calendarViewDirection,
                initialShopTimeSlotsResponse: response,
                preSelectedDate,
                initialDaysLoaded: endDateObject.newDaysLoaded || initialDays,
                daysFromStart: endDateObject.daysFromStart || null
            }));

            if (this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
                || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
                this.mobileDatesData = initialData;

                if (this.selectedProvider?.providerNumber) {
                    const inShopData = await getAvailableDates(
                        initialViewStartDate,
                        initialViewEndDate,
                        AppointmentTypeStrings.IN_SHOP,
                        this.selectedProvider?.providerNumber || defaultProviderNumber,
                        this.selectedServiceLocation.zipCode
                    ).then((response) => ({
                        todayDate: todayDateString,
                        initialViewStartDate,
                        initialViewEndDate,
                        calendarViewDirection,
                        initialShopTimeSlotsResponse: response,
                        preSelectedDate,
                        initialDaysLoaded: endDateObject.newDaysLoaded || initialDays,
                        daysFromStart: endDateObject.daysFromStart || null
                    }));

                    this.inShopDatesData = inShopData;
                }
            } else {
                this.inShopDatesData = initialData;
            }

            return initialData;
        },
        getEndDateForExistingDate(startDate, selectedDateString, daysInView) {
            const endDate = new Date(startDate);
            endDate.setDate(endDate.getDate() + (daysInView - 1));

            if (selectedDateString) {
                const selectedDate = new Date(`${selectedDateString}T00:00:00`);
                const daysDifferenceComparedToStartDate = Math.ceil((selectedDate - startDate) / (1000 * 60 * 60 * 24));
                if (selectedDate > endDate) {
                    const daysDifferenceComparedToStartDateAsPages = Math.ceil(daysDifferenceComparedToStartDate / daysInView);
                    const newEndDate = new Date(startDate);
                    newEndDate.setDate(newEndDate.getDate() + (daysDifferenceComparedToStartDateAsPages * daysInView) - 1);

                    return {
                        endDateString: convertDateToDateString(newEndDate),
                        newDaysLoaded: (daysDifferenceComparedToStartDateAsPages * daysInView),
                        daysFromStart: daysDifferenceComparedToStartDate
                    };
                }

                return {
                    endDateString: convertDateToDateString(endDate),
                    newDaysLoaded: null,
                    daysFromStart: daysDifferenceComparedToStartDate
                };
            }

            return {
                endDateString: convertDateToDateString(endDate),
                newDaysLoaded: null,
                daysFromStart: null
            };
        },
        getServiceZipCtuCodeFromStore() {
            return this.mainStore.order.serviceLocation.zipCodeCtu;
        },
        getSelectedDate() {
            return this.mainStore.order.schedule.date;
        },
        getSelectedProvider() {
            return this.mainStore.order.serviceLocation.provider;
        },
        getSelectedTimeSlotInfo() {
            const isPremiumAppointment =
                !!(
                    this.supportingItems?.filter((lineItem) =>
                        lineItem.partType === PREMIUM_FEE_PART_TYPE) ?? []
                ).length > 0;

            const selectedTimeSlotInfo = {
                timeSlot: this.mainStore.order.schedule,
                isPremiumAppointment
            };

            return selectedTimeSlotInfo;
        },
        getServiceLocation() {
            return this.mainStore.order.serviceLocation;
        },
        getServiceLocationCity() {
            return this.mainStore.order.serviceLocation?.city;
        },
        handleMqlChange(e) {
            this.isMobileView = !e.matches;
        },
        mobileZipUpdatedFromServiceLocation(mobileZipServiceLocation) {
            if (mobileZipServiceLocation) {
                this.mobileProviderNumber = mobileZipServiceLocation.mobileProviderNumber;
                this.selectedServiceLocation = mobileZipServiceLocation;

                if (mobileZipServiceLocation.refreshDatePicker) {
                    this.refreshDatePicker();
                }
            }
        },
        providerChangedFromServiceLocation(newProvider) {
            this.selectedProvider = newProvider?.provider;

            if (newProvider?.refreshDatePicker) {
                showIssLoadingModal(true);
                this.refreshDatePicker();
            }
        },
        async refreshDatePicker() {
            this.resetLocalFlags();
            await this.getDatePickerInitialData().then((data) => {
                this.selectableDatesData = data.initialShopTimeSlotsResponse;
                this.$refs.datePicker.initializeComponent(data);
                showIssLoadingModal(false);
            });
        },
        resetLocalFlags() {
            this.selectedDate = null;
            this.selectedTimeSlotInfo = null;
            this.showDatePickerError = false;
        },
        timeSlotSelectedFromPicker(timeSlot) {
            this.selectedTimeSlotInfo = timeSlot;
            this.showDatePickerError = false;
        },
        async forwardButtonAction() {
            if (!this.isFormValid) {
                this.showDatePickerError = true;
                return;
            }

            // Save service location then schedule and navigate forward
            await this.$refs.serviceLocation.forwardButtonAction();
            this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot);
            this.$router.navigate(
                this.navigationScenarios.CLICKED_FORWARD,
                this.$route
            );
        }
    }
};
</script>

<style lang="scss" scoped>
$page-side-padding: 1.5rem;

.iss-heritage-container-width {
    padding-right: 0.9375rem;
    padding-left: 0.9375rem;

    .schedule-page-container {
        position: relative;
        min-height: 1px;
    }
}

:deep(.text-link-small) {
    a,
    .btn-link {
        font-size: 0.875rem;
        line-height: 1.5;
    }
}

:deep(.change-shop-link) {
    a {
        font-weight: 500;
    }
}

:deep(.subheader-primary) {
    h5.dark-header {
        margin-bottom: 0.25rem !important;
    }
}
</style>
