import Vue from 'vue';
import axios from 'axios';
import { EventBus } from '@site/js/event-bus.js';

/**
 * Tracks the in-flight basket update so a newer request can abort an older one,
 * preventing stale responses from clobbering fresher state (e.g. rapid quantity edits).
 * Kept module-scoped rather than in Vuex state as it is a non-serialisable controller.
 */
let inFlightUpdate = null;
let updateSequence = 0;

/**
 * Vuex module for checkout state management
 * Migrated from /shop/js/mtc_checkout.js inline Vue component
 */
export default {
    namespaced: true,

    state: () => ({
        // Core basket data
        basket: {
            items: [],
            address: {
                billing: {},
                shipping: {}
            },
            info: {
                email: '',
                contact_no: '',
                phone_prefix: '',
                newsletter: 0,
                message: '',
                dob_date: '',
                dob_month: '',
                dob_year: '',
                id_type: '',
                id_number: '',
                how_did_you_hear_about_us: '',
                how_did_you_hear_about_us_text: ''
            },
            delivery_options: [],
            delivery_selected: null,
            delivery_instructions: '',
            coupon_code: '',
            coupon_message: '',
            coupon_deduct: 0,
            coupon_freedelivery: false,
            cost_subtotal: 0,
            cost_delivery: 0,
            cost_vat: 0,
            cost_total: 0,
            billing_address_used: 1,
            has_physical_items: false,
            require_id_check: false,
            require_id_check_cascade: false,
            contains_doctor_pharmacy_items: false,
            stock_error: false,
            limit_error: {},
            general_error: null,
            incompatibility_error: false,
            no_vat: false,
            discounts_found: [],
            zone: null,
            allow_alternate_delivery: true
        },

        // UI state
        busy: true,
        loaded: false,
        terms: 0,
        hide_inputs: 0,
        hide_subtotals: true,
        basket_placeholder_image: false,
        empty_basket_message: 'Loading your basket',
        cardholder_password_check: 0,
        show_total_in_pounds: 0,
        cardholder_password: '',

        // Lookup data (from server)
        currencies: {},
        countries: {},
        genders: {},
        titles: {},
        state_list: { billing: {}, shipping: {} },
        has_postcodes: {},
        how_did_you_hear_about_us_choices: [],

        // Validation
        errors: {},
        messages: [],

        // Account verification
        code_sent: { email: false, phone: false },
        already_verified: { email: false, phone: false },
        account_verifier: {
            code: { email: [], phone: [] },
            verified: { email: false, phone: false }
        },

        // ID Check specific state
        id_check_data: {
            idOptions: [
                { id: 'passport', label: 'Passport' },
                { id: 'id_card', label: 'ID Card' },
                { id: 'drivers_license', label: 'Drivers License' }
            ],
            addSpaces: false,
            showErrors: false,
            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]' }
            ]
        },

        // Config (populated from window.__CHECKOUT_CONFIG__)
        config: {
            hideInputs: false,
            termsShow: true,
            orderNotesEnabled: false,
            orderNotesMaxLength: 500,
            membersSection: false,
            memberId: null,
            member: {},
            couponEnabled: true,
            basketShowVat: true,
            allowAlternateDelivery: true,
            howDidYouHearEnabled: false,
            defaultGender: ''
        }
    }),

    getters: {
        basketItemCount: state => state.basket.items?.length || 0,

        // Basket has finished loading and did not fail; gates the payment form on the overview page.
        checkoutReady: state => state.loaded && !state.errors?.ajax_load,

        hasDoctorItems: state => {
            if (state.basket.items?.length) {
                return state.basket.items.some(item => item.product_type === 'doctor');
            }
            return false;
        },

        showDeliveryOptions: state => {
            let deliveryOptionCount = 0;
            if (typeof state.basket.delivery_options === 'object') {
                deliveryOptionCount = state.basket.delivery_options.length;
            }
            return !(state.hide_inputs || deliveryOptionCount < 2 || state.basket.coupon_freedelivery);
        },

        currentDeliveryName: state => {
            if (!state.basket.has_physical_items) {
                return '';
            }

            if (state.basket.coupon_freedelivery) {
                return 'Delivery';
            }

            if (state.basket.delivery_options.length > 1) {
                for (let i = 0; i < state.basket.delivery_options.length; i++) {
                    if (state.basket.delivery_options[i].id === state.basket.delivery_selected) {
                        return state.basket.delivery_options[i].name;
                    }
                }
            } else if (Object.keys(state.basket.delivery_options).length === 1) {
                return state.basket.delivery_options[Object.keys(state.basket.delivery_options)[0]].name;
            }

            return 'No delivery available. Please contact us for delivery options';
        },

        otherForHowDidYouHear: state => {
            return state.basket.info.how_did_you_hear_about_us === 'other'
                || (state.basket.info.how_did_you_hear_about_us !== ''
                    && state.how_did_you_hear_about_us_choices.indexOf(state.basket.info.how_did_you_hear_about_us) === -1);
        },

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

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

        canEditQty: state => item => {
            if (typeof item.quantity_lock === 'undefined') {
                return !state.hide_inputs;
            } else {
                return !(state.hide_inputs || item.quantity_lock);
            }
        }
    },

    mutations: {
        SET_BASKET(state, basket) {
            state.basket = { ...state.basket, ...basket };
        },

        SET_BASKET_FIELD(state, { path, value }) {
            const parts = path.split('.');
            let obj = state.basket;
            for (let i = 0; i < parts.length - 1; i++) {
                if (!obj[parts[i]]) {
                    // Vue.set so newly-created intermediate objects are reactive
                    Vue.set(obj, parts[i], {});
                }
                obj = obj[parts[i]];
            }
            // Vue.set so a brand-new leaf key (e.g. address.billing.gender) is reactive
            Vue.set(obj, parts[parts.length - 1], value);
        },

        SET_BUSY(state, busy) {
            state.busy = busy;
        },

        SET_LOADED(state, loaded) {
            state.loaded = loaded;
        },

        SET_TERMS(state, terms) {
            state.terms = terms;
        },

        SET_HIDE_INPUTS(state, value) {
            state.hide_inputs = parseInt(value, 10);
        },

        SET_ERRORS(state, errors) {
            state.errors = (errors && !Array.isArray(errors)) ? errors : {};
        },

        SET_MESSAGES(state, messages) {
            state.messages = messages || [];
        },

        // Path is relative to state.errors, e.g. 'address.billing.firstname' clears state.errors.address.billing.firstname.
        CLEAR_ERROR(state, path) {
            if (!state.errors || typeof state.errors !== 'object') return;
            const parts = path.split('.');
            let obj = state.errors;

            try {
                for (let i = 0; i < parts.length - 1; i++) {
                    if (!obj[parts[i]]) return;
                    obj = obj[parts[i]];
                }
                obj[parts[parts.length - 1]] = 0;
            } catch (e) {
                // Path doesn't exist, ignore
            }
        },

        SET_CURRENCIES(state, currencies) {
            state.currencies = currencies;
        },

        SET_COUNTRIES(state, countries) {
            state.countries = countries;
        },

        SET_GENDERS(state, genders) {
            state.genders = genders;
        },

        SET_TITLES(state, titles) {
            state.titles = titles;
        },

        SET_STATE_LIST(state, stateList) {
            state.state_list = stateList;
        },

        SET_HAS_POSTCODES(state, hasPostcodes) {
            state.has_postcodes = hasPostcodes;
        },

        SET_HOW_DID_YOU_HEAR_CHOICES(state, choices) {
            state.how_did_you_hear_about_us_choices = choices;
        },

        SET_BASKET_PLACEHOLDER_IMAGE(state, image) {
            state.basket_placeholder_image = image;
        },

        SET_EMPTY_BASKET_MESSAGE(state, message) {
            state.empty_basket_message = message;
        },

        SET_CARDHOLDER_PASSWORD(state, password) {
            state.cardholder_password = password;
        },

        SET_CODE_SENT(state, { type, value }) {
            state.code_sent[type] = value;
        },

        SET_ALREADY_VERIFIED(state, { type, value }) {
            state.already_verified[type] = value;
        },

        SET_ACCOUNT_VERIFIER_CODE(state, { type, code }) {
            state.account_verifier.code[type] = code;
        },

        SET_ACCOUNT_VERIFIER_VERIFIED(state, { type, verified }) {
            state.account_verifier.verified[type] = verified;
        },

        SET_ID_CHECK_DATA(state, data) {
            state.id_check_data = { ...state.id_check_data, ...data };
        },

        SET_ID_CHECK_FIELD_VALUE(state, { type, index, value }) {
            if (state.id_check_data[type] && state.id_check_data[type][index]) {
                state.id_check_data[type][index].value = value;
            }
        },

        SET_ID_CHECK_SHOW_ERRORS(state, show) {
            state.id_check_data.showErrors = show;
        },

        SET_CONFIG(state, config) {
            state.config = { ...state.config, ...config };
        },

        SET_GENERAL_ERROR(state, error) {
            state.basket.general_error = error;
        }
    },

    actions: {
        /**
         * Initialize checkout from bootstrap data
         */
        initFromBootstrap({ dispatch }) {
            // All data (basket + config) is fetched via AJAX to avoid exposing any data in page source
            // This eliminates the window.__ pattern entirely, preventing future devs from adding sensitive data
            dispatch('fetchBasket');
        },

        /**
         * Fetch basket data from server
         */
        async fetchBasket({ commit, dispatch }) {
            commit('SET_BUSY', true);

            try {
                const response = await axios.get('/shop/checkout/ajax.php?action=get_basket&ajax');
                if (response.data.status === 'ok') {
                    dispatch('applyBasketPayload', response.data);
                    window.__MINIBASKET_BOOTSTRAP__ = response.data;
                } else {
                    commit('SET_ERRORS', { ajax_load: true });
                }
            } catch (error) {
                commit('SET_ERRORS', { ajax_load: true });
            } finally {
                commit('SET_BUSY', false);
                commit('SET_LOADED', true);
            }
        },

        /**
         * Apply basket payload from server response
         */
        applyBasketPayload({ commit, state }, payload) {
            if (!payload || typeof payload !== 'object') {
                return;
            }

            // Update mini basket bootstrap
            window.__MINIBASKET_BOOTSTRAP__ = payload;

            if (payload.messages) {
                commit('SET_MESSAGES', payload.messages);
            }

            if (payload.errors) {
                commit('SET_ERRORS', payload.errors);
            }

            if (!state.errors || typeof state.errors !== 'object') {
                commit('SET_ERRORS', {});
            }
            commit('CLEAR_ERROR', 'ajax_load');

            if (payload.currencies) {
                commit('SET_CURRENCIES', payload.currencies);
            }
            if (payload.countries) {
                commit('SET_COUNTRIES', payload.countries);
            }
            if (payload.state_list) {
                commit('SET_STATE_LIST', payload.state_list);
            }
            if (payload.has_postcodes) {
                commit('SET_HAS_POSTCODES', payload.has_postcodes);
            }
            if (payload.how_did_you_hear_about_us_choices) {
                commit('SET_HOW_DID_YOU_HEAR_CHOICES', payload.how_did_you_hear_about_us_choices);
            }
            if (Object.prototype.hasOwnProperty.call(payload, 'basket_placeholder_image')) {
                commit('SET_BASKET_PLACEHOLDER_IMAGE', payload.basket_placeholder_image);
            }
            if (payload.titles) {
                commit('SET_TITLES', payload.titles);
            }
            if (payload.genders) {
                commit('SET_GENDERS', payload.genders);
            }

            // Apply config settings from AJAX response (replaces window.__CHECKOUT_CONFIG__)
            if (payload.config) {
                commit('SET_CONFIG', payload.config);
            }

            commit('SET_EMPTY_BASKET_MESSAGE', 'You have no items in your basket');

            if (payload.basket) {
                const basketData = payload.basket;

                // Ensure info object exists
                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) || 0;

                // Ensure limit_error object exists
                if (!basketData.limit_error || typeof basketData.limit_error !== 'object') {
                    basketData.limit_error = {};
                }

                commit('SET_BASKET', basketData);

                // Ensure address objects exist
                if (!state.basket.address || typeof state.basket.address !== 'object') {
                    commit('SET_BASKET_FIELD', { path: 'address', value: {} });
                }
                if (!state.basket.address.billing) {
                    commit('SET_BASKET_FIELD', { path: 'address.billing', value: {} });
                }
                if (!state.basket.address.shipping) {
                    commit('SET_BASKET_FIELD', { path: 'address.shipping', value: {} });
                }

                // Set default gender if configured
                if (state.config.defaultGender && state.basket.address?.billing) {
                    commit('SET_BASKET_FIELD', {
                        path: 'address.billing.gender',
                        value: state.config.defaultGender
                    });
                }
            }

            commit('SET_BUSY', false);
            commit('SET_LOADED', true);
        },

        /**
         * Call update action on basket
         */
        async callUpdate({ commit, state, dispatch }, { action, focusOn }) {
            // Abort any in-flight update so its (older) response cannot overwrite newer state.
            if (inFlightUpdate) {
                inFlightUpdate.abort();
            }
            const controller = new AbortController();
            inFlightUpdate = controller;
            const requestId = ++updateSequence;

            commit('SET_BUSY', true);

            try {
                // Send as URL-encoded form data to match vue-resource format expected by backend
                // PHP expects nested array format like basket[items][0][id]
                const response = await axios.post('/shop/checkout/ajax.php', {
                    ajax: true,
                    action: action,
                    basket: state.basket,
                    cardholder_password: state.cardholder_password || '',
                    terms: state.terms,
                    newsletter: state.basket.info?.newsletter || 0
                }, {
                    signal: controller.signal,
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    transformRequest: [(data) => {
                        // Convert nested object to URL-encoded format
                        const params = new URLSearchParams();

                        const buildFormData = (obj, prefix = '') => {
                            for (const key in obj) {
                                if (Object.prototype.hasOwnProperty.call(obj, key)) {
                                    const value = obj[key];
                                    const formKey = prefix ? `${prefix}[${key}]` : key;

                                    if (value !== null && typeof value === 'object' && !(value instanceof Date)) {
                                        buildFormData(value, formKey);
                                    } else {
                                        params.append(formKey, value ?? '');
                                    }
                                }
                            }
                        };

                        buildFormData(data);
                        return params.toString();
                    }]
                });

                // Ignore a response that has since been superseded by a newer request.
                if (requestId !== updateSequence) {
                    return { success: false, stale: true };
                }

                if (response.data.status === 'ok') {
                    commit('SET_GENERAL_ERROR', null);
                    dispatch('applyBasketPayload', response.data);

                    if (action === 'store_details') {
                        // Clear state fields if no states available
                        const hasStates = state.state_list;
                        if (!hasStates.billing || !Object.keys(hasStates.billing).length) {
                            commit('SET_BASKET_FIELD', { path: 'address.billing.state', value: '' });
                        }
                        if (!hasStates.shipping || !Object.keys(hasStates.shipping).length) {
                            commit('SET_BASKET_FIELD', { path: 'address.shipping.state', value: '' });
                        }
                    }

                    // Emit event for mini basket sync
                    EventBus.$emit('receiveBasketUpdate', response.data);

                    return { success: true, focusOn };
                } else if (response.data.status === 'proceed') {
                    window.location.href = response.data.data.url;
                    return { success: true, redirect: true };
                } else {
                    commit('SET_ERRORS', response.data.errors || {});
                    if (response.data.errors?.general) {
                        commit('SET_GENERAL_ERROR', response.data.errors.general);
                    } else {
                        commit('SET_GENERAL_ERROR', null);
                    }
                    return { success: false, errors: response.data.errors };
                }
            } catch (error) {
                // A deliberately aborted request is not a user-facing error.
                if (axios.isCancel(error) || error.name === 'CanceledError' || error.name === 'AbortError') {
                    return { success: false, cancelled: true };
                }
                commit('SET_ERRORS', { general: 'An error occurred. Please try again.' });
                return { success: false, error };
            } finally {
                // Only release busy state for the latest request; an aborted one leaves
                // the newer request's spinner intact.
                if (requestId === updateSequence) {
                    inFlightUpdate = null;
                    commit('SET_BUSY', false);
                }
            }
        },

        /**
         * Update item quantity
         */
        async updateItemQuantity({ dispatch }, { itemId, quantity }) {
            return dispatch('callUpdate', { action: 'update_qty' });
        },

        /**
         * Remove item from basket
         */
        async removeItem({ commit, state, dispatch }, item) {
            if (!window.confirm('Are you sure you want to delete this item?')) {
                // Refresh the item quantity from server
                try {
                    const response = await axios.get(`/shop/checkout/ajax.php?action=get_basket_item_qty&ajax&id=${item.id}`);
                    if (response.data.status === 'ok') {
                        const itemIndex = state.basket.items.findIndex(i => i.id === item.id);
                        if (itemIndex !== -1) {
                            const newItems = [...state.basket.items];
                            newItems[itemIndex] = { ...newItems[itemIndex], quantity: response.data.quantity };
                            commit('SET_BASKET_FIELD', { path: 'items', value: newItems });
                        }
                    }
                } catch (error) {
                    // Ignore
                }
                return;
            }

            // Set quantity to 0 to remove
            const itemIndex = state.basket.items.findIndex(i => i.id === item.id);
            if (itemIndex !== -1) {
                const newItems = [...state.basket.items];
                newItems[itemIndex] = { ...newItems[itemIndex], quantity: 0 };
                commit('SET_BASKET_FIELD', { path: 'items', value: newItems });
            }

            return dispatch('callUpdate', { action: 'update_qty' });
        },

        /**
         * Validate coupon code
         */
        async validateCoupon({ state, dispatch }) {
            if (state.basket.coupon_code !== '') {
                return dispatch('callUpdate', { action: 'validate_coupon' });
            }
        },

        /**
         * Trigger shipping state change
         */
        async shippingTrigger({ commit, state, dispatch }) {
            commit('SET_ERRORS', { ...state.errors, address: { ...state.errors?.address, shipping: {} } });
            return dispatch('callUpdate', { action: 'change_shipping_state' });
        },

        /**
         * Clear ID check number
         */
        clearIdCheckNumber({ commit, dispatch }, type) {
            commit('SET_BASKET_FIELD', { path: 'info.id_number', value: '' });
            if (typeof type !== 'undefined') {
                dispatch('buildIdCheckNumber', type);
            }
            commit('SET_ID_CHECK_SHOW_ERRORS', false);
        },

        /**
         * Build ID check number from individual fields
         */
        buildIdCheckNumber({ commit, state }, type) {
            let idNumber = '';

            if (state.id_check_data.hasOwnProperty(type)) {
                const fields = state.id_check_data[type];

                for (let i = 0; i < fields.length; i++) {
                    const field = fields[i];
                    if (field.value.length) {
                        if (state.id_check_data.addSpaces) {
                            idNumber += field.value.trim() + ' ';
                        } else {
                            idNumber += field.value.trim();
                        }
                    }
                }

                if (idNumber.length) {
                    commit('SET_BASKET_FIELD', {
                        path: 'info.id_number',
                        value: idNumber.trim().toUpperCase()
                    });
                }
            }
        },

        /**
         * Add chevrons to ID check field value
         */
        addIdCheckChevrons(_, { value, length }) {
            if (value.length && length) {
                while (value.length < length) {
                    value += '<';
                }
            }
            return value;
        }
    }
};
