<template>
    <div
        class="date-picker text-center"
        :class="`${calendarViewDirection} ${isPricingByDayClass} ${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 small">
                    {{ 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="separator-line"></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>
                    S
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Monday</span>
                    M
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Tuesday</span>
                    T
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Wednesday</span>
                    W
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Thursday</span>
                    T
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Friday</span>
                    F
                </div>
                <div class="grid-item caption">
                    <span class="sr-only">Saturday</span>
                    S
                </div>
                <div
                    v-for="date in month.dates"
                    :key="date.inputValue"
                    :id="date.inputValue"
                    class="grid-item radio-wrapper"
                    :class="[
                        date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
                        date.dayClasses,
                        date.isSelectable ? 'selectable-day' : '',
                        date.isPricingByDayUpchargeDay ? 'upch-day' : '',
                    ]">
                    <input
                        :disabled="!date.isSelectable"
                        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.isSelectable"
                            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"
                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,
    forceTwoDigitString,
} from "@/digital-components/date-picker/mixins/helpers";
import {
    convertDateToDateString,
    convertDateStringToDate,
} 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,
            selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
            hideSomeDaysForInitialView: null,
        };
    },
    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,
        },
        customSelectableDatesCallback: {
            type: Function,
            default() {
                return [];
            },
        },
        validationRules: {
            type: String,
            default: "",
        },
        showPricingByDay: Boolean,
        pricingByDayBasePrice: Number,
        pricingByDayUpcharge: Number,
        isPricingByDayExperiment: 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 || convertDateToDateString(new Date());
        },
        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";
        },
        isPricingByDayClass() {
            return this.isPricingByDayExperiment ? "pricing-by-day" : "";
        },
        showPricingByDayClass() {
            return this.showPricingByDay ? "show-pricing-by-day" : "";
        },
        selectedDate: {
            get() {
                return this.modelValue;
            },
            set(newSelectedDate) {
                this.$emit("update:modelValue", newSelectedDate);
            },
        },
    },
    methods: {
        initializeComponent(initialData) {
            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, preSelectedDateString) {
            // 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 (preSelectedDateString) {
                let preSelectedDateMonthEnd = this.getMonthEnd(preSelectedDateString);
                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 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 = convertDateToDateString(new Date());
            }
            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.customSelectableDatesCallback(
                    initialViewStartDate,
                    initialViewEndDate,
                    store.getters.order.serviceLocation.appointmentType,
                    store.getters.order.serviceLocation.provider.providerNumber
                );
                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.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView;
            const hideSecondMonth = config.hideSecondMonth;
            const direction = config.calendarViewDirection;
            const monthsAfterToLoadOffset = 6;
            const monthsBeforeToLoadOffset = 36;
            config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => {
                this.selectableDatesData.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 will contain:
                    calendarViewDirection (string)
                    initialViewStartDateNum (number)
                    initialViewEndDateNum (number)
                    monthsBeforeToLoadOffset (number),
                    monthsAfterToLoadOffset (number),
                    hideSecondMonth (boolean),
                    preSelectedDate (string)

                data used:
                    todayDate (date object)
                    this.hideSomeDaysForInitialView (string)
            */

            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;
            const dates = [];
            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";
            }

            // 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 isSelectable =
                    this.selectableDatesData.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,
                    isSelectable: isSelectable,
                    isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
                };
                dates.push(dateObject);
            }

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

            if (this.hideSomeDaysForInitialView) {
                monthToShow = this.months.find(
                    ({ isMonthThatHidesSomeDaysForInitialView }) =>
                        isMonthThatHidesSomeDaysForInitialView
                );
                // find the first day-hidden to become the next api call start date
                monthStartDateNum =
                    monthToShow.dates.find(({ dayClasses }) => dayClasses.includes("day-hidden"))
                        .dateNum - 1;
            } 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) {
                // make new API call with this month's start and end dates
                await this.updateSelectableDates(
                    monthToShow.dates[monthStartDateNum].inputValue,
                    monthToShow.dates[monthToShow.dates.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;
            }
        },
        async updateSelectableDates(monthStart, monthEnd) {
            const moreSelectableDates = await this.customSelectableDatesCallback(
                monthStart,
                monthEnd,
                this.$store.getters.order.serviceLocation.appointmentType,
                this.$store.getters.order.serviceLocation.provider.providerNumber
            );

            moreSelectableDates.days.forEach((selectableDate) => {
                const index = this.selectableDatesData.findIndex(
                    (dateObj) => dateObj.date === selectableDate.date
                );
                if (index === -1) this.selectableDatesData.push(selectableDate);
                this.months.forEach((month) => {
                    // TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above?
                    month.dates.forEach((date) => {
                        if (date.inputValue === selectableDate.date) {
                            date["isSelectable"] = true;
                        }
                    });
                });
            });
        },
        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);
        },
    },
    watch: {
        modelValue(newValue, oldValue) {
            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;

    fieldset {
        flex-grow: 1;
        position: relative;
    }
    .loader {
        height: 2rem;
        width: calc(100% - 1.5rem);

        &::after {
            width: 1.5rem;
            height: 1.5rem;
        }
    }
    .calendar-grid-container {
        margin: 0 auto 2rem auto;
        max-width: 414px;
        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 0.75rem;
        opacity: 1;

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

            &.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;
            text-transform: uppercase;
            font-weight: 300;
            letter-spacing: 0.75px;
        }
        .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;
                cursor: pointer;
                display: flex;
                justify-content: center;
                align-items: center;
                min-width: 2.5rem;
                width: 2.5rem;
                height: 2.5rem;

                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;
                    min-width: 2.5rem;
                    width: 2.5rem;
                    border-radius: 50%;
                }
            }
            &.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: 28px;
                    }
                    .first-day {
                        color: $blue;
                    }
                }
            }
        }

        &.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;
                    }
                }
            }
        }
    }
    // pricing by day override styles
    &.pricing-by-day {
        .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: $white;
                    color: $black;
                }
                &:checked + label {
                    background: $blue-100;
                    border: 2px solid $blue;
                    border-radius: 0.25rem;
                    color: $black;
                    span {
                        font-family: AvertaSemibold;
                        font-weight: 400;
                    }
                }
            }
            label {
                flex-direction: column;
                height: 2.5rem;
                width: 100%;
                font-size: 0.75rem;
                line-height: 1.2;
                justify-content: center;
                padding: 0.25rem;
            }
            &.selectable-day {
                label {
                    color: $black;
                    background-color: $blue-100;
                    min-width: 2.5rem;
                    width: 2.5rem;
                    border-radius: 0.25rem;
                }
            }
            &.current-day {
                label:after {
                    margin-top: 0.2rem;
                    position: relative;
                    top: 0;
                }
            }
        }
        &.show-pricing-by-day {
            .radio-wrapper {
                &.selectable-day label span {
                    text-decoration: none;
                    color: $black;
                    font-weight: 400;
                    font-family: "AvertaSemibold";

                    &.price {
                        font-family: "AvertaRegular";
                        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: "AvertaSemibold";
                        }
                    }
                }
            }
        }
    }
}

.btn-link {
    display: block;
    position: relative;
    height: 2rem;
    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>
