import axios from 'axios';
import { ready } from '@site/js/utils/dom.js';

// reCAPTCHA v3 public site key — shared between recaptchaInit and recaptchaUpdate.
const RECAPTCHA_SITE_KEY = '6LcoU44nAAAAABNyyxY2tb2-XEDWcO0nGRcC05_H';

/**
 * Project-wide front-end utilities.
 *
 * These functions ship as `window.*` globals because legacy code and templates
 * still reach for them by name (`submitForm('.foo')`, `matchesMediaQuery(...)`).
 * Keep the global surface and behaviour identical — only the implementation is
 * being switched from jquery to vanilla.
 */

/**
 * AJAX form handler. Binds once per call to `document` and processes any
 * submit whose target form matches `formSelector`. The server response is a
 * JSON object with `errors` (field-name → message map, or false) or
 * `success` (object describing redirect / success message / hide-form behaviour).
 *
 * Forwards `ajax=1` in the POST body so the legacy PHP handlers know to
 * return JSON.
 */
window.submitForm = function submitForm(formSelector) {

    document.addEventListener('submit', (event) => {
        const target = event.target instanceof Element ? event.target : null;
        const form = target?.closest(formSelector);
        if (!form || !form.matches(formSelector)) {
            return;
        }

        event.preventDefault();

        const action = form.getAttribute('action');
        const submitButton = form.querySelector('[type="submit"]');
        const formData = new FormData(form);
        formData.append('ajax', '1');

        // Lock the button + flag the form as submitting (matches the legacy class hooks).
        form.classList.add('submitting');
        if (submitButton) {
            submitButton.setAttribute('disabled', 'disabled');
        }

        // Remove any leftover success block from a previous submit.
        document.querySelectorAll('.successMessage').forEach((el) => el.remove());

        axios.post(action, formData)
            .then(({ data }) => {
                let response = data;
                if (typeof response !== 'object') {
                    try {
                        response = JSON.parse(response);
                    } catch (err) {
                        console.error('[submitForm] non-JSON response:', err.message);
                        return;
                    }
                }

                // Clear traces of any previous render so the form is fresh.
                form.querySelectorAll('.row').forEach((row) => row.classList.remove('errorWrap'));
                form.querySelectorAll('.validationMessage').forEach((el) => el.remove());
                form.querySelectorAll('.error').forEach((el) => el.remove());
                const prev = form.previousElementSibling;
                if (prev && prev.classList.contains('success')) {
                    prev.remove();
                }

                if (!response) {
                    return;
                }

                const hasErrors = typeof response.errors === 'object'
                    && response.errors !== null
                    && Object.keys(response.errors).length > 0;

                if (hasErrors) {
                    Object.entries(response.errors).forEach(([name, message]) => {
                        const errorField = form.querySelector(
                            `input[name="${name}"], textarea[name="${name}"], select[name="${name}"]`,
                        );
                        const errorFieldRow = errorField?.closest('.row');

                        if (errorField) {
                            errorFieldRow?.classList.add('errorWrap');

                            const clear = () => {
                                if ((errorField.value || '').trim().length > 0) {
                                    errorFieldRow?.classList.remove('errorWrap');
                                } else {
                                    errorFieldRow?.classList.add('errorWrap');
                                }
                            };
                            errorField.addEventListener('keyup', clear);
                            errorField.addEventListener('change', clear);

                            // Build the validation message via DOM APIs so the
                            // server-supplied string cannot inject markup.
                            const messageEl = document.createElement('div');
                            messageEl.className = 'validationMessage';
                            const messageSpan = document.createElement('span');
                            messageSpan.textContent = message;
                            messageEl.appendChild(messageSpan);

                            const inputWrap = errorField.parentElement;
                            const inButtonInline = errorField.closest('.buttonInline');

                            if (inButtonInline && inputWrap) {
                                // Legacy: `.inputWrap`'s next sibling is the button; insert after that.
                                const nextSibling = inputWrap.nextElementSibling;
                                if (nextSibling && nextSibling.tagName === 'BUTTON') {
                                    nextSibling.after(messageEl);
                                } else {
                                    inputWrap.after(messageEl);
                                }
                            } else if (inputWrap && inputWrap.classList.contains('inputWrap')) {
                                inputWrap.after(messageEl);
                            }
                        } else {
                            // No matched field — render as a top-of-form error list.
                            let errorList = form.querySelector('.error ul');
                            if (!errorList) {
                                const errorBox = document.createElement('div');
                                errorBox.className = 'error';
                                errorList = document.createElement('ul');
                                errorBox.appendChild(errorList);
                                form.prepend(errorBox);
                            }
                            const li = document.createElement('li');
                            li.textContent = message;
                            errorList.prepend(li);
                        }
                    });

                    // Scroll to the first error after a brief settle (matches the 200ms delay
                    // the legacy code used for the validation render to flush).
                    const firstError = form.querySelector('.errorWrap, .error');
                    if (firstError) {
                        setTimeout(() => {
                            const top = firstError.getBoundingClientRect().top + window.pageYOffset - 100;
                            window.scrollTo({ top, behavior: 'smooth' });
                        }, 200);
                    }

                    if (submitButton) {
                        submitButton.removeAttribute('disabled');
                    }

                    if (document.querySelector('.overlayBoxOuter')) {
                        window.dispatchEvent(new CustomEvent('resize.overlay'));
                    }
                    return;
                }

                const hasSuccess = typeof response.success === 'object'
                    && response.success !== null
                    && Object.keys(response.success).length > 0;

                if (hasSuccess) {
                    if (response.success.redirect) {
                        window.location.href = response.success.redirect;
                        return;
                    }

                    const successDiv = document.createElement('div');
                    successDiv.className = 'successMessage';
                    form.parentElement?.insertBefore(successDiv, form);

                    if (response.success.title) {
                        successDiv.insertAdjacentHTML('beforeend', response.success.title);
                    }
                    successDiv.insertAdjacentHTML(
                        'beforeend',
                        `<div class="message">${response.success.message}</div>`,
                    );

                    document.querySelectorAll('.removeAfterSubmit').forEach((el) => el.remove());

                    if (response.success.hide_form) {
                        // Mimic the legacy fadeOut — opacity fade then display:none.
                        form.style.transition = 'opacity 400ms';
                        form.style.opacity = '0';
                        setTimeout(() => {
                            form.style.display = 'none';
                        }, 400);
                    }

                    setTimeout(() => {
                        const top = successDiv.getBoundingClientRect().top + window.pageYOffset - 100;
                        window.scrollTo({ top, behavior: 'smooth' });
                    }, 200);

                    if (submitButton) {
                        submitButton.removeAttribute('disabled');
                    }

                    if (document.querySelector('.overlayBoxOuter')) {
                        window.dispatchEvent(new CustomEvent('resize.overlay'));
                    }
                }
            })
            .catch((err) => {
                const httpStatus = err.response?.status ?? 'network error';
                const message = err.response?.data?.message ?? err.message;
                console.error(`[submitForm] ${httpStatus}: ${message}`);
                if (submitButton) {
                    submitButton.removeAttribute('disabled');
                }
            })
            .finally(() => {
                form.classList.remove('submitting');
            });
    });
};

