<template>
    <div role="application">
        <div class="row">
            <div class="col">
                <textboxQuestion
                    id="streetAddressField"
                    ref="autocomplete"
                    v-model="addressModel.streetAddress"
                    cmsWidgetName="StreetAddressQuestionWidget"
                    inputId="autocomplete"
                    aria-haspopup=""
                    hasIcon
                    disableAutoFill
                    validationRules="street-address-required"
                    @keydown.enter.prevent />
            </div>
        </div>
        <transition
            name="fade"
            mode="out-in">
            <div v-show="showAllFields">
                <div
                    v-if="includeStreetAddress2"
                    class="row">
                    <div class="col">
                        <textboxQuestion
                            ref="streetAddress2"
                            v-model="addressModel.streetAddress2"
                            cmsWidgetName="StreetAddress2QuestionWidget"
                            aria-haspopup=""
                            inputId="streetAddress2Field" />
                    </div>
                </div>
                <div
                    class="row"
                    aria-live="polite">
                    <div class="col-lg-6">
                        <textboxQuestion
                            ref="city"
                            v-model="addressModel.city"
                            cmsWidgetName="CityQuestionWidget"
                            inputId="cbf28188fdf2436688fd735915f7ee56"
                            disableAutoFill
                            validationRules="city-required" />
                    </div>
                    <div class="col-lg-6">
                        <dropdownQuestion
                            ref="state"
                            v-model="addressModel.state"
                            cmsWidgetName="StateQuestionWidget"
                            :placeHolderText="statePlaceHolderText"
                            inputId="8fdf9dc2e13e430eb57529499dceb3eb"
                            :options="stateOptions"
                            disableAutoFill
                            validationRules="state-required" />
                    </div>
                </div>
                <div
                    class="row"
                    aria-live="polite">
                    <div class="col">
                        <textboxQuestion
                            ref="zipCode"
                            v-model="addressModel.zipCode"
                            cmsWidgetName="ZipQuestionWidget"
                            inputId="01a9a1c2de0b4c9da8e023c9ae3be498"
                            mask="#####"
                            disableAutoFill
                            validationRules="zip-code-required|zip-code-format" />
                    </div>
                </div>
            </div>
        </transition>
        <alert
            v-if="displayVerificationWarning"
            ref="alertVerificationWarning"
            class="mb-4"
            cmsWidgetName="AlertVerificationWarningWidget"
            alertClass="alert-warning"
            :isDismissible="false" />
        <alert
            v-if="displayNoMatchWarning"
            ref="alertNoMatchWarning"
            class="mb-4 mt-4"
            cmsWidgetName="AlertNoMatchWarningWidget"
            alertClass="alert-warning"
            :isDismissible="false" />
    </div>
</template>

<script>
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
import alert from '@/ux-components/alert/alert.vue';
import applicationConfig from '@/constants/application-config.js';
import { defineRule } from 'vee-validate';
import { required, regex } from '@/helpers/validation-rules';
import errorMessages from '@/constants/error-messages';
import states from '@/constants/states';

// DEFINE VALIDATION RULES
defineRule('street-address-required', required(errorMessages.STREET_ADDRESS_REQUIRED));
defineRule('city-required', required(errorMessages.CITY_REQUIRED));
defineRule('state-required', required(errorMessages.STATE_REQUIRED));
defineRule('zip-code-required', required(errorMessages.ZIP_REQUIRED));
defineRule('zip-code-format', regex(/^\d{5}$/, errorMessages.ZIP_FORMAT));

