import Vue from 'vue';
import axios from 'axios';
import { ready, slideDown } from '@site/js/utils/dom.js';

/**
 * Vue directive that mirrors the native `change` event back into the
 * underlying model and triggers the right basket update.
 */
Vue.directive('select', {
    bind(el, binding, vNode) {
        el.addEventListener('change', () => {
            if (binding.arg === 'id_check_type') {
                vNode.context.clearIdCheckNumber(el.value);
            } else {
                vNode.context.callUpdate(binding.arg);
            }
        });
    },
});

/**
 * Smoothly scroll a target element into view, accounting for the sticky
 * mobile header when present. Replaces the old `html, body` scroll animation.
 */
function smoothScrollTo(target, offset = 0) {
    if (!target) {
        return;
    }
    const rect = target.getBoundingClientRect();
    const top = rect.top + window.scrollY + offset;
    window.setTimeout(() => {
        window.scrollTo({ top, behavior: 'smooth' });
    }, 200);
}

/**
 * Small fade-toggle helper used by `triggerTooltip`. Local to this file —
 * the tooltip is the only consumer and a height-changing slide would look
 * wrong here, so we don't promote it into utils/dom.js.
 */
function fadeToggle(el, duration = 300) {
    const hidden = getComputedStyle(el).display === 'none' || el.style.opacity === '0';
    if (hidden) {
        // Clear inline display:none, then force a visible value if a CSS
        // rule (e.g. `.tooltipContent { display:none }`) is still hiding it.
        if (el.style.display === 'none') {
            el.style.display = '';
        }
        if (getComputedStyle(el).display === 'none') {
            el.style.display = 'block';
        }
        el.style.opacity = '0';
        el.style.transition = `opacity ${duration}ms ease`;
        requestAnimationFrame(() => {
            requestAnimationFrame(() => {
                el.style.opacity = '1';
            });
        });
    } else {
        el.style.opacity = '0';
        el.style.transition = `opacity ${duration}ms ease`;
        window.setTimeout(() => {
            el.style.display = 'none';
        }, duration);
    }
}

/*
 * Main checkout basket component.
 * Provides all basket functionality including communicating with mini basket.
 */