/**
 * Reads CSS-encoded breakpoints out of the `:before` pseudo-element on `html`.
 * The LESS theme writes them as `content: 'mobile:480px,tablet:768px,...'`.
 */
function getBreakpoint() {
    const style = window.getComputedStyle(document.documentElement, ':before');
    const raw = style.content.replace(/,\s+/g, ',').split(',');

    const breakpoints = {};
    raw.forEach((entry) => {
        const parts = entry.replace(': ', ':')
            .replace('px', '')
            .replace(/['"]/g, '')
            .split(':');
        if (parts.length === 2) {
            breakpoints[parts[0]] = parseInt(parts[1], 10);
        }
    });

    return breakpoints;
}

/**
 * Trailing-edge debounce. `timeout` defaults to 200ms.
 */
window.debouncer = function debouncer(func, timeout) {
    let timeoutID;
    const wait = timeout || 200;

    return function (...args) {
        clearTimeout(timeoutID);
        timeoutID = setTimeout(() => {
            func.apply(this, args);
        }, wait);
    };
};

/**
 * Adds a `.stickyFooter` class to the page wrapper above the mobile
 * breakpoint, and pads the page bottom by the footer's height so no
 * content is hidden behind it.
 */
window.stickyBottom = function stickyBottom() {
    const footer = document.querySelector('.footerWrap');
    const footerHeight = footer ? footer.offsetHeight : 0;

    if (window.matchesMediaQuery(0, window.breakpoints.mobile)) {
        document.querySelectorAll('.stickyFooter').forEach((el) => {
            el.style.paddingBottom = '0';
        });
        document.querySelectorAll('.siteWrapper').forEach((el) => {
            el.classList.remove('stickyFooter');
        });
    } else {
        document.querySelectorAll('.siteWrapper').forEach((el) => {
            el.classList.add('stickyFooter');
        });
        document.querySelectorAll('.stickyFooter').forEach((el) => {
            el.style.paddingBottom = `${footerHeight}px`;
        });
    }
};

/**
 * matchMedia wrapper. Either arg can be a number (in px) or a string key
 * that resolves against `window.breakpoints` — when a string `min` is used,
 * the value is incremented by 1 so a "tablet" min query matches widths
 * *strictly above* the named breakpoint.
 */
window.matchesMediaQuery = function matchesMediaQuery(minWidth, maxWidth) {
    if (!window.matchMedia) {
        return false;
    }

    let min = minWidth;
    let max = maxWidth;

    if (typeof min === 'string') {
        if (window.breakpoints && Object.prototype.hasOwnProperty.call(window.breakpoints, min)) {
            min = window.breakpoints[min] + 1;
        } else {
            return false;
        }
    }
    if (typeof max === 'string') {
        if (window.breakpoints && Object.prototype.hasOwnProperty.call(window.breakpoints, max)) {
            max = window.breakpoints[max];
        } else {
            return false;
        }
    }

    const minSet = !(min === undefined || min === 0);
    const maxSet = !(max === undefined || max === 0);

    if (maxSet && !minSet) {
        return window.matchMedia(`(max-width: ${max}px)`).matches;
    }
    if (!maxSet && minSet) {
        return window.matchMedia(`(min-width: ${min}px)`).matches;
    }
    if (maxSet && minSet) {
        return window.matchMedia(`(min-width: ${min}px) and (max-width: ${max}px)`).matches;
    }
    return false;
};

/**
 * Load the reCAPTCHA v3 script and execute the contact action. Gated by the
 * `hasRecaptcha` body class which the layout adds on pages that need it.
 */
window.recaptchaInit = function recaptchaInit() {
    const body = document.body;

    if (!body.classList.contains('recaptchaActive')) {
        document.querySelectorAll('.js_contactForm button[type="submit"], .newsletterForm button[type="submit"]')
            .forEach((btn) => { btn.disabled = true; });
    }

    const shouldLoad = (window.pageYOffset > 10 && !body.classList.contains('recaptchaActive'))
        || document.querySelector('.js_contactForm');

    if (!shouldLoad) {
        return;
    }

    body.classList.add('recaptchaActive');

    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.onload = function () {
        if (typeof grecaptcha === 'undefined') {
            return;
        }
        grecaptcha.ready(() => {
            const captcha1 = document.getElementById('recaptcha');
            const captcha2 = document.getElementById('recaptcha2');

            grecaptcha.execute(RECAPTCHA_SITE_KEY, { action: 'contact' }).then((token) => {
                if (token && (captcha1 || captcha2)) {
                    if (captcha1) captcha1.value = token;
                    if (captcha2) captcha2.value = token;
                }
            });
        });

        document.querySelectorAll('.js_contactForm button[type="submit"], .newsletterForm button[type="submit"]')
            .forEach((btn) => { btn.disabled = false; });
    };
    script.src = `https://www.google.com/recaptcha/api.js?render=${RECAPTCHA_SITE_KEY}`;
    document.head.appendChild(script);
};

/**
 * Re-issue a reCAPTCHA token (e.g. before form submit) and pass it to
 * `cb`. If recaptcha isn't active on the page, calls `cb(false)` immediately.
 */
window.recaptchaUpdate = function recaptchaUpdate(cb) {
    if (!document.body.classList.contains('recaptchaActive')) {
        cb(false);
        return;
    }

    if (typeof grecaptcha === 'undefined') {
        cb(false);
        return;
    }

    grecaptcha.ready(() => {
        const captcha1 = document.getElementById('recaptcha');
        const captcha2 = document.getElementById('recaptcha2');

        grecaptcha.execute(RECAPTCHA_SITE_KEY, { action: 'contact' }).then((token) => {
            if (token && (captcha1 || captcha2)) {
                if (captcha1) captcha1.value = token;
                if (captcha2) captcha2.value = token;
            }
            cb(token);
        });
    });
};

// Initialise `window.breakpoints` as early as possible. Vanilla modules read
// this during their own DOMContentLoaded callbacks, which can fire before
// ready() resolves (ready() waits for vue:mounted on Vue-bearing pages).
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
        if (typeof window.breakpoints === 'undefined') {
            window.breakpoints = getBreakpoint();
        }
    });
} else if (typeof window.breakpoints === 'undefined') {
    window.breakpoints = getBreakpoint();
}

ready(() => {
    // Initial window width snapshot. Resize handlers update it as needed.
    window.windowWidth = window.outerWidth;

    // Fallback: ensure breakpoints is populated by ready() time (covers the
    // case where the DOMContentLoaded listener above ran before computed
    // styles for ::before were ready).
    if (typeof window.breakpoints === 'undefined') {
        window.breakpoints = getBreakpoint();
    }

    if (document.body.classList.contains('hasRecaptcha')) {
        window.recaptchaInit();
    }
});
