/**
 * Global type definition
 * @typedef {Object}  fePaymentData     - Array of localization strings
 * @property {string} endpoint          - URL
 * @property {string} currency          - Currency sign
 * @property {string} empty_field       - Not all input fields have been completed
 * @property {string} invalid_email     - Invalid emailaddress
 * @property {string} check_terms       - You must agree to the terms
 * @property {string} no_tickets        - No tickets ordered
 * @property {string} timeout           - The server did not respond within the given timeframe (5 seconds)
 * @property {string} no_route          - The server does not support this request; please ask the site administrator for help
 * @property {string} fetch_failed      - Could not connect to the server; please ask the site administrator for help
 * @property {string} forbidden         - You do not have permission on the server to execute this request; please ask the site administrator for help
 * @property {string} invalid_json      - The server did not return valid JSON
 *
 * @typedef {Object}  json              - Event info needed for form input
 * @property {string} event_type        - Type of event (0 or 1)
 * @property {string} group_type        - Type of group (0, 1, 2, 3)
 * @property {string} event_group       - Group name, can be empty
 * @property {string} extra_input       - Array of extra input fields
 * @property {string} terms             - 0 = no, 1 = yes
 * @property {string} recaptcha         - 0 = no, 1 = yes
 * @property {string} event_ids         - Array of event ids
 *
 * @typedef {Object}  json.extra_input  - Array of extra input fields
 * @property {string} name              - Name of input field
 * @property {string} id                - Field id
 * @property {string} type              - Type of input field (password, text, select)
 * @property {string} value             - The default value of the input field
 * @property {string} required          - 0 = no, 1 = yes
 *
 * @typedef {Object}  json.ticket_types - Array of extra input fields
 * @property {array}  volume_price      - Array of volume prices
 */