Vue.component('basket', {
    created() {

        this.basket = this.input_basket;
        this.hide_inputs = this.input_hide_inputs;
        this.currencies = this.input_currencies;
        this.countries = this.input_countries;

        if (!this.basket?.info?.newsletter) {
            this.basket.info = {
                newsletter: 0,
            };
        }

        this.basket.info.newsletter = parseInt(this.basket.info.newsletter, 10);

        const bootstrap = window.__INITIAL_BASKET_PAYLOAD__;
        if (bootstrap && bootstrap.basket) {
            this.applyBasketPayload(bootstrap);
            window.__MINIBASKET_BOOTSTRAP__ = bootstrap;
            window.__INITIAL_BASKET_PAYLOAD__ = null;
            if (window.EventBus && typeof window.EventBus.$emit === 'function') {
                window.EventBus.$emit('receiveBasketUpdate', bootstrap);
            }
        } else {
            this.fetchBasket();
        }

        this.hide_inputs = parseInt(this.hide_inputs, 10);

    },

    props: {
        default_gender: {
            type: [String, Boolean],
            default: () => '',
        },
        input_basket: {
            type: [Array, Object, String],
            default: () => [],
        },
        input_hide_inputs: {
            type: [Boolean, Number, String],
            default: 0,
        },
        input_currencies: {
            type: [Array, Object, String],
            default: () => [],
        },
        input_countries: {
            type: [Array, Object, String],
            default: () => [],
        },
    },

    data() {
        return {

            hide_subtotals: true,
            basket: {},
            hide_inputs: 0,
            currencies: {},
            countries: {},
            basket_placeholder_image: false,

            terms: 0,
            busy: true,
            loaded: false,
            errors: {},
            address: {},
            messages: [],
            newsletter: 0,
            state_list: [],
            has_postcodes: [],
            code_sent: {
                email: false,
                phone: false,
            },
            already_verified: {
                email: false,
                phone: false,
            },
            account_verifier: {
                code: {
                    email: [],
                    phone: [],
                },
                verified: {
                    email: false,
                    phone: false,
                },
            },
            how_did_you_hear_about_us_choices: [],
            cardholder_password_check: 0,
            show_total_in_pounds: 0,
            cardholder_password: '',
            empty_basket_message: 'Loading your basket',
            id_check_data: {

                /*
                * ID Check settings
                */
                idOptions: [
                    { id: 'passport', label: 'Passport' },
                    { id: 'id_card', label: 'ID Card' },
                    { id: 'drivers_license', label: 'Drivers License' },
                ],

                // If true, this will add spaces between fields when compiling the final id_number field.
                addSpaces: false,

                // Used for triggering validation errors
                showErrors: false,

                /*
                * ID Check fields
                *
                * Example input field object for 'passport', 'drivers_license' and 'id_card' arrays:
                * {
                *     value: '',
                *     placeholder: 'Example',
                *     length: 11, // The length of the field - used in maxlength="value" and regex validation
                *     regex: '[a-z0-9<]', // Characters for validation - ^, $ and {length} are added later
                *     hidden: true // Optional - This will set the input to type="hidden"
                *     addChevrons: true // Optional - If true, this will populate a field with
                *                       // chevrons to match the length. i.e 1234 will become 1234<<<<<<<
                *                       // if the total length is 11 (Can be seen on the passport fields)
                * }
                */

                passport: [
                    { value: '', placeholder: '107185703', length: 9, regex: '[a-z0-9]' },
                    { value: '', placeholder: '2', length: 1, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'GBR', length: 3, regex: '[a-z]' },
                    { value: '', placeholder: '8501178', length: 7, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'F', length: 1, regex: '[a-z]' },
                    { value: '', placeholder: '1601312', length: 7, regex: '[a-z0-9]' },
                    {
                        value: '<<<<<<<<<<<<<<',
                        placeholder: 'B85475BB<<<<<<',
                        length: 14,
                        regex: '[a-z0-9<]',
                        addChevrons: true,
                    },
                    { value: '', placeholder: '02', length: 2, regex: '[0-9]' },
                ],

                drivers_license: [
                    { value: '', placeholder: 'MORGA', length: 5, regex: '[a-z0-9]' },
                    { value: '', placeholder: '753116', length: 6, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'SM9IJ', length: 5, regex: '[a-z0-9]' },
                ],

                id_card: [
                    { value: '', placeholder: 'ID', length: 2, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'GBR', length: 3, regex: '[a-z]' },
                    { value: '', placeholder: '1234567897', length: 10, regex: '[a-z0-9]' },
                    {
                        value: '<<<<<<<<<<<<<<<',
                        placeholder: '<<<<<<<<<<<<<<<',
                        length: 15,
                        regex: '[a-z0-9<]',
                        hidden: true,
                    },
                    { value: '', placeholder: '7704145', length: 7, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'F', length: 1, regex: '[a-z]' },
                    { value: '', placeholder: '1604117', length: 7, regex: '[a-z0-9]' },
                    { value: '', placeholder: 'GBR', length: 3, regex: '[a-z]' },
                    {
                        value: '<<<<<<<<<<<',
                        placeholder: '<<<<<<<<<<<',
                        length: 11,
                        regex: '[a-z0-9]',
                        hidden: true,
                    },
                    { value: '', placeholder: '4', length: 1, regex: '[a-z0-9]' },
                ],

            },
        };
    },

    events: {
        updateBasket(basket) {
            this.basket = basket;
        },
    },

    computed: {

        hasDoctorItems() {
            if (this.hasField('basket.items')) {
                if (this.hasField('basket.items').length) {
                    const doctorItems = this.basket.items.filter(item => item.product_type === 'doctor');
                    return doctorItems.length > 0;
                }
            }
            return false;
        },

        basket_item_count() {
            if (this.hasField('basket.items')) {
                return this.hasField('basket.items').length;
            }
            return undefined;
        },

        show_delivery_options() {
            let delivery_option_count = 0;
            if (typeof this.basket.delivery_options === 'object') {
                delivery_option_count = this.basket.delivery_options.length;
            }
            return !(this.hide_inputs
                || delivery_option_count < 2
                || this.basket.coupon_freedelivery
            );
        },

        other_for_how_did_you_hear_about_us() {
            return this.basket.info.how_did_you_hear_about_us === 'other'
                || (this.basket.info.how_did_you_hear_about_us !== ''
                    && this.how_did_you_hear_about_us_choices.indexOf(this.basket.info.how_did_you_hear_about_us) === -1);
        },

        current_delivery_name() {
            if (!this.basket.has_physical_items) {
                return '';
            }
            if (this.basket.coupon_freedelivery) {
                return 'Delivery';
            }
            if (this.basket.delivery_options.length > 1) {
                for (let i = 0; i < this.basket.delivery_options.length; i++) {
                    if (this.basket.delivery_options[i].id === this.basket.delivery_selected) {
                        return this.basket.delivery_options[i].name;
                    }
                }
            } else if (Object.keys(this.basket.delivery_options).length === 1) {
                return this.basket.delivery_options[Object.keys(this.basket.delivery_options)[0]].name;
            }
            return 'No delivery available. Please contact us for delivery options';
        },
    },

    methods: {
        applyBasketPayload(payload, options = {}) {
            if (!payload || typeof payload !== 'object') {
                return;
            }

            if (!options.preventBootstrapUpdate) {
                window.__MINIBASKET_BOOTSTRAP__ = payload;
            }

            if (payload.messages && !options.skipMessages) {
                this.messages = payload.messages;
            }

            if (payload.errors && !options.skipErrors) {
                this.errors = payload.errors;
            }

            if (!this.errors || typeof this.errors !== 'object') {
                this.errors = {};
            }
            this.errors.ajax_load = false;

            if (payload.currencies) {
                this.currencies = payload.currencies;
            }
            if (payload.countries) {
                this.countries = payload.countries;
            }
            if (payload.state_list) {
                this.state_list = payload.state_list;
            }
            if (payload.has_postcodes) {
                this.has_postcodes = payload.has_postcodes;
            }
            if (payload.how_did_you_hear_about_us_choices) {
                this.how_did_you_hear_about_us_choices = payload.how_did_you_hear_about_us_choices;
            }
            if (Object.prototype.hasOwnProperty.call(payload, 'basket_placeholder_image')) {
                this.basket_placeholder_image = payload.basket_placeholder_image;
            }
            if (payload.titles) {
                this.titles = payload.titles;
            }
            if (payload.genders) {
                this.genders = payload.genders;
            }

            this.empty_basket_message = 'You have no items in your basket';

            if (payload.basket) {
                const basketData = payload.basket;
                if (!basketData.info || typeof basketData.info !== 'object') {
                    basketData.info = {};
                }
                if (typeof basketData.info.newsletter === 'undefined' || basketData.info.newsletter === null) {
                    basketData.info.newsletter = 0;
                }
                basketData.info.newsletter = parseInt(basketData.info.newsletter, 10) || 0;

                if (!basketData.limit_error || typeof basketData.limit_error !== 'object') {
                    basketData.limit_error = basketData.limit_error ? basketData.limit_error : {};
                }

                this.basket = basketData;

                if (!this.basket.address || typeof this.basket.address !== 'object') {
                    this.basket.address = {};
                }
                if (!this.basket.address.billing) {
                    this.basket.address.billing = {};
                }
                if (!this.basket.address.shipping) {
                    this.basket.address.shipping = {};
                }

                if (this.default_gender.length && this.basket.address?.billing) {
                    this.basket.address.billing.gender = this.default_gender;
                }

                if (typeof this.basket.require_id_check_cascade !== 'undefined' && this.basket.require_id_check_cascade) {
                    window.setTimeout(() => {
                        const idError = document.getElementById('iderror');
                        if (idError) {
                            // -0 offset because the legacy code matched the element top exactly.
                            smoothScrollTo(idError, 0);
                        }
                    }, 500);
                }
            }

            if (!options.preserveFlags) {
                this.busy = false;
                this.loaded = true;
            }
        },

        fetchBasket() {
            const vm = this;
            this.busy = true;
            this.$http.get('/shop/checkout/ajax.php?action=get_basket&ajax').then((response) => {
                if (response.data.status === 'ok') {
                    vm.applyBasketPayload(response.data);
                    window.__MINIBASKET_BOOTSTRAP__ = response.data;
                } else {
                    vm.errors.ajax_load = true;
                    vm.busy = false;
                    vm.loaded = true;
                }
            }).catch(() => {
                vm.errors.ajax_load = true;
                vm.busy = false;
                vm.loaded = true;
            });
        },

        nextDateField(e) {
            if (this.$refs.dobDay?.id === e.target.id) {
                if (this.$refs.dobDay?.value?.length === 2) {
                    this.$refs.dobMonth.focus();
                }
            } else if (this.$refs.dobMonth?.id === e.target.id) {
                if (this.$refs.dobMonth?.value?.length === 2) {
                    this.$refs.dobYear.focus();
                }
            }
        },

        multi_currency(value) {
            return this.$options.filters.multi_currency(value);
        },

        // Called by changing the id type select
        clearIdCheckNumber(type) {
            this.basket.info.id_number = '';
            if (typeof type !== 'undefined') {
                this.buildIdCheckNumber(type);
            }
            this.id_check_data.showErrors = false;
        },

        // Automatically add chevrons to fields (for fields with "addChevrons: true")
        addIdCheckChevrons(value, length) {
            if (value.length && length) {
                let currentLen = value.length;
                while (currentLen < length) {
                    value += '<';
                    currentLen = value.length;
                }
            }
            return value;
        },

        // Jump to the next field once the length has been reached
        nextFieldIdCheck(e, type, index) {
            this.buildIdCheckNumber(type);

            const vm = this;
            const field = this.id_check_data[type][index];

            if (field.value.length < field.length) {
                return;
            }

            // Walk up two levels from the input to reach the row wrapper —
            // the original ascended via two parent() calls on the event target.
            const wrapper = e.target.parentElement?.parentElement;
            if (!wrapper) {
                return;
            }

            const rows = wrapper.querySelectorAll(':scope > .row');
            let nextIndex = false;

            // Find the next row that is visible AND whose value doesn't start
            // with a chevron (the legacy semantics for skippable hidden fields).
            rows.forEach((row, i) => {
                if (nextIndex !== false) {
                    return;
                }
                const firstChar = vm.id_check_data[type][i].value[0];
                const isVisible = row.offsetParent !== null;
                if (i > index && isVisible && firstChar !== '<') {
                    nextIndex = i;
                }
            });

            if (nextIndex === false) {
                return;
            }

            const nextInput = rows[nextIndex]?.querySelector('input');
            nextInput?.focus();
        },

        // Build the main id check number based on individual id check type fields
        buildIdCheckNumber(type) {
            this.basket.info.id_number = '';

            if (!Object.prototype.hasOwnProperty.call(this.id_check_data, type)) {
                return false;
            }

            const fields = this.id_check_data[type];
            let idNumber = '';

            for (const field of fields) {
                if (!field.value.length) {
                    continue;
                }
                if (this.id_check_data.addSpaces) {
                    idNumber += String(field.value).trim() + ' ';
                } else {
                    idNumber += String(field.value).trim();
                }
            }

            if (idNumber.length) {
                this.basket.info.id_number = idNumber.trim().toUpperCase();
            }

            return false;
        },

        // Validate individual id check fields
        validateIdCheckField(e, type, index) {
            const vm = this;
            let isValid = false;

            if (!Object.prototype.hasOwnProperty.call(this.id_check_data, type)) {
                return;
            }

            const field = this.id_check_data[type][index];

            // Populate the rest of the field with chevrons if requested.
            if (Object.prototype.hasOwnProperty.call(field, 'addChevrons') && field.addChevrons) {
                this.id_check_data[type][index].value = this.addIdCheckChevrons(field.value, field.length);
            }

            const value = String(field.value).trim();
            const length = field.length;
            const regex = '^' + field.regex + '{' + length + '}$';
            const inputEl = e.target;
            const wrap = inputEl.parentElement;

            // Build the full id number
            this.buildIdCheckNumber(type);

            // Check validation
            if (typeof value !== 'undefined' && value !== '') {
                const regX = new RegExp(regex, 'i');
                isValid = value.match(regX);
            }

            if (wrap) {
                if (isValid) {
                    wrap.classList.remove('errorWrap');
                } else {
                    wrap.classList.add('errorWrap');
                }
            }

            // After a short delay, check all fields and surface errors only
            // when nothing in the wrapper is focused any more.
            window.setTimeout(() => {
                const wrapper = wrap?.parentElement;
                if (!wrapper) {
                    return;
                }
                const errorCount = wrapper.querySelectorAll('.row.errorWrap').length;
                vm.id_check_data.showErrors = (errorCount > 0);
            }, 100);
        },

        // Whether user can edit a specific items stock
        canEditQty(item) {
            if (typeof item.quantity_lock === 'undefined') {
                return !this.hide_inputs;
            }
            return !(this.hide_inputs || item.quantity_lock);
        },

        hasField(field) {
            // Safely access nested properties without using eval
            if (typeof field !== 'string' || !field.length) return false;
            try {
                return field.split('.').reduce((obj, key) => {
                    if (obj && Object.prototype.hasOwnProperty.call(obj, key)) {
                        return obj[key];
                    }
                    // allow access via dynamic keys even if not own property (e.g., arrays)
                    return obj ? obj[key] : undefined;
                }, this);
            } catch (err) {
                return false;
            }
        },

        notesLength(max_length) {
            const notes = document.getElementById('orderNotes');
            if (!notes) {
                return;
            }
            const max = parseInt(max_length, 10);
            if (max - parseInt(notes.value.length, 10) <= 0) {
                notes.value = notes.value.substring(0, max);
            }
            const remaining = max - parseInt(notes.value.length, 10);
            document.querySelectorAll('.countdown').forEach((el) => {
                el.textContent = remaining + ' characters remaining';
            });
        },

        clearError(field) {
            const split = field.split('.');
            const vm = this;

            if (typeof split === 'undefined' || !split.length) {
                return;
            }

            const [val1, val2, val3, val4, val5] = split;

            try {
                if (typeof val5 !== 'undefined') {
                    vm[val1][val2][val3][val4][val5] = 0;
                } else if (typeof val4 !== 'undefined') {
                    vm[val1][val2][val3][val4] = 0;
                } else if (typeof val3 !== 'undefined') {
                    vm[val1][val2][val3] = 0;
                } else if (typeof val2 !== 'undefined') {
                    vm[val1][val2] = 0;
                }
            } catch (e) {
                // Ignore — clearing optional nested errors is best-effort.
            }
        },

        // Handle the remove item button press
        removeItem(item) {
            if (window.confirm('Are you sure you want to delete this item?')) {
                item.quantity = 0;
                this.callUpdate('update_qty');
            } else {
                this.$http.get('/shop/checkout/ajax.php?action=get_basket_item_qty&ajax&id=' + item.id).then((response) => {
                    if (response.data.status === 'ok') {
                        item.quantity = response.data.quantity;
                    }
                });
            }
        },

        changeStock(item) {
            if (item.quantity > 0) {
                this.callUpdate('update_qty', 'item_' + item.id);
            }
        },

        updateStock(item) {
            if (item.quantity < 1) {
                this.removeItem(item);
            }
        },

        validateCoupon() {
            if (this.basket.coupon_code !== '') {
                this.callUpdate('validate_coupon');
            }
        },

        shippingTrigger() {
            if (this.hasField('errors.address')) {
                this.errors.address.shipping = [];
            }
            this.callUpdate('change_shipping_state');
        },

        hasStates(type) {
            if (typeof this.state_list[type] === 'undefined') {
                return 0;
            }
            return Object.keys(this.state_list[type]).length;
        },

        hasPostcodes(type) {
            if (typeof this.has_postcodes === 'undefined') {
                return false;
            }
            if (typeof this.has_postcodes[type] === 'undefined') {
                return false;
            }
            return true;
        },

        triggerTooltip() {
            const vm = this;
            vm.$nextTick(() => {
                document.querySelectorAll('.tooltipContent').forEach((tooltip) => {
                    fadeToggle(tooltip);
                });
            });
        },

        // Ajax action for managing changes on basket
        callUpdate(action, focus_on) {
            const vm = this;

            this.$http.post('/shop/checkout/ajax.php', {
                ajax: true,
                action,
                basket: this.basket,
                cardholder_password: this.cardholder_password,
                terms: this.terms,
                newsletter: this.newsletter,
            }, {
                before(request) {
                    vm.busy = true;
                    if (this.previousRequest) {
                        this.previousRequest.abort();
                    }
                    this.previousRequest = request;
                },
            }).then((response) => {
                if (response.data.status === 'ok') {
                    vm.$set(vm.basket, 'general_error', null);
                    vm.applyBasketPayload(response.data);

                    if (action === 'store_details') {
                        if (!vm.hasStates('billing')) {
                            vm.basket.address.billing.state = '';
                        }
                        if (!vm.hasStates('billing')) {
                            vm.basket.address.shipping.state = '';
                        }
                    }

                    window.EventBus.$emit('receiveBasketUpdate', response.data);
                    vm.$nextTick(() => {
                        if (focus_on !== 'undefined') {
                            const element = document.getElementById(focus_on);
                            element?.focus();
                        }
                    });
                } else if (response.data.status === 'proceed') {
                    window.location.href = response.data.data.url;
                } else {
                    vm.errors = response.data.errors || {};
                    if (Object.prototype.hasOwnProperty.call(vm.errors, 'general')) {
                        vm.$set(vm.basket, 'general_error', vm.errors.general);
                    } else {
                        vm.$set(vm.basket, 'general_error', null);
                    }

                    // Find the first error and scroll to it.
                    vm.$nextTick(() => {
                        const firstError = document.querySelector('.checkoutForm .errorWrap');
                        if (!firstError) {
                            return;
                        }

                        let offTop = -20;
                        if (window.matchesMediaQuery(0, 'flyout')) {
                            const header = document.getElementById('header');
                            const fixedFlyout = document.querySelector('.fixedFlyout');
                            if (header && fixedFlyout) {
                                offTop -= header.offsetHeight;
                            }
                        }

                        smoothScrollTo(firstError, offTop);
                    });
                }
            }).finally(() => {
                vm.busy = false;
            });
        },
    },
});

