<template>
    <Form
        ref="theForm"
        v-slot="{ meta }"
        @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" />
                    <template v-if="ChangeShopLink.length">
                        <textBlock
                            cmsWidgetName="ChangeShopLink"
                            justifyText="center"
                            class="mb-5 text-link-small change-shop-link"
                            :marginTopSizeOverride="1" />
                    </template>
                    <div class="main-content-container">
                        <locationAlerts
                            ref="locationAlerts"
                            cmsWidgetPrefix="LocationAlert-" />
                        <datePicker
                            ref="datePicker"
                            v-model="selectedDate"
                            customComponentId="dateQuestion"
                            selectableDatesSetting="custom"
                            class="text-link-small"
                            :customSelectableDatesCallback="
                                getAvailableDatesMethod
                            "
                            validationRules="date-required"
                            @dateClicked="openInshopTimeSlotsModal" />
                        <timeSlotModalQuestion
                            ref="timeSlotModalQuestion"
                            v-model="selectedTimeSlotInfo"
                            customComponentId="timeSlotModalQuestion"
                            cmsWidgetName="TimeSlotModalQuestion"
                            mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
                            mobileCmsWidgetName="MobileTimeSlotModal"
                            dropoffCmsWidgetName="DropOffTimeSlotModal"
                            sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
                            overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
                            :selectedDate="selectedDate"
                            :appointmentType="appointmentType"
                            :premiumAppointmentFee="mobilePremiumAppointmentFee"
                            :timeSlotsForSelectedDate="timeSlotsForSelectedDate"
                            :estimatedServiceMinutesMinimum="
                                selectableDatesData.estimatedServiceMinutesMinimum
                            "
                            :estimatedServiceMinutesMaximum="
                                selectableDatesData.estimatedServiceMinutesMaximum
                            "
                            validationRules="time-slot-selection-required"
                            @timeSlotModalClosed="timeSlotModalClosed"
                            @timeSlotSelected="forwardButtonAction" />
                        <siteFooter
                            ref="navbar"
                            class="mt-5"
                            cmsWidgetName="SiteFooterWidget"
                            :isForwardActionDisabled="!meta.valid"
                            @backClicked="navigateBack"
                            @forwardClicked="forwardButtonAction" />
                    </div>
                </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 timeSlotModalQuestion from '@/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import textBlock from '@/digital-components/text-block/text-block.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,
    convertDateStringToDate,
    sumDateString
} from '@/helpers/date-helper';
import settleAllPromises from '@/helpers/layout-helper';
import { Form, defineRule } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import errorMessages from '@/constants/error-messages';
import { required } from '@/helpers/validation-rules';
import { useMainStore } from '@/store';

// DEFINE VALIDATION RULES
defineRule('date-required', required(errorMessages.DATE_REQUIRED));
defineRule('time-slot-selection-required', (value) => {
    if (value?.timeSlot?.routeCode == null) {
        return errorMessages.DATE_REQUIRED;
    }
    return true;
});

// Define constants
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)