(function() {

    const FE_TIMEOUT = 10000;

    const feSingleInput      = document.getElementById('fast-events-single-input');
    const feName             = document.getElementById('fast-events-name');
    const feMail             = document.getElementById('fast-events-email');
    const formatter =new Intl.NumberFormat(
        navigator.language, {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2
        });
    let fastEventsNumTickets = 0;
    let total                = 0.0;
    let couponLockCounter    = 0;
    let activeEventIds      = new Set();
    let currentEventId        = '';
    let discountPerEvent    = {};

    /**
     * Toggles UI state while a fetch is pending.
     *
     * @param {HTMLButtonElement}  feButton
     * @param {boolean}            active  true → show spinner, hide cursor, disable button
     */
    function setLoadingState(feButton, active) {
        document.body.style.cursor = active ? 'wait' : 'default';
        feButton.style.cursor = active ? 'wait' : 'default';
        feButton.disabled = active;
        feButton.style.opacity = active ? '0.6' : '1';
    }

    /**
     * Show detailed translated error message.
     *
     * @param {Object} feError Can be a JavaScript Error object or a WP_Error object.
     */
    function showDetailedError(feError) {
        if (feError instanceof Error) {
            console.error(feError.name + ': ' + feError.message);
            if (feError.name === 'TimeoutError') {
                alert(fePaymentData.timeout);
            } else if (feError.name === 'TypeError' && feError.message === 'Failed to fetch') {
                alert(fePaymentData.fetch_failed);
            } else if (feError.name === 'SyntaxError' && feError.message.startsWith('Unexpected token')) {
                alert(fePaymentData.invalid_json);
            } else {
                alert(feError.message);
            }
        } else {
            if ('code' in feError) {
                console.error(feError.code + ': ' + feError.message);
                if (feError.code === 'rest_no_route') {
                    alert(fePaymentData.no_route);
                } else if (feError.code === 'rest_forbidden') {
                    alert(fePaymentData.forbidden);
                } else {
                    alert(feError.message,);
                }
            } else {
                console.error(feError.message);
                alert(feError.message);
            }
        }
    }

    /**
     * Apply a coupon
     */
    let applyCoupon = async function (evt) {
        const eventId = evt.currentTarget.getAttribute('data-event-id');
        const couponCodeInput = document.getElementById('fast-events-coupon-code-' + eventId);
        if (!couponCodeInput.value) {
            return;
        }

        // Call the Payment API
        setLoadingState(this,true);
        try {
            let body = new FormData(document.getElementById('fast-events-form'));
            body.append('event_id', eventId);
            let response = await fetch(fePaymentData.endpoint + '/payment/coupon', {
                method: 'POST',
                signal: AbortSignal.timeout(FE_TIMEOUT),
                body: body
            })
            const json = await response.json()
            if (response.ok) {
                discountPerEvent[eventId.toString()] = json.amount;
                let couponAmountField = document.getElementById('fast-events-coupon-amount-' + eventId);
                couponAmountField.textContent = fePaymentData.currency + ' -' + formatter.format(discountPerEvent[eventId.toString()]);
                fastEventsEventCountTotal();
            } else {
                showDetailedError(json)
            }
        } catch (e) {
            showDetailedError(e)
        } finally {
            setLoadingState(this,false);
        }
    }


    /**
     * Delete coupon information
     */
    function removeCouponInfo(eventId) {
        const couponCodeInput = document.getElementById('fast-events-coupon-code-' + eventId);
        if (couponCodeInput) {
            couponCodeInput.value = '';
        }
        const couponCodeAmount = document.getElementById('fast-events-coupon-amount-' + eventId);
        if (couponCodeAmount) {
            couponCodeAmount.textContent= '';
        }
        discountPerEvent[eventId.toString()] = 0;
    }


    /**
     * Calculate the total amount
     */
    function fastEventsEventCountTotal() {
        fastEventsNumTickets = 0;
        total = 0.0;

        const firstKey = Object.keys(json)[0].toString();
        switch (json[firstKey].group_type) {
            case 0:
            case 3:
                currentEventId = firstKey
                document.querySelectorAll('.fast-events-event-select-event-' + firstKey).forEach(el=> {
                    fastEventsNumTickets += parseInt(el.value);
                    total += parseFloat(el.getAttribute('data-price')) * parseInt(el.value);
                })
                break;
            case 1:
                currentEventId = feSingleInput.value;
                if ( currentEventId !== '0' ) {
                    document.querySelectorAll('.fast-events-event-select-event-' + currentEventId).forEach(el=> {
                        fastEventsNumTickets += parseInt(el.value);
                        total += parseFloat(el.getAttribute('data-price')) * parseInt(el.value);
                    })
                }
                break;
            case 2:
                document.querySelectorAll('.fast-events-checkbox-group-type-2[type=checkbox]:checked').forEach(cb=> {
                    document.querySelectorAll('.fast-events-event-select-event-' + cb.value).forEach(el=> {
                        fastEventsNumTickets += parseInt(el.value);
                        total += parseFloat(el.getAttribute('data-price')) * parseInt(el.value);
                    })
                })
                break;
        }

        // Show total.
        const totalDiscountAmount = Object.values(discountPerEvent).reduce((a, b) => a + b, 0);
        document.querySelectorAll('.fast-events-order-total').forEach(el=> {
            el.innerHTML = fePaymentData.currency + ' ' + formatter.format(total - totalDiscountAmount);
        })
    }


    /**
     * Check field values
     */
    function fastEventsCheckFields( json, useTerms = true ) {
        const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        let   er = '';

        // Check for an empty name or email field.
        if ( !feName.value || !feMail.value ) {
            return fePaymentData.empty_field;
        }

        // Check for a valid emailaddress.
        if ( ! re.test( feMail.value ) ) {
            return fePaymentData.invalid_email;
        }

        // Check if tickets are ordered.
        if ( ! fastEventsNumTickets ) {
            return fePaymentData.no_tickets;
        }

        // Check if there are extra input fields and if the required ones have a value.
        if (activeEventIds.size !== 0) {
            activeEventIds.forEach((eventId) => {
                json[eventId.toString()].extra_input.forEach((item) => {
                    const inpField = document.getElementById('e' + eventId + item.id);
                    if (inpField) {
                        if (item.required === 1 && inpField.value === '') {
                            er = fePaymentData.empty_field;
                        }
                    }
                });
            });
            if (er) {
                return er;
            }
        }

        // If terms exist, check if they are checked.
        const tm = document.getElementById('fast-events-event-terms');
        if ( useTerms && tm && !tm.checked ) {
            return fePaymentData.check_terms;
        }

        return '';
    }








    // Parse JSON fields.
    const json          = JSON.parse( document.getElementById('event_info').value );
    const fePaymentData = JSON.parse( document.getElementById('event_translation').value );

    // Retrieve 'name' and 'email' values
    if ( 'fast_events_name' in localStorage ) {
        feName.value = localStorage.fast_events_name;
    }
    if ( 'fast_events_email' in localStorage ) {
        feMail.value = localStorage.fast_events_email;
    }

    // Retrieve other input fields.
    for ( let key of Object.keys(json)) {
        json[key.toString()].extra_input.forEach((item) => {
            const id = 'e' + key + item.id;
            const varName = document.getElementById(id);
            if (varName) {
                if (item.type.toLowerCase() !== 'password') { // Don't save password fields.
                    if ('fast_events_' + id in localStorage) {
                        varName.value = localStorage['fast_events_' + id];
                    }
                }
            }
        });
    }

    if (json[Object.keys(json)[0].toString()].group_type === 0 || json[Object.keys(json)[0].toString()].group_type === 3) {
        activeEventIds.add(Object.keys(json)[0]);
    }

    fastEventsEventCountTotal();

    // Capture events on group_type 1 (Single select)
    if (feSingleInput) {
        feSingleInput.onchange = function () {
            Object.keys(json).forEach(value => {
                if (feSingleInput.value === value.toString()) {
                    document.getElementById('fast-events-ticket-table-' + value.toString()).style.display = 'table';
                    activeEventIds.add(value);
                    fastEventsEventCountTotal();
                } else {
                    activeEventIds.delete(value);
                    removeCouponInfo(value);
                    document.getElementById('fast-events-ticket-table-' + value.toString()).style.display = 'none';
                }
            });
        }
    }

    // Capture events on group_type 2 (Multi select)
    (document.querySelectorAll('.fast-events-checkbox-group-type-2') ?? []).forEach(el => {
       el.onchange = function() {
           let tt = document.getElementById('fast-events-ticket-table-' + el.value);
           if (tt.style.display === 'none') {
               tt.style.display = 'table';
               activeEventIds.add(el.value);
           } else {
               tt.style.display = 'none';
               activeEventIds.delete(el.value);
               removeCouponInfo(el.value);
           }
           fastEventsEventCountTotal(el.value);
       }
    })

    // Capture changes of ticket selections and apply volume prices if applicable.
    document.querySelectorAll('.fast-events-event-tickets-select').forEach(el=> {
        el.onchange = function() {
            const ticketId = el.getAttribute('data-ticket-id');
            const eventId  = el.getAttribute('data-event-id');
            json[eventId].ticket_types.forEach(ticket => {
                 /**
                 * Ticket type definition
                 * @typedef {Object}  ticket       - Ticket object
                 * @property {string} price        - Price of ticket
                 * @property {array}  volume_price - Array of volume prices
                 */
                if (ticket.id === ticketId && ticket.hasOwnProperty('volume_price') && Array.isArray(ticket.volume_price) && ticket.volume_price.length) {
                    let price = parseFloat(ticket.price);
                    ticket.volume_price.sort((a, b) => parseInt(Object.keys(b)[0]) - parseInt(Object.keys(a)[0])); // Reverse sort
                    for (let key in ticket.volume_price) {
                        const qty = Object.keys(ticket.volume_price[key])[0];
                        if (parseInt(el.value) >= parseInt(qty)) {
                            price = ticket.volume_price[key][qty];
                            break;
                        }
                    }
                    el.setAttribute('data-price', price.toString());
                    el.parentElement.previousElementSibling.textContent = fePaymentData.currency + ' ' + formatter.format(price);
                }
            });
            fastEventsEventCountTotal();
        };
    });

    // Capture clicks on tickets with volume pricing and expand or collapse the content.
    [...document.getElementsByClassName('fast-events-collapsible')].map(el => {
        el.addEventListener('click', function() {
            this.classList.toggle('fast-events-active');
            let content = this.nextElementSibling;
            if (content.style.maxHeight){
                content.style.maxHeight = null;
            } else {
                content.style.maxHeight = content.scrollHeight + "px";
            }
        });
    });

    // Capture clicks on entering a coupon code.
    [...document.getElementsByClassName('fast-events-coupon')].map(el => {
        el.addEventListener('click', function() {
            const eventId = this.getAttribute('data-event-id');

            // Check name, email, input fields are provided, and tickets are selected. (group_type 0, 1 and 3)
            const fieldError = fastEventsCheckFields(json, false);
            if (fieldError !== '') {
                alert(fieldError);
                return;
            }

            // This is needed for group_type =2 (Multi-select events)
            let selectFields = document.querySelectorAll('select[data-event-id="' + eventId + '"]');
            let filledSelects = false;
            for (let idx = 0; idx< selectFields.length; idx++) {
                if (parseInt(selectFields[idx].value) > 0) {
                    filledSelects = true;
                }
            }
            if (! filledSelects) {
                alert(fePaymentData.no_tickets);
                return;
            }

            this.classList.toggle('fast-events-active');

            // Get the id of the coupon 'Apply' button.
            const couponApplyButton = document.getElementById('fast-events-coupon-apply-' + eventId);

            // If the 'fast-events-active' class is present, disable input.
            let sel = 'select[data-event-id="' + eventId + '"]' + ',input[data-event-id="' + eventId + '"]';
            if (this.classList.contains('fast-events-active')) {
                sel += ',.fast-events-common-field';
                couponLockCounter++;
                document.querySelectorAll(sel).forEach(el => {
                    if (el.tagName.toLowerCase() === 'input') {
                        el.readOnly = true;
                    } else {
                        [...el.options].map(o => o.disabled = !o.selected); // Disable select options
                    }
                });
                couponApplyButton.addEventListener('click', applyCoupon);
            } else {
                if (couponLockCounter === 1) {
                    sel += ',.fast-events-common-field';
                }
                couponLockCounter--;
                document.querySelectorAll(sel).forEach(el => {
                    if (el.tagName.toLowerCase() === 'input') {
                        el.readOnly = false;
                    } else {
                        [...el.options].map(o => o.disabled = false); // Enable all select options
                    }
                });

                // Remove the coupon handler and set any value to 0.
                couponApplyButton.removeEventListener('click', applyCoupon);
                removeCouponInfo(eventId);
                fastEventsEventCountTotal();
            }

            // Collapse or expand content
            let content = this.nextElementSibling;
            if (content.style.display !== 'none'){
                content.style.display = 'none';
            } else {
                content.style.display = 'block';
            }
        });
    });

    // Submit the user form
    const fe_form_submit_button = document.getElementById('fast-events-form-submit')
    fe_form_submit_button.onclick = async function (event) {
        event.preventDefault();

        // Check for errors.
        const fieldError = fastEventsCheckFields(json);
        if (fieldError !== '') {
            alert(fieldError);
            return;
        }

        document.getElementById('fast-events-event-info').innerHTML = ''; // Clear info field

        // Save the 'name' and 'email' value
        localStorage.fast_events_name = feName.value;
        localStorage.fast_events_email = feMail.value;

        // Save other input fields.
        for ( let key of Object.keys(json)) {
            json[key.toString()].extra_input.forEach((item) => {
                const id = 'e' + key + item.id;
                const varName = document.getElementById(id);
                if (varName) {
                    if (item.type.toLowerCase() !== 'password') { // Don't save password fields.
                        localStorage['fast_events_' + id] = varName.value;
                    }
                }
            });
        }

        // Call the Payment API
        setLoadingState(this,true);
        try {
            let response = await fetch(fePaymentData.endpoint + '/payment', {
                method: 'POST',
                signal: AbortSignal.timeout(FE_TIMEOUT),
                body: new FormData(document.getElementById('fast-events-form'))
            })
            const json = await response.json()
            if (response.ok) {
                window.location = json.redirect
            } else {
                showDetailedError(json)
            }
        } catch (e) {
            showDetailedError(e)
        } finally {
            setLoadingState(this,false);
        }
    }

})();