ready(() => {
    // Block accidental Enter-key submissions in the checkout form.
    document.querySelectorAll('.checkoutForm').forEach((form) => {
        form.addEventListener('keypress', (event) => {
            if (event.key === 'Enter') {
                event.preventDefault();
            }
        });
    });

    /*
     * Add-to-basket flow on the standalone assessment confirmation page.
     */
    const addedToBasket = document.querySelector('.addedToBasket');
    const errorAddingToBasket = document.querySelector('.errorAddingToBasket');
    const addToBag = document.getElementById('addToBag');
    const addToBasketForm = document.getElementById('addtobasket');

    if (addedToBasket && errorAddingToBasket && addToBag && addToBasketForm) {
        addToBag.addEventListener('click', async (event) => {
            event.preventDefault();
            try {
                const data = new URLSearchParams(new FormData(addToBasketForm));
                data.append('ajax', 'true');
                await axios.post('/shop/assessment.php', data);
                window.addItemToBasket();
            } catch (err) {
                const status = err.response?.status;
                const message = err.response?.data?.message ?? err.message;
                console.error(`[addToBag] ${status ?? 'network error'}: ${message}`);
            }
        });
    }
});

window.addItemToBasket = async function addItemToBasket() {
    const form = document.getElementById('addtobasket');
    if (!form) {
        return;
    }

    const data = new URLSearchParams(new FormData(form));
    data.append('ajax', 'true');
    data.append('add-bag', 'true');

    try {
        const { data: response } = await axios.post('/shop/addtobasket.php', data);
        if (response.status === 'ok') {
            if (response.url) {
                window.location.href = response.url;
                return;
            }
            const added = document.querySelector('.addedToBasket');
            if (added) {
                slideDown(added);
            }
            window.vue_environment?.updateMiniBasket?.();
        } else if (response.error === 'size') {
            const errBox = document.querySelector('.errorAddingToBasket');
            if (errBox) {
                slideDown(errBox);
            }
            document.querySelectorAll('.itemInputWrap .sbHolder').forEach((el) => {
                el.classList.add('inputError');
            });
        }
    } catch (err) {
        const status = err.response?.status;
        const message = err.response?.data?.message ?? err.message;
        console.error(`[addItemToBasket] ${status ?? 'network error'}: ${message}`);
    }
};
