<template>
    <label
        :class="[
            buttonWrapperClasses,
            { 'has-error': errors.length > 0 && !suppressError, selected: isChecked },
        ]"
        :for="buttonId"
        @focusin="handleFocus"
        @focusout="handleBlur"
        @mousedown.left="handleEventAction(eventTypes.CLICK, $event)">
        <input
            :type="inputType"
            :id="buttonId"
            :key="buttonId"
            :name="groupName"
            :class="inputClasses"
            :aria-required="isRequired"
            :value="value"
            :checked="isChecked"
            @keypress.space="handleEventAction(eventTypes.SPACE, $event)"
            @keypress.enter="handleEventAction(eventTypes.ENTER, $event)"
            @change="handleEventAction(eventTypes.CHANGE, $event)" />

        <slot></slot>
    </label>
</template>

<script>
import { useField } from "vee-validate";
import { toRef } from "vue";
import { queryStrings } from "@/constants/query-strings";
import {
    handleButtonComponentFocus,
    handleInputComponentBlur,
} from "@/helpers/button-question-focus-helper";
import { inputButtonProps } from "@/digital-components/base-input-button/button-functionality-props";

export default {
    name: "base-input-button",
    props: {
        ...inputButtonProps,
        buttonWrapperClasses: [String, Array, Object],
        inputClasses: [String, Array, Object],
    },
    data() {
        return {
            valueToEmit: null,
        };
    },
    mounted() {
        // Need this here and in the watch for vehicle-parts, unless there'a better way
        if (this.isChecked) {
            this.handleChange(this.modelValue);
        }
    },
    methods: {
        handleEventAction(eventType, e) {
            if (this.isMultiSelect) {
                switch (eventType) {
                    case this.eventTypes.CHANGE:
                        this.handleClick(e);
                        this.handlePushClickEventToGACheck(this.eventTypes.CLICK);
                        break;
                }
            } else {
                switch (eventType) {
                    case this.eventTypes.CLICK:
                    case this.eventTypes.ENTER:
                    case this.eventTypes.SPACE:
                        this.handleClick(e);
                        this.handlePushClickEventToGACheck(this.eventTypes.CLICK);
                        break;
                    case this.eventTypes.CHANGE:
                        this.selectingInitiatesLoad
                            ? this.handleSelectionChange(e)
                            : this.handleClick(e);
                        break;
                }
            }
        },
        handleSelectionChange(e) {
            if (
                this.isMultiSelect &&
                (this.modelValue instanceof Array || this.modelValue == null)
            ) {
                let newValue = this.modelValue ? [...this.modelValue] : [];
                if (!newValue.includes(this.value)) {
                    newValue.push(this.value);
                } else {
                    newValue.splice(newValue.indexOf(this.value), 1);
                }

                this.valueToEmit = newValue;
            } else if (!this.isMultiSelect) {
                this.valueToEmit = this.value;
            }

            this.handleChange(this.valueToEmit);
        },
        handleClick(e) {
            this.handleSelectionChange(e);
            this.$emit("update:modelValue", this.valueToEmit);
        },
        handleFocus() {
            handleButtonComponentFocus({
                groupName: this.groupName,
            });
        },
        handleBlur() {
            handleInputComponentBlur({
                groupName: this.groupName,
                onButtonQuestionLostFocusCallback: this.handlePushClickEventToGACheck,
            });
        },
        handlePushClickEventToGACheck(source) {
            // if from a click or click-like event
            if (source === this.eventTypes.CLICK) {
                this.pushClickEventToGA();
            } else {
                // if from tabbing around
                if (
                    this.valueToEmit !== null &&
                    !this.isValueSelectedOnClick &&
                    this.isChecked &&
                    this.lastValuePushedToGa != this.value
                ) {
                    this.pushClickEventToGA();
                }
            }
        },
        pushClickEventToGA(value) {
            this.pushEventToGA(
                this.$route.query[queryStrings.FMG_PAGE],
                this.GaActions.CLICKED,
                value?.toString() ?? this.value?.toString(),
                true,
                this.valueToLogType
            );

            this.setLastValuePushedToGa(value ?? this.value);
        },
    },
    computed: {
        isChecked() {
            if (this.isMultiSelect && this.modelValue instanceof Array) {
                return this.modelValue.includes(this.value);
            } else if (!this.isMultiSelect) {
                return this.modelValue == this.value;
            } else {
                return false;
            }
        },
        inputType() {
            return this.isMultiSelect ? "checkbox" : "radio";
        },
        buttonId() {
            return `${this.groupName?.replace(" ", "-")}-${this.value
                ?.toString()
                ?.replace(" ", "-")}`;
        },
        isValueSelectedOnClick() {
            return this.isMultiSelect || this.selectingInitiatesLoad;
        },
        eventTypes() {
            return {
                CHANGE: "change",
                ENTER: "enter",
                SPACE: "space",
                CLICK: "click",
            };
        },
    },
    setup(props) {
        const inputType = props.isMultiSelect ? "checkbox" : "radio";

        const fieldOptions = {
            type: inputType,
            validateOnValueUpdate: false,
            validateOnMount: false,
        };

        const { handleChange, meta, errors } = useField(
            toRef(props, "groupName"),
            toRef(props, "validationRules"),
            fieldOptions
        );

        return {
            handleChange,
            errors,
            meta,
            fieldOptions, // only need to expose this for unit test purposes
        };
    },
    watch: {
        isChecked(isChecked) {
            if (isChecked) {
                this.handleChange(this.modelValue);
            }
        },
    },
};
</script>

<style lang="scss" scoped>
input {
    opacity: 0;
    height: 0.1px; // NOTE: cannot be zero or Safari can't put focus on it
    width: 0;
    position: absolute;
}
</style>