export default {
    name: 'address-questions',
    components: {
        textboxQuestion,
        dropdownQuestion,
        alert
    },
    props: {
        modelValue: {
            type: Object,
            default: () => ({
                streetAddress: '',
                streetAddress2: '',
                city: '',
                state: '',
                zipCode: ''
            })
        },
        validationRules: String,
        includeStreetAddress2: {
            type: Boolean,
            default: false
        }
    },
    emits: ['update:modelValue'],
    data() {
        return {
            autocomplete: null,
            alertCopyNoMatchWarning: '',
            alertCopyVerificationWarning: '',
            alertHeadlineNoMatchWarning: '',
            alertHeadlineVerificationWarning: '',
            displayVerificationWarning: false,
            displayNoMatchWarning: false,
            enterPressed: false,
            matchingIndirectly: false,
            matchFound: null, // null = no attempted match, true = match was found, false = match was not found
            showAllFields: false
        };
    },
    computed: {
        addressField1: {
            get() {
                return document.getElementById('autocomplete');
            }
        },
        addressModel: {
            get() {
                return this.modelValue;
            },
            set(newValue) {
                this.$emit('update:modelValue', newValue);
            }
        },
        stateOptions: {
            get() {
                return states;
            }
        },
        statePlaceHolderText() {
            return this.getCmsContent('StateQuestionWidget', 'ButtonText');
        }
    },
    watch: {
        matchFound: {
            handler(newValue) {
                this.displayNoMatchWarning = !newValue;
                if (!newValue) {
                    this.addressModel.city = '';
                    this.addressModel.state = '';
                    this.addressModel.zipCode = '';
                    this.displayVerificationWarning = false;
                }
            }
        }
    },
    mounted() {
        // If we already have a full address, show it
        this.showAllFields =
            (this.addressModel.streetAddress ?? '') !== ''
            && (this.addressModel.city ?? '') !== ''
            && (this.addressModel.state ?? '') !== ''
            && (this.addressModel.zipCode ?? '') !== '';

        if (!this.showAllFields) {
            this.setupAddressLookup();
        }
    },
    methods: {
        initializeAutocomplete() {
            // Initialize the Google Places Autocomplete
            this.autocomplete = new window.google.maps.places.Autocomplete(document.getElementById('autocomplete'), {
                componentRestrictions: { country: ['us'] },
                fields: ['address_components'],
                types: ['address']
            });

            // Set up the Autocomplete place_changed event to call our method to fill in the address
            this.autocomplete.addListener('place_changed', this.onPlaceChanged);

            // Set up the pressing tab inside address1 field (enter's are caught by Google Autocomplete)
            this.addressField1.addEventListener('keydown', (e) => {
                // If a match has been previously attempted then do nothing
                if (this.matchFound !== null) {
                    return;
                }

                if (this.addressField1.value?.length > 0 && e.code === 'Tab') {
                    this.fillInAddressUsingFirstItem();
                }
            });
        },
        onPlaceChanged() {
            const place = this.autocomplete.getPlace();
            if (place && place.address_components) {
                this.matchingIndirectly = false;
                this.fillInAddress(place);
            } else {
                this.fillInAddressUsingFirstItem();
            }
        },
        fillInAddress(googlePlace) {
            this.matchFound = true;

            const self = this;
            this.$nextTick(() => {
                this.showAllFields = true;
                // eslint-disable-next-line no-restricted-syntax
                let processedStreetAddress = false;
                let processedRoute = false;
                for (const component of googlePlace.address_components) {
                    const componentType = component.types[0];

                    switch (componentType) {
                        case 'street_number': {
                            if (processedRoute) {
                                self.addressModel.streetAddress = `${component.long_name} ${self.addressModel.streetAddress}`;
                            } else {
                                self.addressModel.streetAddress = component.long_name;
                            }

                            processedStreetAddress = true;
                            break;
                        }
                        case 'route': {
                            if (processedStreetAddress) {
                                self.addressModel.streetAddress += ` ${component.short_name}`;
                            } else {
                                self.addressModel.streetAddress = component.short_name;
                            }

                            processedRoute = true;
                            break;
                        }
                        case 'locality': {
                            self.addressModel.city = component.long_name;
                            break;
                        }
                        case 'administrative_area_level_1': {
                            self.addressModel.state = component.short_name;
                            break;
                        }
                        case 'postal_code': {
                            self.addressModel.zipCode = component.long_name;
                            break;
                        }
                        default:
                    }
                }

                // After filling in the address fields, disable the address autocomplete
                this.unloadAutocomplete();
                // Restore focus to the first address field
                this.addressField1.focus();

                self.displayVerificationWarning = self.matchingIndirectly;
            });
        },
        fillInAddressUsingFirstItem() {
            const addressValue = this.addressField1.value;
            if (addressValue && addressValue.length > 0) {
                // Get Autocomplete Service
                const acService = new window.google.maps.places.AutocompleteService();
                // Get Places Service, needs psuedo element (or a map)
                const placeService = new window.google.maps.places.PlacesService(document.createElement('div'));
                // Create Autocomplete Session token, multiple requests one pricing hit
                const acSessionToken = new window.google.maps.places.AutocompleteSessionToken();

                acService.getPlacePredictions(
                    {
                        input: addressValue,
                        type: ['geocode'],
                        componentRestrictions: { country: ['us'] },
                        sessionToken: acSessionToken
                    },
                    (predictions) => {
                        if (predictions && predictions.length > 0) {
                            const firstPrediction = predictions[0];
                            if (firstPrediction.place_id) {
                                placeService.getDetails(
                                    {
                                        placeId: firstPrediction.place_id,
                                        fields: ['address_components'],
                                        sessionToken: acSessionToken
                                    },
                                    (details) => {
                                        this.matchingIndirectly = true;
                                        this.fillInAddress(details);
                                    }
                                );
                            } else {
                                // No place_id found for the prediction
                                this.matchFound = false;
                            }
                        } else {
                            // No prediction found for the input
                            this.matchFound = false;
                        }
                    }
                );
            }
        },
        setupAddressLookup() {
            const apiKey = applicationConfig.GOOGLE_PLACES_API_KEY;

            this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=Function.prototype`)
                .then(() => {
                    // Script is loaded, initialize the autocomplete textbox
                    this.initializeAutocomplete();
                })
                .catch(() => {
                    // Failed to fetch script
                    window.console.warn('Unable to load Google Places API script');
                });
        },
        resetAlerts() {
            this.displayVerificationWarning = false;
        },
        unloadAutocomplete() {
            if (this.autocomplete) {
                window.google.maps.event.clearInstanceListeners(this.autocomplete);
                this.autocomplete = null;

                const pacContainer = document.querySelector('.pac-container');
                if (pacContainer) {
                    pacContainer.remove();
                }
            }
        }
    }
};
</script>

<style scoped lang="scss">
.textbox-question {
    margin-bottom: 1.25rem;
}
.dropdown-question {
    margin-bottom: 1.25rem;
}
</style>