const getAvailableDates = async (
    startDateString,
    endDateString,
    appointmentType,
    providerNumber
) => {
    const apiEndDateLimit = sumDateString(
        startDateString,
        TIME_SLOTS_CALL_DAYS_LIMIT
    );
    const difference = calcDaysBetweenDates(startDateString, endDateString);
    const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
    const storeActionConfigs = [];
    const timeSlotsData = {};
    timeSlotsData.days = [];
    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
            );

            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
                }
            };
        } 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 {
                timeSlotsResponse = await useMainStore().getMobileTimeSlots(
                    storeAction.payload.startDate,
                    storeAction.payload.endDate
                );
            }

            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,
        timeSlotModalQuestion,
        siteFooter,
        textBlock,
        // eslint-disable-next-line vue/no-reserved-component-names
        Form
    },
    mixins: [BaseFormMixin],
    async beforeRouteEnter(to, from, next) {
        // Call APIs
        let preSelectedDate = await useMainStore().order.schedule.date;
        if (!preSelectedDate || preSelectedDate.startTime === null) {
            preSelectedDate = null;
        }

        const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);

        const datePickerInitialDataPromise =
            await datePicker.methods.loadInitialData({
                selectableDatesSetting: 'custom',
                initialViewRowsToShow: 5,
                customSelectableDatesCallback: getAvailableDates,
                preSelectedDate
            });

        const premiumFeePromise = useMainStore().getMobilePremiumFee();

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

        const alertReasonsPromise = locationAlerts.methods.loadInitialData(
            useMainStore().order.serviceLocation.zipCodeCtu,
            useMainStore().order.serviceLocation.provider?.address?.zipCodeCtu
        );

        // Settle promises and get results
        const promiseResultMap = [
            {
                resultKey: 'cmsContent',
                promise: cmsContentPromise
            },
            {
                resultKey: 'alertReasons',
                promise: alertReasonsPromise
            },
            {
                resultKey: 'datePickerInitialData',
                promise: datePickerInitialDataPromise
            },
            {
                resultKey: 'premiumFeeWithPrice',
                promise: premiumFeeWithPricePromise
            }
        ];

        // use resultMap to populate layout content.
        const resultMap = await settleAllPromises(promiseResultMap);
        next((vm) => {
            vm.setCmsContent(resultMap.cmsContent);
            vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
            vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
            vm.setData(
                resultMap.datePickerInitialData.initialShopTimeSlotsResponse,
                resultMap.premiumFeeWithPrice
            );
            vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
        });
    },
    setup() {
        const mainStore = useMainStore();
        return { mainStore };
    },
    data() {
        return {
            selectedDate: this.getSelectedDate(),
            selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
            selectableDatesData: [],
            mobilePremiumAppointmentFee: null
        };
    },
    computed: {
        ChangeShopLinkText() {
            return this.getCmsContent('ChangeShopLink', 'Text');
        },
        ChangeShopLink() {
            return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
        },
        appointmentType() {
            return useMainStore().order.serviceLocation.appointmentType;
        },
        timeSlotsForSelectedDate() {
            if (!this.selectedDate) {
                return null;
            }

            return this.selectableDatesData.days?.find((selectableDate) => selectableDate.date === this.selectedDate);
        },
        supportingItems() {
            return useMainStore().lineItems.supportingItems;
        }
    },
    watch: {
        selectedDate(newValue, oldValue) {
            // Clear time slot selection if date selected changes
            if (newValue !== oldValue) {
                this.selectedTimeSlotInfo = {
                    timeSlot: {
                        date: null,
                        routeCode: null,
                        startTime: null,
                        endTime: null,
                        jobMaxMinutes: null,
                        jobMinMinutes: null
                    },
                    isPremiumAppointment: null
                };
            }
        },
        selectedTimeSlotInfo(newValue) {
            this.updateFooterButtonText(newValue);
        }
    },
    methods: {
        splitCopyOnCMSPlaceHolder,
        arePagePrerequisitesValid() {
            const { serviceLocation } = useMainStore().order;
            const serviceLocationPreReqs =
                serviceLocation.zipCode
                && serviceLocation.zipCodeCtu
                && serviceLocation.appointmentType
                && (serviceLocation.appointmentType
                    === AppointmentTypeStrings.MOBILE
                    || serviceLocation.appointmentType
                        === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
                    || serviceLocation.provider.providerNumber);
            const supportingItems = this.supportingItems !== null;
            const damageInfo =
                useMainStore().order.damage.isRepair
                || (useMainStore().order.lineItems?.glassParts != null
                    && useMainStore().order.lineItems.glassParts.length > 0);

            return serviceLocationPreReqs && supportingItems && damageInfo;
        },
        setData(initialShopTimeSlotsResponse, premiumFeeWithPriceResponse) {
            this.selectableDatesData = initialShopTimeSlotsResponse;
            this.mobilePremiumAppointmentFee = premiumFeeWithPriceResponse
                ? premiumFeeWithPriceResponse[0]
                : null;
        },
        async getAvailableDatesMethod(startDate, endDate) {
            const newShopTimeSlots = await getAvailableDates(
                startDate,
                endDate,
                this.appointmentType,
                this.mainStore.order.serviceLocation.provider.providerNumber
            );
            // ADD API CALL RESULTS TO EXISTING DATE DATA
            this.selectableDatesData.days =
                this.selectableDatesData.days.concat(newShopTimeSlots.days);
            return newShopTimeSlots;
        },
        getAvailableDates,
        getServiceZipCtuCodeFromStore() {
            return this.mainStore.order.serviceLocation.zipCodeCtu;
        },
        openInshopTimeSlotsModal() {
            this.$refs.timeSlotModalQuestion.openModal();
        },
        getSelectedDate() {
            return this.mainStore.order.schedule.date;
        },
        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;
        },
        timeSlotModalClosed() {
            // Clear the selectedDate if no timeSlot has been selected
            if (this.selectedTimeSlotInfo.timeSlot.routeCode == null) {
                this.selectedDate = null;
            }
        },
        updateFooterButtonText(timeSlotInfo) {
            let navbarButtonText;
            if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
                navbarButtonText = 'Continue';
            } else {
                navbarButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(timeSlotInfo.timeSlot.date)}`;
                if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
                    navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(timeSlotInfo.timeSlot.startTime)}`;
                } else if (
                    this.appointmentType === AppointmentTypeStrings.MOBILE
                    && !timeSlotInfo.isPremiumAppointment
                ) {
                    navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
                        timeSlotInfo.timeSlot.startTime,
                        true
                    )} - ${this.getDisplayTextForMilitaryTime(
                        timeSlotInfo.timeSlot.endTime,
                        true
                    )}`;
                }
            }
            this.$refs.navbar.updateButtonText(navbarButtonText);
        },
        convertSelectedDateToShortMonthAndDay(selectedDate) {
            // This conversion ensures we don't get get GMT induced date changes
            const dateObject = convertDateStringToDate(selectedDate);
            return dateObject.toLocaleDateString('en-us', {
                month: 'short',
                day: 'numeric'
            });
        },
        getDisplayTextForMilitaryTime(
            militaryTimeInput,
            shouldTrimMinutesIfEmpty = false
        ) {
            // Expected input: "HH:MM"
            let hours = parseInt(militaryTimeInput.split(':')[0], 10);
            const minutes = militaryTimeInput.split(':')[1];
            const meridianNotation = hours > 11 ? 'PM' : 'AM';

            if (hours > 12) {
                hours -= 12;
            }

            if (shouldTrimMinutesIfEmpty && minutes === '00') {
                return `${hours} ${meridianNotation}`;
            }
            return `${hours}:${minutes} ${meridianNotation}`;
        },
        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 {
    .schedule-page-container {
        position: relative;
        min-height: 1px;
        padding-left: .9375rem;
        padding-right: .9375rem;
    }
}

: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>
