<template>
    <Form
        ref="theForm"
        v-slot="{ meta }"
        @submit="onSubmit"
        @invalidSubmit="onInvalidSubmit">
        <div class="page-container-grouped-styles">
            <siteHeader cmsWidgetName="SiteHeaderWidget" />
            <siteSubHeader
                cmsWidgetName="ScheduleSubHeaderWidget"
                subTextClasses="text-center small sub-text"
                class="mt-5" />
            <template v-if="ChangeShopLink.length">
                <textBlock
                    cmsWidgetName="ChangeShopLink"
                    justifyText="center"
                    class="mb-3 text-link-small"
                    :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" />
                <siteFooter
                    ref="navbar"
                    class="mt-5"
                    cmsWidgetName="SiteFooterWidget"
                    :isForwardActionDisabled="!meta.valid"
                    @backClicked="navigateBack"
                    @forwardClicked="forwardButtonAction" />
            </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().priceOrderItemsAndSaveServerData(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);
        }
    },
    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 = useMainStore().lineItems.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 supportingItems = this.getSupportingItems();
            const isPremiumAppointment =
                !!supportingItems.filter((lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE)
                    .length > 0;

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

            return selectedTimeSlotInfo;
        },
        getSupportingItems() {
            return this.mainStore.lineItems.supportingItems;
        },
        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}`;
        },
        updateSupportingItems() {
            const supportingItems = this.getSupportingItems();

            // if we have a premium fee(early bird), then save/update supporting items
            if (
                this.appointmentType === AppointmentTypeStrings.MOBILE
                && this.selectedTimeSlotInfo?.isPremiumAppointment
            ) {
                const premiumFeeIndex = supportingItems.findIndex((item) => item.partType === PREMIUM_FEE_PART_TYPE);

                if (premiumFeeIndex >= 0) {
                    supportingItems[premiumFeeIndex].laborAmount =
                        this.mobilePremiumAppointmentFee.laborAmount;
                    supportingItems[premiumFeeIndex].sellingPrice =
                        this.mobilePremiumAppointmentFee.sellingPrice;
                    supportingItems[premiumFeeIndex].kitPrice =
                        this.mobilePremiumAppointmentFee.kitPrice;
                } else {
                    supportingItems.push(this.mobilePremiumAppointmentFee);
                }

                this.mainStore.saveSupportingItemsSuppressingStateResetting(supportingItems);
            } else {
                // if it's not a mobile and/or premium early bird, then make sure we remove any that may have been added
                const removePremiumFeeIndex = supportingItems.findIndex((item) => item.partType === PREMIUM_FEE_PART_TYPE);

                if (removePremiumFeeIndex >= 0) {
                    supportingItems.splice(removePremiumFeeIndex, 1);
                    this.mainStore.saveSupportingItemsSuppressingStateResetting(supportingItems);
                }
            }
        },
        forwardButtonAction() {
            this.updateSupportingItems();
            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;

.page-container-grouped-styles {
    overflow: auto;

    .main-content-container {
        padding: 0 1.5rem !important;
    }
}

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

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