<template>
    <div
        class="date-picker text-center"
        :class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass}`">
        <fieldset id="date-picker-fieldset" ref="datePickerFieldset">
            <legend class="sr-only">Select a day and time</legend>
            <div
                v-for="month in months"
                :key="`${month.monthLabel}-${month.yearNum?.toString()}`"
                :id="`${month.monthLabel}-${month.yearNum?.toString()}`"
                class="calendar-grid-container"
                :class="[
                    hideSomeDaysForInitialView ? 'partial-month-initial-view' : '',
                    month.monthClass,
                ]">
                <div class="month-year body-small d-flex align-items-center">
                    {{ month.monthLabel }} {{ month.yearNum?.toString() }}
                </div>
                <div
                    v-if="calendarViewDirection === 'future'"
                    class="legend caption d-flex align-items-center justify-content-end">
                    <span class="legend-circle me-1"></span>
                    &equals; Available
                </div>
                <div class="nav-back ps-3"><button></button></div>
                <div class="nav-forward pe-3"><button></button></div>
                <div class="grid-item caption">
                    <span class="sr-only">Sunday</span>
                    SUN
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Monday</span>
                    MON
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Tuesday</span>
                    TUE
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Wednesday</span>
                    WED
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Thursday</span>
                    THU
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Friday</span>
                    FRI
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Saturday</span>
                    SAT
                </div>
                <div
                    v-show="!isMobileSelected"
                    v-for="date in month.datesInshop"
                    :key="date.inputValue"
                    :id="date.inputValue"
                    class="grid-item radio-wrapper inshop-version"
                    :class="[
                        date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
                        date.dayClasses,
                        date.isAvailable ? 'selectable-day' : '',
                        date.isPricingByDayUpchargeDay ? 'upch-day' : '',
                    ]">
                    <input
                        :disabled="!date.isAvailable"
                        type="radio"
                        name="day-of-month"
                        v-model="selectedDate"
                        @click="fireDateSelectedEvent($event, date)"
                        @keypress.enter="fireDateSelectedEvent($event, date)"
                        :value="date.inputValue"
                        :id="`${month.monthLabel}-${date.dateNum.toString()}`" />
                    <label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
                        <span>{{ date.dateNum.toString() }}</span>
                        <span
                            v-if="isPricingByDayExperiment && showPricingByDay && date.isAvailable"
                            class="price">
                            {{ date.priceString }}
                        </span>
                    </label>
                </div>
                <div
                    v-show="isMobileSelected"
                    v-for="date in month.datesMobile"
                    :key="`${date.inputValue}-mobile`"
                    :id="`${date.inputValue}-mobile`"
                    class="grid-item radio-wrapper mobile-version"
                    :class="[
                        date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
                        date.dayClasses,
                        date.isAvailable ? 'selectable-day' : '',
                        date.isPricingByDayUpchargeDay ? 'upch-day' : '',
                    ]">
                    <input
                        :disabled="!date.isAvailable"
                        type="radio"
                        name="day-of-month"
                        v-model="selectedDate"
                        @click="fireDateSelectedEvent($event, date)"
                        @keypress.enter="fireDateSelectedEvent($event, date)"
                        :value="`${date.inputValue}-mobile`"
                        :id="`${month.monthLabel}-${date.dateNum.toString()}-mobile`" />
                    <label :for="`${month.monthLabel}-${date.dateNum.toString()}-mobile`">
                        <span>{{ date.dateNum.toString() }}</span>
                        <span
                            v-if="isPricingByDayExperiment && showPricingByDay && date.isAvailable"
                            class="price">
                            {{ date.priceString }}
                        </span>
                    </label>
                </div>
            </div>
            <loader
                :class="[!isLoading ? 'date-picker-hidden' : '']"
                loaderColor="blue"
                loaderPosition="center" />
            <div class="row form-test-error" id="date-of-month-error">
                <ErrorMessage :name="customComponentId" class="small mt-1"></ErrorMessage>
            </div>
            <button
                v-if="calendarViewDirection === 'future' && !disableViewMoreDatesButton"
                type="button"
                class="btn btn-link view-more-dates"
                id="viewMoreDates"
                :disabled="isLoading"
                @click="showAnotherMonth">
                View more dates
            </button>
        </fieldset>
    </div>
</template>

<script>
// Supporting files
import loader from "@/ux-components/loader/loader";
import store from "@/store";
import {
    TIMINGFUNC_MAP,
    BUFFER_OFFSET,
    MONTHS_OF_YEAR,
    DAYS_OF_WEEK,
} from "@/digital-components/date-picker/mixins/constants";
import {
    selectableDaysOptions,
    requiredParameter,
} from "@/digital-components/date-picker/mixins/helpers";
import {
    convertDateToDateString,
    convertDateStringToDate,
    getTodayDateString,
} from "@/layouts/schedule/helpers/schedule-helper";
import { useField, ErrorMessage } from "vee-validate";
import { deepClone } from "@/helpers/object-helper";
import { v4 as uuidv4 } from "uuid";

export default {
    name: "datePicker",
    data() {
        return {
            isLoading: true,
            months: null,
            disableViewMoreDatesButton: false,
            hideSomeDaysForInitialView: null,
            selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
            selectableDatesMobile: [],
        };
    },
    props: {
        customComponentId: String,
        selectableDatesSetting: {
            type: String,
            validator(value) {
                return Object.values(selectableDaysOptions).includes(value);
            },
            default: selectableDaysOptions.PAST,
        },
        modelValue: {
            type: Object,
        },
        todayOverrideDateString: {
            // keep for use in unit tests to override today's date
            type: String,
            default: null,
        },
        getMoreDatesCallback: {
            type: Function,
            default() {
                return [];
            },
        },
        validationRules: {
            type: String,
            default: "",
        },
        showPricingByDay: Boolean,
        pricingByDayBasePrice: Number,
        pricingByDayUpcharge: Number,
        isPricingByDayExperiment: Boolean,
        isMobileSelected: Boolean,
    },
    setup(props) {
        const uuid = uuidv4();
        const componentId = !props.customComponentId
            ? `component-${uuid}`
            : props.customComponentId;
        const modelValue = deepClone(props).modelValue;
        const initialValue = modelValue;
        const fieldOptions = {
            value: modelValue,
            initialValue: initialValue,
        };
        const { errorMessage, handleBlur, handleChange, meta, validate, errors, resetField } =
            useField(componentId, props.validationRules, fieldOptions);

        return {
            componentId,
            errorMessage,
            handleBlur,
            handleChange,
            validate,
            meta,
            errors,
            resetField,
        };
    },
    computed: {
        todayString() {
            return this.todayOverrideDateString || getTodayDateString();
        },
        todayDayIndex() {
            return convertDateStringToDate(this.todayString).getDay();
        },
        todayDateNum() {
            return convertDateStringToDate(this.todayString).getDate();
        },
        currentWeekStartDateNum() {
            return this.todayDayIndex >= this.todayDateNum
                ? 1
                : this.todayDateNum - this.todayDayIndex;
        },
        currentWeekEndDateNum() {
            return this.todayDateNum + (6 - this.todayDayIndex);
        },
        calendarViewDirection() {
            if (this.selectableDatesSetting === "custom") return "future";
            return "past";
        },
        showPricingByDayClass() {
            return this.showPricingByDay ? "show-pricing-by-day" : "";
        },
        selectedDate: {
            get() {
                return this.modelValue;
            },
            set(newSelectedDate) {
                this.$emit("update:modelValue", newSelectedDate);
                this.dispatchStoreAction(this.storeActions.SAVE_WAITLIST_REQUESTED, false, false);
            },
        },
        firstAvailableSelectableDate() {
            const mobileFirstDate = Array.isArray(this.selectableDatesMobile)
                ? this.selectableDatesMobile[0]?.date
                : null;
            const inshopFirstDate = Array.isArray(this.selectableDatesInshop)
                ? this.selectableDatesInshop[0]?.date
                : null;
            return this.isMobileSelected ? mobileFirstDate + "-mobile" : inshopFirstDate;
        },
    },
    methods: {
        async initializeComponent(initialData) {
            await this.setCalendarData(initialData);
        },
        fireDateSelectedEvent(event, date) {
            // Ignore if arrow key selected radioButton
            if (event.screenX === 0 && event.screenY === 0) {
                return;
            }
            this.$emit("date-clicked", date);
        },
        getWeekStartDate(dateString) {
            const date = convertDateStringToDate(dateString);
            const dayOfWeek = date.getDay();
            // Subtract the day of the week from date to get the date of Sunday
            const sunday = new Date(date);
            sunday.setDate(sunday.getDate() - dayOfWeek);
            return convertDateToDateString(sunday);
        },
        getWeekEndDate(dateString) {
            const date = convertDateStringToDate(dateString);
            const dayOfWeek = date.getDay();
            const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday
            // Clone the given date and add the remaining days until Saturday
            const saturday = new Date(date);
            saturday.setDate(date.getDate() + daysUntilSaturday);
            return convertDateToDateString(saturday);
        },
        getNextWeekSunday(dateString) {
            const date = convertDateStringToDate(dateString);
            const dayOfWeek = date.getDay();
            const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday
            // Clone the given date and add the remaining days until Sunday
            const nextSunday = new Date(date);
            nextSunday.setDate(date.getDate() + daysUntilNextSunday);
            return convertDateToDateString(nextSunday);
        },
        getMonthEnd(dateStr) {
            // convert string to date, do date calc, then return a string back
            const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0);
            return convertDateToDateString(date);
        },
        getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) {
            // TODO: this only is for future direction; need to create logic for past direction
            const weeks = [];
            let weekStartDate = this.getWeekStartDate(todayString);
            let weekEndDate = this.getWeekEndDate(todayString);

            if (preSelectedDate) {
                let preSelectedDateMonthEnd = this.getMonthEnd(
                    preSelectedDate.replace("-mobile", "")
                );
                let weekIncludesPreSelectedMonthEnd = false;
                let i = 0;
                while (!weekIncludesPreSelectedMonthEnd) {
                    if (i > 0) {
                        weekStartDate = this.getNextWeekSunday(weekEndDate);
                        weekEndDate = this.getWeekEndDate(weekStartDate);

                        if (
                            (preSelectedDateMonthEnd > weekStartDate &&
                                preSelectedDateMonthEnd < weekEndDate) ||
                            preSelectedDateMonthEnd === weekStartDate ||
                            preSelectedDateMonthEnd === weekEndDate
                        ) {
                            weekEndDate = preSelectedDateMonthEnd;
                            weekIncludesPreSelectedMonthEnd = true;
                        }
                    }
                    weeks.push({
                        weekNum: i + 1,
                        weekStartDate: weekStartDate,
                        weekEndDate: weekEndDate,
                    });
                    i++;
                }
            } else {
                for (let i = 0; i < initialViewRowsToShow; i++) {
                    if (i > 0) {
                        weekStartDate = this.getNextWeekSunday(weekEndDate);
                        weekEndDate = this.getWeekEndDate(weekStartDate);
                    }
                    weeks.push({
                        weekNum: i + 1,
                        weekStartDate: weekStartDate,
                        weekEndDate: weekEndDate,
                    });
                }
                // If any of these weeks is split between two months, then make them 2 separate "weeks"
                // (a week split between two months is considered 2 weeks per business requirements)
                const hasSplitWeek = (week) => {
                    return week.weekStartDate.split("-")[1] !== week.weekEndDate.split("-")[1]
                        ? true
                        : false;
                };
                const splitWeekIndex = weeks.findIndex(hasSplitWeek);

                if (splitWeekIndex > -1) {
                    const week1 = [];
                    const week2 = [];
                    let switchToWeek2 = false;

                    for (let j = 0; j < 7; j++) {
                        const newDate = convertDateStringToDate(
                            weeks[splitWeekIndex].weekStartDate
                        );
                        newDate.setDate(newDate.getDate() + j);
                        if (newDate.getDate() === 1) switchToWeek2 = true;
                        if (switchToWeek2) {
                            week2.push(convertDateToDateString(newDate));
                        } else {
                            week1.push(convertDateToDateString(newDate));
                        }
                    }

                    const week1EndDate = week1[week1.length - 1];
                    const week2StartDate = week2[0];

                    if (week1EndDate < todayString) {
                        // replace week 1 with week 2
                        weeks[splitWeekIndex].weekStartDate = week2StartDate;
                    } else {
                        const newWeek = {
                            weekNum: weeks[splitWeekIndex].weekNum,
                            weekStartDate: week2StartDate,
                            weekEndDate: weeks[splitWeekIndex].weekEndDate,
                        };
                        weeks[splitWeekIndex].weekEndDate = week1EndDate;
                        weeks.splice(splitWeekIndex + 1, 0, newWeek);
                        weeks.pop();
                        weeks.forEach((item, index) => {
                            if (index > splitWeekIndex) {
                                item.weekNum = item.weekNum + 1;
                            }
                        });
                    }
                }
            }
            return weeks;
        },
        async loadInitialData(config) {
            /*
             ** NOTE: this _could_ be called by a parent before fully loaded, so FYI component data or computeds might not be available
             */
            let todayDateString;
            let calendarViewDirection = "none";
            let hideSomeDaysForInitialView = false;
            let hideSecondMonth = false;

            if (this.todayString) {
                todayDateString = this.todayString;
            } else if (config.todayOverrideDateString) {
                todayDateString = config.todayOverrideDateString;
            } else {
                todayDateString = getTodayDateString();
            }
            if (config.selectableDatesSetting === "past") calendarViewDirection = "past";
            if (config.selectableDatesSetting === "custom") calendarViewDirection = "future";

            const currentMonthEnd = this.getMonthEnd(todayDateString);
            // TODO - set up currentMonthStart if direction is PAST:
            //      let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
            const initialViewWeeks = this.getInitialViewWeeks(
                todayDateString,
                config.initialViewRowsToShow,
                config.preSelectedDate
            );
            const initialViewStartDate = todayDateString;
            const initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate;

            if (!config.preSelectedDate) {
                const firstSaturdayMonth = initialViewWeeks[0].weekEndDate.split("-")[1];
                const lastSundayMonth =
                    initialViewWeeks[initialViewWeeks.length - 1].weekStartDate.split("-")[1];

                if (calendarViewDirection === "future") {
                    if (firstSaturdayMonth !== lastSundayMonth) {
                        hideSomeDaysForInitialView = true;
                    }
                    if (initialViewStartDate.split("-")[1] === lastSundayMonth) {
                        hideSecondMonth = true;
                        if (currentMonthEnd > initialViewEndDate) {
                            // should part of 1st month be hidden?
                            hideSomeDaysForInitialView = true;
                        }
                    }
                }
                // TODO - NEED TO CREATE OPPOSITE LOGIC FOR "past" DIRECTION LIKE THE ABOVE ^ ^ ^
            }

            const loadInitialDataPromise = new Promise((resolve, reject) => {
                const response = config.getSelectableDatesCallback({
                    startDateString: initialViewStartDate,
                    endDateString: initialViewEndDate,
                    providerNumber: config.providerNumber,
                    zipCode: config.zipCode,
                    includeMobileTimeSlots: config.includeMobileTimeSlots,
                    includeInshopTimeSlots: config.includeInshopTimeSlots,
                });
                resolve(response);
            });

            return loadInitialDataPromise.then((response) => {
                const initialData = {
                    todayDate: todayDateString,
                    initialViewStartDate: initialViewStartDate,
                    initialViewEndDate: initialViewEndDate,
                    calendarViewDirection: calendarViewDirection,
                    initialShopTimeSlotsResponse: response,
                    hideSomeDaysForInitialView: hideSomeDaysForInitialView,
                    hideSecondMonth: hideSecondMonth,
                    preSelectedDate: config.preSelectedDate,
                };
                return initialData;
            });
        },
        async setCalendarData(config = {}) {
            this.isLoading = true;
            this.selectableDatesInshop = [];
            this.selectableDatesMobile = [];
            this.months = [];

            this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView;
            const hideSecondMonth = config.hideSecondMonth;
            const direction = config.calendarViewDirection;
            const monthsAfterToLoadOffset = 6;
            const monthsBeforeToLoadOffset = 36;

            config.initialShopTimeSlotsResponse.inshopTimeSlotsData.days.forEach(
                (selectableDate) => {
                    this.selectableDatesInshop.push(selectableDate);
                }
            );
            config.initialShopTimeSlotsResponse.mobileTimeSlotsData.days.forEach(
                (selectableDate) => {
                    this.selectableDatesMobile.push(selectableDate);
                }
            );

            // GENERATE MONTHS AND PUSH THEM INTO ARRAY
            const months = [];
            const options = {
                calendarViewDirection: direction,
                monthsBeforeToLoadOffset: monthsBeforeToLoadOffset,
                monthsAfterToLoadOffset: monthsAfterToLoadOffset,
                initialViewStartDate: config.initialViewStartDate,
                initialViewEndDate: config.initialViewEndDate,
                hideSecondMonth: hideSecondMonth,
                preSelectedDate: config.preSelectedDate,
                pricingByDayBasePrice: config.pricingByDayBasePrice,
                pricingByDayUpcharge: config.pricingByDayUpcharge,
            };
            if (direction === "future") {
                // first 0, then 1
                for (let i = 0; i <= monthsAfterToLoadOffset; i++) {
                    months.push(await this.getMonthData(i, options));
                }
            } else if (direction === "past") {
                // first 0, then -1
                for (let i = 0; i >= 0 - monthsBeforeToLoadOffset; i--) {
                    months.unshift(await this.getMonthData(i, options));
                }
            } else {
                // TODO - IF A CALENDAR WITH BOTH PAST AND FUTURE WAS EVER NEEDED
                // for (let i = monthsAfterToLoadOffset; i >= monthsBeforeToLoadOffset; i--) {
                //     months.push(this.getMonthDataPAST(i));
                // }
            }
            this.months = months;
            this.isLoading = false;

            if (config.preSelectedDate) {
                this.$nextTick(() => {
                    //Advance to month
                    const monthToShow = this.months.find((month) =>
                        month.monthClass.includes("month-preselected")
                    );
                    if (
                        monthToShow.monthClass.includes("month-preselected") &&
                        monthToShow.monthClass.includes("last-available-month")
                    ) {
                        // disable View More dates button if the preSelectedDate in the last available month
                        this.disableViewMoreDatesButton = true;
                    }
                    this.scrollToElement(monthToShow.monthString);
                });
            }
        },
        async getMonthData(offset = requiredParameter(), options) {
            /*  options contains:
                    calendarViewDirection (string)
                    initialViewStartDateNum (number)
                    initialViewEndDateNum (number)
                    monthsBeforeToLoadOffset (number),
                    monthsAfterToLoadOffset (number),
                    hideSecondMonth (boolean),
                    preSelectedDate (string)

                data variables used:
                    this.todayString (date string)
                    this.currentWeekStartDateNum
                    this.currentWeekEndDateNum
                    this.todayDateNum
                    this.hideSomeDaysForInitialView (string)
                    this.selectableDatesInshop
                    this.selectableDatesMobile
            */

            let monthNum = convertDateStringToDate(this.todayString).getMonth() + offset + 1;
            let yearNum = convertDateStringToDate(this.todayString).getFullYear();
            const calendarViewDirection = options.calendarViewDirection;
            if (calendarViewDirection === "future" && offset > 0) {
                while (monthNum > 12) {
                    monthNum = monthNum - 12;
                    yearNum++;
                }
            } else if (calendarViewDirection === "past" && offset < 0) {
                while (monthNum < 1) {
                    monthNum = 12 + monthNum;
                    yearNum--;
                }
            }
            const initialViewEndDate = options.initialViewEndDate;
            // const initialViewStartDate = options.initialViewStartDate; // TODO: to be used for past calendarViewDirection
            const hideSecondMonth = options.hideSecondMonth;
            const preSelectedDateObj = options.preSelectedDate
                ? convertDateStringToDate(options.preSelectedDate)
                : null;
            let datesInshop = [];
            let datesMobile = [];
            const monthEndDate = new Date(yearNum, monthNum, 0);
            const monthStartDateNum =
                offset === 0 && calendarViewDirection === "future"
                    ? this.currentWeekStartDateNum
                    : 1;
            const monthStartDate = new Date(yearNum, monthNum - 1, monthStartDateNum);
            const startDateDayIndex = monthStartDate.getDay();

            let monthClass = "";
            let isMonthThatHidesSomeDaysForInitialView;
            let monthEndDateNum = monthEndDate.getDate();

            if (
                offset === 0 &&
                calendarViewDirection === "past" &&
                monthEndDateNum > this.currentWeekEndDateNum
            ) {
                monthEndDateNum = this.currentWeekEndDateNum;
            }

            if (options.preSelectedDate) {
                if (
                    monthStartDate.getFullYear() === preSelectedDateObj.getFullYear() &&
                    monthStartDate.getMonth() === preSelectedDateObj.getMonth()
                ) {
                    monthClass = monthClass + " month-preselected";
                } else if (monthStartDate > preSelectedDateObj) {
                    monthClass = monthClass + " month-hidden";
                }
            } else {
                if (Math.abs(offset) === 1 && hideSecondMonth) {
                    monthClass = monthClass + " month-hidden";
                } else if (Math.abs(offset) > 1) {
                    monthClass = monthClass + " month-hidden";
                }
            }

            if (
                Math.abs(offset) === options.monthsAfterToLoadOffset &&
                calendarViewDirection === "future"
            ) {
                monthClass = monthClass + " last-available-month";
            }
            if (
                Math.abs(offset) === options.monthsBeforeToLoadOffset &&
                calendarViewDirection === "past"
            ) {
                // TODO - re-check this logic if past direction
                monthClass = monthClass + " last-available-month";
            }

            const getDates = (selectableDates, datesArray) => {
                // populate dates array
                for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
                    let dayClasses = "";
                    const dateString =
                        yearNum.toString() +
                        "-" +
                        ("0" + monthNum).slice(-2) +
                        "-" +
                        ("0" + i).slice(-2);
                    const dayIndex = convertDateStringToDate(dateString).getDay();
                    const dayObject = DAYS_OF_WEEK[dayIndex];
                    const isAvailable =
                        selectableDates.findIndex((date) => date.date === dateString) > -1
                            ? true
                            : false;
                    const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
                    const displayPrice = isPricingByDayUpchargeDay
                        ? options.pricingByDayBasePrice + options.pricingByDayUpcharge
                        : options.pricingByDayBasePrice;
                    const priceString = "$" + displayPrice;

                    if (offset === 0 && i === this.todayDateNum) {
                        dayClasses += " current-day";
                    }
                    if (
                        offset === 0 &&
                        i < this.todayDateNum &&
                        calendarViewDirection === "future"
                    ) {
                        dayClasses += " unavailable-day";
                    }
                    if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
                        dayClasses += " unavailable-day";
                    }
                    if (dayIndex === 0) {
                        dayClasses += " " + dayObject.cssClass;
                    }
                    if (
                        this.hideSomeDaysForInitialView &&
                        convertDateStringToDate(initialViewEndDate).getMonth() + 1 === monthNum &&
                        convertDateStringToDate(initialViewEndDate).getDate() < i
                    ) {
                        dayClasses += " day-hidden";
                        isMonthThatHidesSomeDaysForInitialView = true;
                    }
                    const dateObject = {
                        dateNum: i,
                        dayClasses: dayClasses,
                        inputValue: dateString,
                        priceString: priceString,
                        isAvailable: isAvailable,
                        isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
                    };
                    datesArray.push(dateObject);
                }
                return datesArray;
            };
            datesInshop = getDates(this.selectableDatesInshop, datesInshop);
            datesMobile = getDates(this.selectableDatesMobile, datesMobile);

            const monthToAdd = {
                monthLabel: MONTHS_OF_YEAR[monthNum - 1],
                monthIndex: monthNum,
                monthString: MONTHS_OF_YEAR[monthNum - 1] + "-" + yearNum?.toString(),
                yearNum: yearNum,
                datesInshop: datesInshop,
                datesMobile: datesMobile,
                startDateDayIndex: startDateDayIndex,
                monthClass: monthClass,
                isMonthThatHidesSomeDaysForInitialView: isMonthThatHidesSomeDaysForInitialView,
            };
            return monthToAdd;
        },
        async showAnotherMonth() {
            if (!this.months) return;
            this.isLoading = true;
            let monthToShow;
            let monthStartDate;

            if (this.hideSomeDaysForInitialView) {
                monthToShow = this.months.find(
                    ({ isMonthThatHidesSomeDaysForInitialView }) =>
                        isMonthThatHidesSomeDaysForInitialView
                );

                // find the first day-hidden to become the next api call start date
                monthStartDate = this.isMobileSelected
                    ? monthToShow.datesMobile.find(({ dayClasses }) =>
                          dayClasses.includes("day-hidden")
                      )
                    : monthToShow.datesInshop.find(({ dayClasses }) =>
                          dayClasses.includes("day-hidden")
                      );
            } else {
                if (this.calendarViewDirection === "future") {
                    monthToShow = this.months.find((month) =>
                        month.monthClass.includes("month-hidden")
                    );
                }
                if (this.calendarViewDirection === "past") {
                    // TODO: UPDATE THIS WITH CORRECT PAST LOOKING LOGIC
                    monthToShow = this.months.find((month) =>
                        month.monthClass.includes("month-hidden")
                    );
                }
            }
            if (monthToShow) {
                const monthToShowDates = this.isMobileSelected
                    ? monthToShow.datesMobile
                    : monthToShow.datesInshop;
                const monthStartDateString = monthStartDate
                    ? monthStartDate.inputValue
                    : monthToShowDates[0].inputValue;

                // make new API call with this month's start and end dates
                const moreSelectableDatesData = await this.updateSelectableDates(
                    monthStartDateString,
                    monthToShowDates[monthToShowDates.length - 1].inputValue
                );

                this.isLoading = false;
                this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days
                monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
                this.scrollToElement(monthToShow.monthString);

                if (monthToShow.monthClass.includes("last-available-month"))
                    this.disableViewMoreDatesButton = true;

                return moreSelectableDatesData;
            }
        },
        async updateSelectableDates(monthStart, monthEnd) {
            const moreSelectableDates = await this.getMoreDatesCallback(monthStart, monthEnd);

            moreSelectableDates.inshopTimeSlotsData.days.forEach((selectableDate) => {
                const index = this.selectableDatesInshop.findIndex(
                    (dateObj) => dateObj.date === selectableDate.date
                );
                if (index === -1) this.selectableDatesInshop.push(selectableDate);
                this.months.forEach((month) => {
                    // TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above?
                    month.datesInshop.forEach((date) => {
                        if (date.inputValue === selectableDate.date) {
                            date["isAvailable"] = true;
                        }
                    });
                });
            });
            moreSelectableDates.mobileTimeSlotsData.days.forEach((selectableDate) => {
                const index = this.selectableDatesMobile.findIndex(
                    (dateObj) => dateObj.date === selectableDate.date
                );
                if (index === -1) this.selectableDatesMobile.push(selectableDate);
                this.months.forEach((month) => {
                    // TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above?
                    month.datesMobile.forEach((date) => {
                        if (date.inputValue === selectableDate.date) {
                            date["isAvailable"] = true;
                        }
                    });
                });
            });

            return moreSelectableDates;
        },
        scrollToElement(elementId, duration = 800, easing = "ease-in-out") {
            const wrapper = document.querySelector(".page-container-grouped-styles");
            const target = document.getElementById(elementId);
            const initY = wrapper.scrollTop;
            const wrapperRect = wrapper.getBoundingClientRect();
            const targetRect = target.getBoundingClientRect();
            const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET;
            const timingFunc = TIMINGFUNC_MAP[easing];
            let start = null;
            const step = (timestamp) => {
                start = start || timestamp;
                const time = Math.min(1, (timestamp - start) / duration);
                const percentageNew = timingFunc(time);
                const distanceToGo = targetY;
                const thisDistance = percentageNew * distanceToGo;
                wrapper.scrollTo(0, initY + thisDistance);
                if (percentageNew < 1) {
                    window.requestAnimationFrame(step);
                }
            };
            window.requestAnimationFrame(step);
        },
        getSupportingItems() {
            return store.getters.lineItems.supportingItems;
        },
    },
    watch: {
        isLoading(newValue) {
            // if done loading dates, then set to first available date
            if (newValue === false && this.firstAvailableSelectableDate) {
                this.selectedDate = this.firstAvailableSelectableDate;
            }
        },
        modelValue(newValue) {
            this.resetField({
                value: newValue,
            });
        },
        errorMessage(newValue, oldValue) {
            // Check for changing from no error message to some error message.
            if (newValue && !oldValue) {
                this.scrollToElement("date-of-month-error");
            }
        },
    },
    components: {
        loader,
        ErrorMessage,
    },
};
</script>

<style lang="scss" scoped>
.date-picker-hidden {
    opacity: 0;
    max-height: 0;
}
.date-picker {
    position: relative;
    flex-grow: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;

    fieldset {
        flex-grow: 1;
        position: relative;
        width: 100%;

        .view-more-dates {
            color: $blue;
            font-family: UrbanistSemiBold;
        }
    }
    .loader {
        height: 2rem;
        width: calc(100% - 1.5rem);

        &::after {
            width: 1.5rem;
            height: 1.5rem;
        }
    }
    .calendar-grid-container {
        margin: 0 auto 1rem auto;
        position: relative;
        transition:
            height ease 2s,
            opacity ease 2s;
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        justify-content: center;
        align-items: center;
        padding: 0;
        opacity: 1;

        .grid-item {
            text-align: center;
            margin: 10px 3px;
            font-size: 0.75rem;
            line-height: 1.5;

            &.caption {
                color: $gray-550;
            }

            &.first-day-,
            &.first-day-0 {
                grid-column-start: 1;
            }
            &.first-day-1 {
                grid-column-start: 2;
            }
            &.first-day-2 {
                grid-column-start: 3;
            }
            &.first-day-3 {
                grid-column-start: 4;
            }
            &.first-day-4 {
                grid-column-start: 5;
            }
            &.first-day-5 {
                grid-column-start: 6;
            }
            &.first-day-6 {
                grid-column-start: 7;
            }
        }

        .separator-line {
            grid-area: 2/1/2/8;
            border-top: 1px solid $gray-500;
            margin: 0.5rem 0;
        }

        .month-year {
            grid-area: 1 / 1 / 2 / 5;
            font-family: UrbanistRegular;
            color: $black;
            justify-content: left;
            margin-bottom: 0.5rem;
            font-weight: 600;
        }
        .legend {
            grid-area: 1 / 5 / 2 / 8;
            .legend-circle {
                border-radius: 50%;
                width: 16px;
                height: 16px;
                background-color: $blue-100;
                border: 1px solid $blue;
            }
        }
        .nav-back,
        .nav-forward {
            display: none;
        }

        .radio-wrapper {
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
            outline: none;
            height: 1.5rem;
            opacity: 1;
            transition:
                height ease 250ms,
                opacity ease 250ms;

            input[type="radio"] {
                position: absolute; //override bootstrap
                height: 0;
                opacity: 0;

                &:focus-visible + label {
                    box-shadow: 0 0 0 2.5px $blue;
                }

                &:focus + label,
                &:checked:focus + label {
                    box-shadow:
                        0 0 0 3px #fff,
                        0 0 0 5.5px #1574a1;
                    background-color: $blue;
                    color: $white;
                    &.current-day {
                        &:after {
                            background-color: $white;
                        }
                        .first-day {
                            color: $white;
                        }
                    }
                }

                &:checked + label {
                    color: $white;
                    background: $blue;
                    &:after {
                        background-color: $white;
                    }
                    .first-day {
                        color: $white;
                    }
                }
            }

            .first-day {
                position: absolute;
                font-size: 9px;
                font-weight: 500;
                color: $gray-600;
                top: 0;
                z-index: 1;
            }

            label {
                position: relative;
                display: flex;
                justify-content: center;
                align-items: center;
                height: 2.5rem;
                border: 2px solid transparent;

                span {
                    &.small {
                        font-size: 0.75rem;
                        color: $gray-550;
                    }
                }

                &:checked {
                    @include media-breakpoint-up(sm) {
                        box-shadow: 0 0 0 4px transparent;
                        background-color: $blue;
                        color: $white;
                        &:after {
                            background-color: $white;
                        }
                    }
                    cursor: pointer;
                    &.current-day {
                        + .first-day {
                            color: $white;
                        }
                    }
                    .first-day {
                        color: $white;
                    }
                }
                + p {
                    display: none;
                }
            }

            &.selectable-day {
                label {
                    background-color: $blue-100;
                    border-radius: 50%;
                    cursor: pointer;
                    &:hover {
                        border: 2px solid $blue;
                    }
                }
            }
            &.unavailable-day {
                label {
                    border: none;
                    pointer-events: none;
                }
            }
            //NEEDED?
            &.unavailable-day:not(&.sunday) {
                label {
                    &::before {
                        content: "";
                        display: inline-block;
                        position: absolute;
                        left: -1rem;
                        width: 1rem;
                        height: 2.5rem;
                    }
                }
            }
            &.current-day {
                label {
                    font-weight: 500;
                    color: $black;

                    &:after {
                        content: "";
                        width: 0.25rem;
                        height: 0.25rem;
                        border-radius: 50%;
                        background-color: $black;
                        position: absolute;
                        top: 1.75rem;
                    }
                    .first-day {
                        color: $blue;
                    }
                }
                input[type="radio"]:checked + label:after {
                    background-color: $black;
                }
            }
        }

        &.partial-month-initial-view {
            .day-hidden {
                opacity: 0;
                overflow: hidden;
                display: flex;
                margin: 0;
                max-height: 0;
            }
        }
        &.month-hidden {
            opacity: 0;
            max-height: 0;
            margin-bottom: 0;
            overflow: hidden;
        }
    }

    .btn-link {
        font-weight: 500;
        text-underline-offset: 4px;
        flex-grow: 0;
        &:focus {
            box-shadow: none;
        }
    }

    .past {
        .calendar-grid-container {
            .month-year {
                grid-area: 1 / 1 / 2 / 6;
            }
            .nav-forward {
                grid-area: 1 / 7 / 2 / 8;
                display: flex;
                justify-content: flex-end;
                button {
                    &:after {
                        content: "";
                        position: absolute;
                        width: 7px;
                        height: 12px;
                        background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
                    }
                }
            }
            .nav-back {
                grid-area: 1 / 6 / 2 / 7;
                display: flex;
                justify-content: flex-end;
                button {
                    &:after {
                        content: "";
                        position: absolute;
                        width: 7px;
                        height: 12px;
                        background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
                    }
                }
            }
            .nav-back,
            .nav-forward {
                button {
                    position: relative;
                    border-radius: 50%;
                    width: 28px;
                    height: 28px;
                    background-color: $blue-100;
                    border: 1px solid $blue;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    &:focus,
                    &:focus-visible {
                        border: 2.5px solid $blue;
                        box-shadow: none;
                        background-color: $blue-100;
                        color: $white;
                    }
                }
            }
        }
    }
    // new calendar styling overrides
    &.calendar-2025 {
        .calendar-grid-container .grid-item {
            margin: 0 0 0.15rem 0;
        }
        .month-year {
            grid-area: 1 / 1 / 2 / 8;
        }
        .legend {
            display: none;
        }
        .radio-wrapper {
            align-items: flex-start;
            width: 100%;
            height: 2.5rem;
            color: $black;

            input[type="radio"] {
                &:focus-visible + label {
                    box-shadow: none;
                }

                &:focus + label,
                &:checked:focus + label {
                    box-shadow: none;
                    background-color: $blue-100;
                    color: $black;
                }
                &:checked + label {
                    background: $blue-100;
                    border: 2px solid $blue;
                    border-radius: 0.25rem;
                    color: $black;
                    span {
                        font-family: UrbanistSemibold;
                        font-weight: 400;
                    }
                }
            }
            label {
                color: $gray-500;
                flex-direction: column;
                height: 2.5rem;
                width: 100%;
                font-size: 0.75rem;
                line-height: 1.2;
                justify-content: center;
                padding: 0.25rem;
                margin: 0.15rem;
            }
            &.selectable-day {
                label {
                    color: $black;
                    background-color: $blue-100;
                    border-radius: 0.25rem;
                }
            }
            &.current-day {
                label {
                    font-weight: 600;
                    font-family: "UrbanistSemibold";
                    color: $black;
                }
            }
        }
        &.show-pricing-by-day {
            .radio-wrapper {
                &.selectable-day label span {
                    text-decoration: none;
                    color: $black;
                    font-weight: 400;
                    font-family: "UrbanistSemibold";

                    &.price {
                        font-weight: 400;
                    }
                }
                &.upch-day label span.price {
                    color: $gray-600;
                    font-weight: 400;
                    font-family: $font-family-sans-serif;
                }
                input[type="radio"] {
                    &:focus + label,
                    &:checked:focus + label {
                        color: $gray-600;
                    }
                    &:checked + label {
                        color: $blue;
                        span {
                            font-weight: 400;
                            font-family: "UrbanistSemibold";
                        }
                    }
                }
                &.selectable-day.current-day {
                    label {
                        &:after {
                            display: none;
                        }
                    }
                }
            }
        }
    }
    :deep(.timeslots .button-inner-wrapper) {
        display: flex;
        max-width: 100%;
        flex-wrap: wrap;

        & > div {
            flex: 0 0 100%;
        }
    }
}

.btn-link {
    display: block;
    position: relative;
    height: 2rem;
    font-size: 0.875rem;
    padding-top: 0;
    width: 100%;
    justify-content: center;
    background: transparent;
    border: none;
    font-weight: 500;
    text-underline-offset: 4px;
    z-index: 2;
}

.form-test-error {
    margin: 0 auto;
    max-width: 414px;
    text-align: left;
}
</style>
