document.addEventListener('DOMContentLoaded', () => {
    const i18n = window.CCW_I18N || {};
    const t = (value) => i18n[value] || value;
    const interpolate = (template, replacements = {}) => Object.entries(replacements).reduce((carry, [key, value]) => {
        return carry.split(`:${key}`).join(String(value));
    }, template);
    const monthNames = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December',
    ].map((month) => t(month));

    const formatYearMonthLabel = (value) => {
        if (!/^\d{4}-\d{2}$/.test(String(value))) {
            return value || '';
        }

        const [year, month] = String(value).split('-');
        const monthIndex = Number(month) - 1;
        const monthName = monthNames[monthIndex] || month;

        return `${monthName} ${year}`;
    };

    const closeAllMonthPickers = () => {
        document.querySelectorAll('[data-month-picker].is-open').forEach((openPicker) => {
            openPicker.classList.remove('is-open');
            const openPanel = openPicker.querySelector('[data-month-picker-panel]');
            if (openPanel) {
                openPanel.hidden = true;
            }
        });
    };

    document.querySelectorAll('[data-month-picker]').forEach((picker) => {
        const nativeInput = picker.querySelector('[data-month-picker-native]');
        const button = picker.querySelector('[data-month-picker-button]');
        const label = picker.querySelector('[data-month-picker-label]');
        const panel = picker.querySelector('[data-month-picker-panel]');
        const yearLabel = picker.querySelector('[data-month-picker-year-label]');
        const prevYearButton = picker.querySelector('[data-month-picker-prev-year]');
        const nextYearButton = picker.querySelector('[data-month-picker-next-year]');
        const monthButtons = Array.from(picker.querySelectorAll('[data-month-picker-month]'));

        if (!nativeInput || !button || !label || !panel || !yearLabel || !prevYearButton || !nextYearButton || monthButtons.length === 0) {
            return;
        }

        let viewYear = Number.parseInt(picker.dataset.monthPickerYear || nativeInput.value.slice(0, 4), 10);

        if (!Number.isFinite(viewYear)) {
            viewYear = new Date().getFullYear();
        }

        const syncLabel = () => {
            label.textContent = formatYearMonthLabel(nativeInput.value);
        };

        const syncPanel = () => {
            yearLabel.textContent = String(viewYear);

            monthButtons.forEach((monthButton) => {
                const monthValue = monthButton.dataset.monthPickerMonth || monthButton.getAttribute('data-month-picker-month') || '';
                const optionValue = `${viewYear}-${monthValue}`;
                const isSelected = nativeInput.value === optionValue;

                monthButton.classList.toggle('is-selected', isSelected);
                monthButton.setAttribute('aria-pressed', isSelected ? 'true' : 'false');
            });
        };

        const openPicker = () => {
            closeAllMonthPickers();
            picker.classList.add('is-open');
            panel.hidden = false;
            syncPanel();
        };

        const closePicker = () => {
            picker.classList.remove('is-open');
            panel.hidden = true;
        };

        button.addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();

            if (picker.classList.contains('is-open')) {
                closePicker();
                return;
            }

            openPicker();
        });

        prevYearButton.addEventListener('click', (event) => {
            event.preventDefault();
            viewYear -= 1;
            syncPanel();
        });

        nextYearButton.addEventListener('click', (event) => {
            event.preventDefault();
            viewYear += 1;
            syncPanel();
        });

        monthButtons.forEach((monthButton) => {
            monthButton.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();
                const monthValue = monthButton.dataset.monthPickerMonth || monthButton.getAttribute('data-month-picker-month') || '';

                if (!/^\d{2}$/.test(monthValue)) {
                    return;
                }

                nativeInput.value = `${viewYear}-${monthValue}`;
                nativeInput.dispatchEvent(new Event('change', { bubbles: true }));
                syncLabel();
                syncPanel();
                closePicker();
            });
        });

        nativeInput.addEventListener('change', () => {
            const matched = /^(\d{4})-(\d{2})$/.exec(nativeInput.value);
            if (matched) {
                viewYear = Number.parseInt(matched[1], 10);
            }

            syncLabel();
            syncPanel();
        });

        syncLabel();
        syncPanel();

        document.addEventListener('click', (event) => {
            if (!picker.contains(event.target)) {
                closePicker();
            }
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                closePicker();
            }
        });
    });

    document.querySelectorAll('[data-nav-report-group]').forEach((group) => {
        const toggle = group.querySelector('[data-nav-report-toggle]');
        const menu = group.querySelector('[data-nav-report-menu]');

        if (!toggle || !menu) {
            return;
        }

        if (document.body.classList.contains('display-app')) {
            document.body.appendChild(menu);
        }

        const setOpen = (isOpen) => {
            group.classList.toggle('is-open', isOpen);
            menu.classList.toggle('is-open', isOpen);
            toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
        };

        toggle.addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();
            setOpen(!group.classList.contains('is-open'));
        });

        menu.querySelectorAll('a').forEach((link) => {
            link.addEventListener('click', () => setOpen(false));
        });

        document.addEventListener('click', (event) => {
            if (!group.contains(event.target) && !menu.contains(event.target)) {
                setOpen(false);
                toggle.blur();
            }
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                setOpen(false);
                toggle.blur();
            }
        });
    });

    const appNotificationDock = document.querySelector('[data-app-notification-dock]');

    document.querySelectorAll('[data-notification-center]').forEach((center) => {
        const toggle = center.querySelector('[data-notification-toggle]');
        const panel = center.querySelector('[data-notification-panel]');

        if (!toggle || !panel) {
            return;
        }

        // Keep notification behavior in app mode consistent with web mode:
        // show the panel as a floating popup instead of docking it into content.
        const shouldDockInApp = () => false;

        const undockPanel = () => {
            if (panel.parentElement !== center) {
                center.appendChild(panel);
            }

            center.classList.remove('is-docked');

            if (appNotificationDock && !appNotificationDock.querySelector('[data-notification-panel]:not([hidden])')) {
                appNotificationDock.hidden = true;
            }
        };

        const dockPanel = () => {
            if (!shouldDockInApp()) {
                return;
            }

            if (appNotificationDock && panel.parentElement !== appNotificationDock) {
                appNotificationDock.hidden = false;
                appNotificationDock.appendChild(panel);
            }

            center.classList.add('is-docked');
        };

        const setOpen = (isOpen) => {
            if (isOpen) {
                dockPanel();
            } else {
                undockPanel();
            }

            center.classList.toggle('is-open', isOpen);
            panel.hidden = !isOpen;
            toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
        };

        toggle.addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();

            document.querySelectorAll('[data-notification-center].is-open').forEach((openCenter) => {
                if (openCenter !== center) {
                    const openToggle = openCenter.querySelector('[data-notification-toggle]');
                    const openPanel = openCenter.querySelector('[data-notification-panel]');
                    openCenter.classList.remove('is-open');
                    if (openPanel) {
                        openPanel.hidden = true;
                        if (openPanel.parentElement !== openCenter) {
                            openCenter.appendChild(openPanel);
                        }
                    }
                    if (openToggle) {
                        openToggle.setAttribute('aria-expanded', 'false');
                    }
                    openCenter.classList.remove('is-docked');
                }
            });

            setOpen(!center.classList.contains('is-open'));
        });

        panel.querySelectorAll('a').forEach((link) => {
            link.addEventListener('click', () => setOpen(false));
        });

        document.addEventListener('click', (event) => {
            if (!center.contains(event.target) && !panel.contains(event.target)) {
                setOpen(false);
            }
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                setOpen(false);
            }
        });
    });

    const appMenuToggle = document.querySelector('[data-app-menu-toggle]');
    const appMenuPanel = document.querySelector('[data-app-menu-panel]');
    const appMenuBackdrop = document.querySelector('[data-app-menu-backdrop]');
    const appMenuClose = document.querySelector('[data-app-menu-close]');

    if (appMenuToggle && appMenuPanel && appMenuBackdrop) {
        const setAppMenuOpen = (isOpen) => {
            appMenuPanel.hidden = !isOpen;
            appMenuBackdrop.hidden = !isOpen;
            appMenuPanel.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
            appMenuToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            document.body.classList.toggle('app-menu-open', isOpen);

            if (isOpen && appMenuClose) {
                appMenuClose.focus({ preventScroll: true });
            } else if (!isOpen) {
                appMenuToggle.blur();
            }
        };

        appMenuToggle.addEventListener('click', () => {
            setAppMenuOpen(appMenuPanel.hidden);
        });

        appMenuBackdrop.addEventListener('click', () => setAppMenuOpen(false));

        if (appMenuClose) {
            appMenuClose.addEventListener('click', () => setAppMenuOpen(false));
        }

        appMenuPanel.querySelectorAll('a').forEach((link) => {
            link.addEventListener('click', () => setAppMenuOpen(false));
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape' && !appMenuPanel.hidden) {
                setAppMenuOpen(false);
            }
        });
    }

    const dashboardConsole = document.querySelector('[data-dashboard-console]');
    const dashboardSidebarToggle = document.querySelector('[data-dashboard-sidebar-toggle]');
    const dashboardSidebarClose = document.querySelector('[data-dashboard-sidebar-close]');
    const dashboardSidebarBackdrop = document.querySelector('[data-dashboard-sidebar-backdrop]');

    if (dashboardConsole && dashboardSidebarToggle) {
        const desktopSidebarQuery = window.matchMedia('(min-width: 901px)');
        const storageKey = 'ccw_dashboard_sidebar_collapsed';

        const setDesktopCollapsed = (isCollapsed) => {
            dashboardConsole.classList.toggle('is-collapsed', isCollapsed);
            dashboardSidebarToggle.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true');

            try {
                window.localStorage.setItem(storageKey, isCollapsed ? '1' : '0');
            } catch (error) {
                // Ignore storage errors in private mode or restricted browsers.
            }
        };

        const setMobileOpen = (isOpen) => {
            dashboardConsole.classList.toggle('is-sidebar-open', isOpen);
            if (dashboardSidebarBackdrop) {
                dashboardSidebarBackdrop.hidden = !isOpen;
            }
            dashboardSidebarToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            document.body.classList.toggle('dashboard-sidebar-open', isOpen);
        };

        const syncDashboardSidebarMode = () => {
            if (desktopSidebarQuery.matches) {
                setMobileOpen(false);

                let isCollapsed = false;
                try {
                    isCollapsed = window.localStorage.getItem(storageKey) === '1';
                } catch (error) {
                    isCollapsed = false;
                }
                dashboardConsole.classList.toggle('is-collapsed', isCollapsed);
                dashboardSidebarToggle.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true');
                return;
            }

            dashboardConsole.classList.remove('is-collapsed');
            dashboardSidebarToggle.setAttribute(
                'aria-expanded',
                dashboardConsole.classList.contains('is-sidebar-open') ? 'true' : 'false'
            );
        };

        dashboardSidebarToggle.addEventListener('click', () => {
            if (desktopSidebarQuery.matches) {
                setDesktopCollapsed(!dashboardConsole.classList.contains('is-collapsed'));
                return;
            }

            setMobileOpen(!dashboardConsole.classList.contains('is-sidebar-open'));
        });

        if (dashboardSidebarClose) {
            dashboardSidebarClose.addEventListener('click', () => setMobileOpen(false));
        }

        if (dashboardSidebarBackdrop) {
            dashboardSidebarBackdrop.addEventListener('click', () => setMobileOpen(false));
        }

        desktopSidebarQuery.addEventListener('change', () => {
            syncDashboardSidebarMode();
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape' && dashboardConsole.classList.contains('is-sidebar-open')) {
                setMobileOpen(false);
            }
        });

        syncDashboardSidebarMode();
    }

    const syncLinkedGroupPanels = (form) => {
        form.querySelectorAll('[data-main-service-flow]').forEach((flow) => {
            const mainServiceCheckbox = flow.querySelector('summary [data-service-option]');
            const groupInputs = Array.from(flow.querySelectorAll('[data-main-service-group]'));
            const groupPanels = Array.from(flow.querySelectorAll('[data-group-sub-services]'));

            if (groupInputs.length === 0 || groupPanels.length === 0) {
                return;
            }

            const mainServiceChecked = !!mainServiceCheckbox?.checked;

            if (!mainServiceChecked) {
                groupInputs.forEach((input) => {
                    input.checked = false;
                });
            }

            const selectedGroup = mainServiceChecked
                ? (groupInputs.find((input) => input.checked)?.value || '')
                : '';

            groupPanels.forEach((panel) => {
                const isMatch = panel.dataset.groupId === selectedGroup;
                panel.hidden = !isMatch;

                if (isMatch) {
                    return;
                }

                panel.querySelectorAll('[data-service-option]').forEach((checkbox) => {
                    checkbox.checked = false;
                });

                panel.querySelectorAll('[data-package-usage]').forEach((select) => {
                    select.value = '';
                    select.disabled = true;
                });

                panel.querySelectorAll('[data-film-input]').forEach((input) => {
                    input.value = '';
                    input.disabled = true;
                });
            });
        });
    };

    const syncMainServiceFlows = (form) => {
        form.querySelectorAll('[data-main-service-flow]').forEach((flow) => {
            const mainServiceCheckbox = flow.querySelector('summary [data-service-option]');
            const selectedGroupInput = flow.querySelector('[data-main-service-group]:checked');
            const hasCheckedChild = Array.from(flow.querySelectorAll('[data-service-option]')).some((input) => input !== mainServiceCheckbox && input.checked);
            const shouldOpen = !!mainServiceCheckbox?.checked || !!selectedGroupInput || hasCheckedChild;

            flow.open = shouldOpen;
        });
    };

    const syncWindowFilmPanels = (form) => {
        let activePanel = null;

        form.querySelectorAll('[data-main-service-flow]').forEach((flow) => {
            const mainServiceCheckbox = flow.querySelector('summary [data-service-option]');
            const selectedGroupInput = Array.from(flow.querySelectorAll('[data-main-service-group]')).find((input) => input.checked);
            const selectedGroupId = selectedGroupInput?.value || '';
            const shouldShowPanel = !!selectedGroupInput
                && selectedGroupInput.dataset.showWindowFilmDetails === '1'
                && !!mainServiceCheckbox?.checked;

            flow.querySelectorAll('[data-film-options-panel]').forEach((panel) => {
                const isMatch = shouldShowPanel && activePanel === null && panel.dataset.groupId === selectedGroupId;
                panel.hidden = !isMatch;

                panel.querySelectorAll('[data-film-input]').forEach((input) => {
                    input.disabled = !isMatch;

                    if (isMatch) {
                        return;
                    }

                    const tagName = input.tagName;
                    if (tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'TEXTAREA') {
                        input.value = '';
                    }
                });

                if (isMatch) {
                    activePanel = panel;
                }
            });
        });
    };

    const syncSelectOptionsByCategory = (select, categoryId) => {
        if (!select) {
            return;
        }

        const currentValue = select.value;

        Array.from(select.options).forEach((option, index) => {
            if (index === 0) {
                option.hidden = false;
                option.disabled = false;
                return;
            }

            const optionCategoryId = option.dataset.categoryId || '';
            const isVisible = categoryId === '' || optionCategoryId === categoryId;
            option.hidden = !isVisible;
            option.disabled = !isVisible;
        });

        if (currentValue !== '' && !Array.from(select.options).some((option) => option.value === currentValue && !option.disabled)) {
            select.value = '';
        }
    };

    const syncGroupOptions = (select, categoryId, mainServiceId) => {
        if (!select) {
            return;
        }

        const currentValue = select.value;

        Array.from(select.options).forEach((option, index) => {
            if (index === 0) {
                option.hidden = false;
                option.disabled = false;
                return;
            }

            const optionCategoryId = option.dataset.categoryId || '';
            const optionMainServiceId = option.dataset.mainServiceId || '';
            const categoryMatches = categoryId === '' || optionCategoryId === categoryId;
            const mainMatches = mainServiceId === '' || optionMainServiceId === mainServiceId;
            const isVisible = categoryMatches && mainMatches;

            option.hidden = !isVisible;
            option.disabled = !isVisible;
        });

        if (currentValue !== '' && !Array.from(select.options).some((option) => option.value === currentValue && !option.disabled)) {
            select.value = '';
        }
    };

    const syncServiceStructureForm = (form) => {
        const typeSelect = form.querySelector('[data-service-type-select]');
        const categorySelect = form.querySelector('[data-service-category-select]');
        const mainServiceField = form.querySelector('[data-main-service-field]');
        const mainServiceSelect = form.querySelector('[data-service-main-select]');
        const groupField = form.querySelector('[data-group-field]');
        const groupSelect = form.querySelector('[data-service-group-select]');
        const packageBuilder = form.querySelector('[data-package-builder]');
        const quickServiceField = form.querySelector('[data-quick-service-field]');
        const quickServiceInput = quickServiceField ? quickServiceField.querySelector('input[name="is_quick_service"]') : null;

        if (!typeSelect || !categorySelect) {
            return;
        }

        const selectedCategoryId = categorySelect.value;
        const isSubService = typeSelect.value === 'sub_service';
        const isPackage = typeSelect.value === 'package';

        syncSelectOptionsByCategory(mainServiceSelect, selectedCategoryId);
        syncGroupOptions(groupSelect, selectedCategoryId, mainServiceSelect ? mainServiceSelect.value : '');

        if (mainServiceField) {
            mainServiceField.hidden = !isSubService;
        }

        if (mainServiceSelect) {
            mainServiceSelect.disabled = !isSubService;
            mainServiceSelect.required = isSubService;

            if (!isSubService) {
                mainServiceSelect.value = '';
            }
        }

        if (groupField) {
            groupField.hidden = !isSubService;
        }

        if (groupSelect) {
            groupSelect.disabled = !isSubService;

            if (!isSubService) {
                groupSelect.value = '';
            }
        }

        if (packageBuilder) {
            packageBuilder.hidden = !isPackage;
            packageBuilder.querySelectorAll('input, select').forEach((input) => {
                input.disabled = !isPackage;
            });
        }

        if (quickServiceField) {
            quickServiceField.hidden = isPackage;
        }

        if (quickServiceInput) {
            quickServiceInput.disabled = isPackage;

            if (isPackage) {
                quickServiceInput.checked = false;
            }
        }
    };

    const syncServiceGroupForm = (form) => {
        const categorySelect = form.querySelector('[data-service-category-select]');
        const mainServiceSelect = form.querySelector('[data-service-main-select]');

        if (!categorySelect || !mainServiceSelect) {
            return;
        }

        syncSelectOptionsByCategory(mainServiceSelect, categorySelect.value);
    };

    const bookingForm = document.querySelector('[data-booking-form]');

    if (bookingForm) {
        const isQuickServiceForm = bookingForm.dataset.quickServiceForm === '1';
        const checkboxes = Array.from(bookingForm.querySelectorAll('[data-service-option]'));
        const countNode = bookingForm.querySelector('[data-selected-count]');
        const totalNode = bookingForm.querySelector('[data-selected-total]');
        const packageDiscountNode = bookingForm.querySelector('[data-selected-package-discount]');
        const totalPriceInput = bookingForm.querySelector('[data-total-price-input]');
        const packageUsageContainers = Array.from(bookingForm.querySelectorAll('[data-package-usage-container]'));
        const packageUsageSelects = Array.from(bookingForm.querySelectorAll('[data-package-usage]'));
        const mainServiceGroupInputs = Array.from(bookingForm.querySelectorAll('[data-main-service-group]'));
        const packageSummaryCard = bookingForm.querySelector('[data-package-summary-card]');
        const packageSummary = bookingForm.querySelector('[data-package-summary]');
        const lookupButton = bookingForm.querySelector('[data-customer-lookup]');
        const newCustomerButton = bookingForm.querySelector('[data-new-customer-trigger]');
        const lookupFeedback = bookingForm.querySelector('[data-lookup-feedback]');
        const lookupResults = bookingForm.querySelector('[data-lookup-results]');
        const bookingFlow = bookingForm.querySelector('[data-booking-flow]');
        const bookingLookupCard = bookingForm.querySelector('.booking-lookup-card');
        const lookupPhoneField = bookingForm.querySelector('#lookup_phone');
        const paymentMethodSelect = bookingForm.querySelector('#payment_method');
        const receivedAmountInput = bookingForm.querySelector('[data-received-amount-input]');
        const paidBeforeNode = bookingForm.querySelector('[data-paid-before]');
        const balanceDueNode = bookingForm.querySelector('[data-balance-due]');
        const balanceDueCard = bookingForm.querySelector('[data-balance-due-card]');
        const recognizedPaymentNode = bookingForm.querySelector('[data-recognized-payment]');
        const changeDueNode = bookingForm.querySelector('[data-change-due]');
        const changeField = bookingForm.querySelector('[data-change-field]');
        const paySubmitButton = bookingForm.querySelector('[data-pay-submit]');
        const paymentValidationMessage = bookingForm.querySelector('[data-payment-validation-message]');
        const bookingTotalValidationMessage = bookingForm.querySelector('[data-booking-total-validation-message]');
        const qrPanel = bookingForm.querySelector('[data-quick-qr-panel]');
        const qrImage = bookingForm.querySelector('[data-quick-qr-image]');
        const qrHint = bookingForm.querySelector('[data-quick-qr-hint]');
        const qrDownload = bookingForm.querySelector('[data-quick-qr-download]');
        const slipField = bookingForm.querySelector('[data-payment-slip-field]');
        const slipInput = bookingForm.querySelector('[data-payment-slip-input]');
        const branchNameInput = bookingForm.querySelector('#branch_name');
        const qrScope = bookingForm.dataset.qrScope || 'quick_service';
        const positiveTotalMessage = bookingForm.dataset.positiveTotalMessage || t('Booking total must be greater than 0 before opening the bill.');
        const depositSlipGroups = Array.from(bookingForm.querySelectorAll('[data-deposit-slip-group]')).map((group) => {
            const key = group.dataset.depositSlipGroup || '';
            return {
                key,
                group,
                amountInput: bookingForm.querySelector(`#${key}_deposit_amount`),
                methodSelect: bookingForm.querySelector(`[data-deposit-method-select][data-deposit-slip-target="${key}"]`),
                slipInput: group.querySelector('[data-deposit-slip-input]'),
            };
        });
        const depositQrGroups = ['admin', 'branch'].map((key) => ({
            key,
            panel: bookingForm.querySelector(`[data-deposit-qr-panel="${key}"]`),
            hint: bookingForm.querySelector(`[data-deposit-qr-hint="${key}"]`),
            image: bookingForm.querySelector(`[data-deposit-qr-image="${key}"]`),
            download: bookingForm.querySelector(`[data-deposit-qr-download="${key}"]`),
            amountInput: bookingForm.querySelector(`#${key}_deposit_amount`),
            methodSelect: bookingForm.querySelector(`[data-deposit-method-select][data-deposit-slip-target="${key}"]`),
            scope: key === 'admin' ? 'booking_deposit_admin' : 'booking_deposit_branch',
        }));
        const qrAvailable = bookingForm.dataset.qrAvailable === '1';
        const qrEndpoint = bookingForm.dataset.qrEndpoint || '';
        const supportsHoldAction = !!bookingForm.querySelector('[name="submit_action"][value="hold"]');
        const quickGuestExtraFields = Array.from(bookingForm.querySelectorAll('[data-quick-guest-extra]'));
        const fields = {
            customerId: bookingForm.querySelector('#customer_id'),
            carId: bookingForm.querySelector('#car_id'),
            customerMode: bookingForm.querySelector('#customer_mode'),
            customerName: bookingForm.querySelector('#customer_name'),
            phone: bookingForm.querySelector('#phone'),
            plate: bookingForm.querySelector('#plate_number'),
            province: bookingForm.querySelector('#province'),
            vehicleType: bookingForm.querySelector('#vehicle_type'),
            brand: bookingForm.querySelector('#brand'),
            model: bookingForm.querySelector('#model'),
            color: bookingForm.querySelector('#color'),
        };
        const defaultProvinceValue = fields.province?.dataset.defaultValue || '';
        let totalPriceEdited = totalPriceInput
            ? (isQuickServiceForm ? totalPriceInput.dataset.manual === '1' : totalPriceInput.value.trim() !== '')
            : false;

        const formatMoney = (value) => Number(value || 0).toLocaleString('en-US', {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2,
        });

        const moneyToCents = (value) => {
            const numericValue = typeof value === 'number'
                ? value
                : Number(String(value ?? '').replace(/,/g, '').trim() || 0);

            if (!Number.isFinite(numericValue)) {
                return 0;
            }

            return Math.max(0, Math.round(numericValue * 100));
        };

        const centsToMoney = (value) => Math.max(0, Number(value || 0)) / 100;
        const quickServicePaidBeforeCents = moneyToCents(bookingForm.dataset.paidBefore || 0);
        const quickServicePaidBefore = centsToMoney(quickServicePaidBeforeCents);
        const methodsWithSlipSupport = ['transfer', 'qr', 'card'];
        const getActiveQrBranch = () => {
            if (branchNameInput instanceof HTMLInputElement || branchNameInput instanceof HTMLSelectElement) {
                return branchNameInput.value.trim();
            }

            return (bookingForm.dataset.qrBranch || '').trim();
        };
        const buildQrQueryString = (amount, scope) => {
            const params = new URLSearchParams();

            if (Number.isFinite(amount) && amount > 0) {
                params.set('amount', amount.toFixed(2));
            }

            if (scope) {
                params.set('scope', scope);
            }

            const activeBranch = getActiveQrBranch();
            if (activeBranch !== '') {
                params.set('branch', activeBranch);
            }

            return params.toString();
        };

        const normalizeMoneyInput = (input) => {
            if (!(input instanceof HTMLInputElement)) {
                return;
            }

            const trimmedValue = input.value.trim();
            if (trimmedValue === '') {
                return;
            }

            input.value = centsToMoney(moneyToCents(trimmedValue)).toFixed(2);
        };

        const protectNumberInputFromWheel = (input) => {
            if (!(input instanceof HTMLInputElement) || input.type !== 'number') {
                return;
            }

            input.addEventListener('wheel', (event) => {
                if (document.activeElement !== input) {
                    return;
                }

                event.preventDefault();
                input.blur();
            }, { passive: false });
        };

        const quickServicePaymentState = () => {
            const billTotalCents = moneyToCents(totalPriceInput ? totalPriceInput.value || 0 : 0);
            const balanceDueCents = Math.max(0, billTotalCents - quickServicePaidBeforeCents);
            const receivedAmountCents = moneyToCents(receivedAmountInput ? receivedAmountInput.value || 0 : 0);
            const billTotal = centsToMoney(billTotalCents);
            const balanceDue = centsToMoney(balanceDueCents);
            const receivedAmount = centsToMoney(receivedAmountCents);
            const isCashPayment = !!paymentMethodSelect && paymentMethodSelect.value === 'cash';
            const isUnderpaid = balanceDueCents > 0 && receivedAmountCents < balanceDueCents;
            const isOverpaid = receivedAmountCents > balanceDueCents;
            const recognizedPaymentCents = Math.min(receivedAmountCents, balanceDueCents);
            const recognizedPayment = centsToMoney(recognizedPaymentCents);
            const changeDue = isCashPayment
                ? centsToMoney(Math.max(0, receivedAmountCents - balanceDueCents))
                : 0;
            const hasPaymentInput = receivedAmountCents > 0;
            const allowPayment = balanceDueCents <= 0 || (hasPaymentInput && (!isOverpaid || isCashPayment));
            const willCloseBill = balanceDueCents <= 0 || receivedAmountCents >= balanceDueCents;

            return {
                billTotal,
                balanceDue,
                receivedAmount,
                isCashPayment,
                isUnderpaid,
                isOverpaid,
                recognizedPayment,
                changeDue,
                hasPaymentInput,
                allowPayment,
                willCloseBill,
            };
        };

        const validateBookingTotal = () => {
            if (isQuickServiceForm || !totalPriceInput) {
                return true;
            }

            const totalCents = moneyToCents(totalPriceInput.value || 0);
            const isValid = totalCents > 0;

            totalPriceInput.setCustomValidity(isValid ? '' : positiveTotalMessage);
            totalPriceInput.classList.toggle('is-danger', !isValid);

            if (bookingTotalValidationMessage) {
                bookingTotalValidationMessage.hidden = isValid;
                bookingTotalValidationMessage.textContent = isValid ? '' : positiveTotalMessage;
                bookingTotalValidationMessage.classList.toggle('is-danger', !isValid);
            }

            return isValid;
        };

        const updatePaymentSlipField = () => {
            if (!slipField || !paymentMethodSelect) {
                return;
            }

            const supportsSlip = methodsWithSlipSupport.includes(paymentMethodSelect.value);
            slipField.hidden = !supportsSlip;

            if (!supportsSlip && slipInput instanceof HTMLInputElement) {
                slipInput.value = '';
            }
        };

        const updateDepositSlipFields = () => {
            if (depositSlipGroups.length === 0) {
                return;
            }

            depositSlipGroups.forEach((item) => {
                if (!item.group || !item.methodSelect) {
                    return;
                }

                const amountValue = item.amountInput instanceof HTMLInputElement
                    ? Number(String(item.amountInput.value || '').replace(/,/g, '').trim() || 0)
                    : 0;
                const hasAmount = Number.isFinite(amountValue) && amountValue > 0;
                const supportsSlip = methodsWithSlipSupport.includes(item.methodSelect.value);
                const shouldShow = hasAmount && supportsSlip;

                item.group.hidden = !shouldShow;

                if (!shouldShow && item.slipInput instanceof HTMLInputElement) {
                    item.slipInput.value = '';
                }
            });
        };

        const updateDepositQrPanels = () => {
            if (depositQrGroups.length === 0) {
                return;
            }

            depositQrGroups.forEach((item) => {
                if (!item.panel || !item.methodSelect || !item.image || !item.hint) {
                    return;
                }

                const amountValue = item.amountInput instanceof HTMLInputElement
                    ? Number(String(item.amountInput.value || '').replace(/,/g, '').trim() || 0)
                    : 0;
                const shouldShow = item.methodSelect.value === 'qr' && Number.isFinite(amountValue) && amountValue > 0;

                item.panel.hidden = !shouldShow;

                if (!shouldShow) {
                    item.image.removeAttribute('src');
                    item.hint.textContent = t('Scan to receive payment.');
                    if (item.download) {
                        item.download.hidden = true;
                        item.download.removeAttribute('href');
                    }
                    return;
                }

                const qrAmount = amountValue;
                const queryString = buildQrQueryString(qrAmount, item.scope);

                if (qrEndpoint !== '') {
                    const qrUrl = `${qrEndpoint}?${queryString}`;
                    item.image.src = qrUrl;
                    if (item.download) {
                        item.download.href = `${qrEndpoint}?${queryString}&download=1`;
                        item.download.hidden = !qrAvailable;
                    }
                } else {
                    item.image.removeAttribute('src');
                    if (item.download) {
                        item.download.hidden = true;
                        item.download.removeAttribute('href');
                    }
                }

                if (!qrAvailable) {
                    item.hint.textContent = t('QR preview placeholder: set PromptPay ID or QR image in config to show the real code.');
                    if (item.download) {
                        item.download.hidden = true;
                    }
                    return;
                }

                item.hint.textContent = interpolate(t('Scan QR for :amount THB.'), { amount: formatMoney(qrAmount) });
            });
        };

        const updateQuickServiceQrPreview = (balanceDue, receivedAmount) => {
            if (!isQuickServiceForm || !qrPanel || !qrImage || !qrHint || !paymentMethodSelect) {
                return;
            }

            const shouldShowQr = paymentMethodSelect.value === 'qr';
            qrPanel.hidden = !shouldShowQr;

            if (!shouldShowQr) {
                qrImage.removeAttribute('src');
                qrHint.textContent = t('Scan to receive payment.');
                if (qrDownload) {
                    qrDownload.hidden = true;
                    qrDownload.removeAttribute('href');
                }
                return;
            }

            const qrAmount = receivedAmount > 0
                ? Math.min(receivedAmount, balanceDue > 0 ? balanceDue : receivedAmount)
                : balanceDue;
            const queryString = buildQrQueryString(qrAmount, qrScope);

            if (qrEndpoint !== '') {
                const qrUrl = queryString !== '' ? `${qrEndpoint}?${queryString}` : qrEndpoint;
                qrImage.src = qrUrl;
                if (qrDownload) {
                    qrDownload.href = queryString !== ''
                        ? `${qrEndpoint}?${queryString}&download=1`
                        : `${qrEndpoint}?download=1`;
                    qrDownload.hidden = false;
                }
            } else {
                qrImage.removeAttribute('src');
                if (qrDownload) {
                    qrDownload.hidden = true;
                    qrDownload.removeAttribute('href');
                }
            }

            if (!qrAvailable) {
                if (qrDownload) {
                    qrDownload.hidden = true;
                    qrDownload.removeAttribute('href');
                }
                qrHint.textContent = t('QR preview placeholder: set PromptPay ID or QR image in config to show the real code.');
                return;
            }

            qrHint.textContent = qrAmount > 0
                ? interpolate(t('Scan QR for :amount THB.'), { amount: formatMoney(qrAmount) })
                : t('Scan QR to receive payment.');
        };

        const setLookupFeedback = (message, state = '') => {
            if (!lookupFeedback) {
                return;
            }

            lookupFeedback.textContent = message;
            lookupFeedback.dataset.state = state;
        };

        const clearLookupResults = () => {
            if (!lookupResults) {
                return;
            }

            lookupResults.innerHTML = '';
            lookupResults.hidden = true;
        };

        const setBookingFlowVisibility = (visible) => {
            if (!bookingFlow) {
                return;
            }

            bookingFlow.hidden = !visible;

            if (!isQuickServiceForm && bookingLookupCard) {
                bookingLookupCard.hidden = visible;
                bookingLookupCard.querySelectorAll('input, select, textarea, button, a').forEach((element) => {
                    if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLButtonElement) {
                        element.disabled = visible;
                    }

                    if (element instanceof HTMLAnchorElement) {
                        if (visible) {
                            element.setAttribute('aria-hidden', 'true');
                            element.tabIndex = -1;
                        } else {
                            element.removeAttribute('aria-hidden');
                            element.removeAttribute('tabindex');
                        }
                    }
                });
            }
        };

        const setInputValue = (field, value) => {
            if (!field || value === undefined || value === null) {
                return;
            }

            field.value = String(value);
        };

        const setSelectValue = (field, value) => {
            if (!field || value === undefined || value === null) {
                return;
            }

            if (!('options' in field)) {
                field.value = String(value);
                return;
            }

            const nextValue = String(value);
            const optionLabel = field.id === 'province' ? t(nextValue) : nextValue;

            if (nextValue !== '' && !Array.from(field.options).some((option) => option.value === nextValue)) {
                const option = document.createElement('option');
                option.value = nextValue;
                option.textContent = optionLabel;
                field.append(option);
            }

            field.value = nextValue;
        };

        const syncQuickServiceCustomerFields = () => {
            if (!isQuickServiceForm) {
                return;
            }

            const mode = fields.customerMode?.value || '';
            const isGuest = mode === 'guest';
            const hasSelection = mode !== '';

            quickGuestExtraFields.forEach((wrapper) => {
                wrapper.hidden = isGuest;
                wrapper.querySelectorAll('input, select, textarea').forEach((input) => {
                    input.disabled = isGuest;
                });
            });

            [fields.phone, fields.province, fields.brand, fields.model, fields.color]
                .filter(Boolean)
                .forEach((field) => {
                    field.readOnly = true;
                });

            if (fields.customerName) {
                fields.customerName.readOnly = !isGuest;
            }

            if (fields.plate) {
                fields.plate.readOnly = mode === 'matched';
                fields.plate.required = hasSelection;
            }
        };

        const resetCustomerRecord = () => {
            setInputValue(fields.customerId, '');
            setInputValue(fields.carId, '');
            setInputValue(fields.customerMode, '');
            setInputValue(fields.customerName, '');
            setInputValue(fields.phone, '');
            setInputValue(fields.plate, '');
            setSelectValue(fields.province, defaultProvinceValue);
            setSelectValue(fields.vehicleType, '');
            setSelectValue(fields.brand, '');
            setInputValue(fields.model, '');
            setInputValue(fields.color, '');
            syncQuickServiceCustomerFields();
            renderPackageData([], false);
            updateSummary();
        };

        const startNewCustomerRegistration = (event) => {
            if (event) {
                event.preventDefault();
            }

            clearLookupResults();
            resetCustomerRecord();

            if (isQuickServiceForm) {
                const lookupQuery = lookupPhoneField?.value.trim() || '';
                const lookupKind = lookupQuery === ''
                    ? ''
                    : (/^[\d\s\-\+\(\)]+$/.test(lookupQuery) && lookupQuery.replace(/\D+/g, '').length >= 8 ? 'phone' : 'plate');
                setInputValue(fields.customerMode, 'guest');
                setInputValue(fields.customerName, t('Guest'));
                if (lookupKind === 'plate') {
                    setInputValue(fields.plate, lookupQuery);
                }
                setBookingFlowVisibility(true);
                setLookupFeedback(t('Enter the plate number for the guest customer to continue.'), 'warning');
                syncQuickServiceCustomerFields();
                if (lookupKind === 'plate') {
                    fields.customerName?.focus();
                } else {
                    fields.plate?.focus();
                }
                updateSummary();
                return;
            }

            setBookingFlowVisibility(true);
            setLookupFeedback(t('Enter the new customer and vehicle details below.'), '');
            fields.customerName?.focus();
        };

        const renderPackageData = (packageRows, hasCustomer = false) => {
            const packageScopeNote = (row) => {
                if ((row.vehicle_scope || 'member') !== 'car') {
                    return t('All member vehicles');
                }

                return row.plate_number
                    ? interpolate(t('Only vehicle :plate'), { plate: row.plate_number })
                    : t('Only purchasing vehicle');
            };

            const groupedRows = new Map();

            packageRows.forEach((row) => {
                const serviceId = Number(row.sub_service_id || 0);

                if (!serviceId) {
                    return;
                }

                if (!groupedRows.has(serviceId)) {
                    groupedRows.set(serviceId, []);
                }

                groupedRows.get(serviceId).push(row);
            });

            packageUsageContainers.forEach((container) => {
                const serviceId = Number(container.dataset.serviceId || 0);
                const select = container.querySelector('[data-package-usage]');
                const previousValue = select ? select.value : '';
                const rows = groupedRows.get(serviceId) || [];

                if (!select) {
                    return;
                }

                select.innerHTML = '';

                const defaultOption = document.createElement('option');
                defaultOption.value = '';
                defaultOption.textContent = t('Charge normal price');
                select.append(defaultOption);

                rows.forEach((row) => {
                    const option = document.createElement('option');
                    option.value = String(row.customer_package_item_id || '');
                    option.textContent = `${row.package_name_snapshot || t('Package')} / ${packageScopeNote(row)} / ${interpolate(t(':count left'), { count: Number(row.remaining_uses || 0) })}`;
                    select.append(option);
                });

                if (rows.length === 0) {
                    container.hidden = true;
                    select.disabled = true;
                    select.value = '';
                    return;
                }

                container.hidden = false;
                if (Array.from(select.options).some((option) => option.value === previousValue)) {
                    select.value = previousValue;
                }

                const relatedCheckbox = checkboxes.find((checkbox) => Number(checkbox.value) === serviceId);
                select.disabled = !(relatedCheckbox && relatedCheckbox.checked);
            });

            if (!packageSummaryCard || !packageSummary) {
                return;
            }

            if (!hasCustomer) {
                packageSummaryCard.hidden = true;
                packageSummary.innerHTML = '';
                return;
            }

            packageSummaryCard.hidden = false;
            packageSummary.innerHTML = '';

            if (packageRows.length === 0) {
                const helper = document.createElement('p');
                helper.className = 'helper';
                helper.textContent = t('No active package balance found for this customer.');
                packageSummary.append(helper);
                return;
            }

            const list = document.createElement('div');
            list.className = 'member-summary-list';

            packageRows.forEach((row) => {
                const item = document.createElement('div');
                const label = document.createElement('span');
                const name = document.createTextNode(String(row.service_name_snapshot || ''));
                const br = document.createElement('br');
                const meta = document.createElement('small');
                const remaining = document.createElement('strong');

                meta.className = 'muted';
                meta.textContent = `${String(row.package_name_snapshot || '')} / ${packageScopeNote(row)}`;
                label.append(name, br, meta);
                remaining.textContent = interpolate(t(':count left'), { count: Number(row.remaining_uses || 0) });
                item.append(label, remaining);
                list.append(item);
            });

            packageSummary.append(list);
        };

        const applyCustomerRecord = (record) => {
            setInputValue(fields.customerId, record.customerId || '');
            setInputValue(fields.carId, record.carId || '');
            setInputValue(fields.customerMode, 'matched');
            setInputValue(lookupPhoneField, record.phone || lookupPhoneField?.value || '');
            setInputValue(fields.customerName, record.customerName || fields.customerName?.value || '');
            setInputValue(fields.phone, record.phone || fields.phone?.value || '');
            setInputValue(fields.plate, record.plateNumber || fields.plate?.value || '');
            setSelectValue(fields.province, record.province || fields.province?.value || '');
            setSelectValue(fields.vehicleType, record.vehicleType || fields.vehicleType?.value || '');
            setSelectValue(fields.brand, record.brand || fields.brand?.value || '');
            setInputValue(fields.model, record.model || fields.model?.value || '');
            setInputValue(fields.color, record.color || fields.color?.value || '');
            renderPackageData(Array.isArray(record.availablePackages) ? record.availablePackages : [], true);
            clearLookupResults();
            setBookingFlowVisibility(true);
            setLookupFeedback(t('Customer data loaded.'), 'success');
            syncQuickServiceCustomerFields();
            updateSummary();
        };

        const renderLookupResults = (matches) => {
            if (!lookupResults) {
                return;
            }

            lookupResults.innerHTML = '';

            matches.forEach((match) => {
                const button = document.createElement('button');
                const title = document.createElement('strong');
                const meta = document.createElement('span');
                const vehicle = [match.brand, match.model].filter(Boolean).join(' ');
                const plate = match.plateNumber || t('No plate');

                button.type = 'button';
                button.className = 'lookup-result';
                title.textContent = match.customerName || t('Unnamed customer');
                meta.textContent = [match.phone, plate, vehicle].filter(Boolean).join(' / ');
                button.append(title, meta);
                button.addEventListener('click', () => applyCustomerRecord(match));
                lookupResults.append(button);
            });

            lookupResults.hidden = matches.length === 0;
        };

        const searchCustomer = async (event) => {
            if (event) {
                event.preventDefault();
            }

            const lookupQuery = lookupPhoneField?.value.trim() || '';

            if (lookupQuery === '') {
                clearLookupResults();
                renderPackageData([], false);
                setBookingFlowVisibility(false);
                setLookupFeedback(t('Enter the exact customer phone number or plate number to search.'), 'warning');
                updateSummary();
                return;
            }

            const params = new URLSearchParams();
            params.set('q', lookupQuery);

            lookupButton.disabled = true;
            setLookupFeedback(t('Searching...'), '');
            clearLookupResults();
            renderPackageData([], false);
            updateSummary();

            try {
                const response = await fetch(`../process/search_customer.php?${params.toString()}`, {
                    headers: {
                        Accept: 'application/json',
                    },
                });
                const data = await response.json();

                if (!response.ok || !data.success) {
                    throw new Error(data.message || t('Customer lookup failed.'));
                }

                const matches = Array.isArray(data.matches) ? data.matches : [];

                if (matches.length === 0) {
                    resetCustomerRecord();
                    if (isQuickServiceForm) {
                        setBookingFlowVisibility(false);
                        setLookupFeedback(t('No matching customer found. Use Guest to continue with a plate number.'), 'warning');
                        return;
                    }
                    setBookingFlowVisibility(true);
                    setLookupFeedback(t('No matching customer found. Register the new customer below.'), 'warning');
                    return;
                }

                if (matches.length === 1) {
                    applyCustomerRecord(matches[0]);
                    return;
                }

                resetCustomerRecord();
                setBookingFlowVisibility(false);
                renderLookupResults(matches);
                setLookupFeedback(interpolate(t('Found :count matches.'), { count: matches.length }), 'success');
            } catch (error) {
                setLookupFeedback(error.message || t('Customer lookup failed.'), 'error');
            } finally {
                lookupButton.disabled = false;
            }
        };

        const updateSummary = () => {
            let count = 0;
            let total = 0;
            let packageDiscount = 0;

            syncLinkedGroupPanels(bookingForm);
            syncMainServiceFlows(bookingForm);
            syncWindowFilmPanels(bookingForm);

            checkboxes.forEach((checkbox) => {
                const packageSelect = bookingForm.querySelector(`[name="package_usage[${checkbox.value}]"]`);

                if (packageSelect) {
                    const container = packageSelect.closest('[data-package-usage-container]');
                    packageSelect.disabled = !checkbox.checked || !!container?.hidden;
                }

                if (!checkbox.checked) {
                    return;
                }

                count += 1;
                total += Number(checkbox.dataset.price || 0);

                if (packageSelect && packageSelect.value !== '') {
                    packageDiscount += Number(checkbox.dataset.price || 0);
                }
            });

            if (countNode) {
                countNode.textContent = String(count);
            }

            if (totalNode) {
                totalNode.textContent = formatMoney(Math.max(0, total - packageDiscount));
            }

            if (packageDiscountNode) {
                packageDiscountNode.textContent = formatMoney(packageDiscount);
            }

            if (totalPriceInput && !totalPriceEdited) {
                totalPriceInput.value = Math.max(0, total - packageDiscount).toFixed(2);
            }

            if (isQuickServiceForm) {
                const {
                    balanceDue,
                    receivedAmount,
                    isCashPayment,
                    isUnderpaid,
                    isOverpaid,
                    recognizedPayment,
                    changeDue,
                    hasPaymentInput,
                    allowPayment,
                    willCloseBill,
                } = quickServicePaymentState();
                let paymentMessage = t('Payment can be recorded more than once. The bill will close automatically only when the total paid reaches the amount due.');
                let paymentMessageState = '';

                if (paidBeforeNode) {
                    paidBeforeNode.textContent = formatMoney(quickServicePaidBefore);
                }

                if (balanceDueNode) {
                    balanceDueNode.textContent = formatMoney(balanceDue);
                }

                if (balanceDueCard) {
                    balanceDueCard.classList.toggle('is-danger', isUnderpaid);
                }

                if (recognizedPaymentNode) {
                    recognizedPaymentNode.textContent = formatMoney(recognizedPayment);
                }

                if (changeDueNode) {
                    changeDueNode.textContent = formatMoney(changeDue);
                }

                if (changeField) {
                    changeField.hidden = changeDue <= 0;
                }

                if (!hasPaymentInput && balanceDue > 0) {
                    paymentMessage = supportsHoldAction
                        ? t('Enter the amount received, or use Hold bill to keep this bill open without adding a payment.')
                        : t('Enter the amount received to continue.');
                } else if (isUnderpaid) {
                    paymentMessage = interpolate(t('Outstanding :amount THB. Payment can be recorded, but the bill will stay open until fully paid.'), {
                        amount: formatMoney(balanceDue - recognizedPayment),
                    });
                    paymentMessageState = 'is-danger';
                } else if (isOverpaid && !isCashPayment) {
                    paymentMessage = t('Received amount is higher than the amount due. Adjust Received now to match before closing the bill.');
                    paymentMessageState = 'is-danger';
                } else if (changeDue > 0) {
                    paymentMessage = interpolate(t('Change due :amount THB. Cash payment can close the bill.'), {
                        amount: formatMoney(changeDue),
                    });
                    paymentMessageState = 'is-success';
                } else if (hasPaymentInput && willCloseBill) {
                    paymentMessage = t('Payment is enough to close the bill.');
                    paymentMessageState = 'is-success';
                }

                if (paymentValidationMessage) {
                    paymentValidationMessage.textContent = paymentMessage;
                    paymentValidationMessage.classList.remove('is-danger', 'is-success');

                    if (paymentMessageState !== '') {
                        paymentValidationMessage.classList.add(paymentMessageState);
                    }
                }

                if (receivedAmountInput) {
                    receivedAmountInput.classList.toggle('is-danger', isUnderpaid || (isOverpaid && !isCashPayment));
                }

                if (paySubmitButton) {
                    paySubmitButton.disabled = !allowPayment;
                }

                updateQuickServiceQrPreview(balanceDue, receivedAmount);
                updatePaymentSlipField();
                updateDepositSlipFields();
                updateDepositQrPanels();
            }

            validateBookingTotal();
        };

        if (totalPriceInput) {
            totalPriceInput.addEventListener('input', () => {
                totalPriceEdited = true;
                validateBookingTotal();
            });
            totalPriceInput.addEventListener('blur', () => {
                normalizeMoneyInput(totalPriceInput);
                updateSummary();
            });
            protectNumberInputFromWheel(totalPriceInput);
        }

        if (receivedAmountInput) {
            receivedAmountInput.addEventListener('input', updateSummary);
            receivedAmountInput.addEventListener('blur', () => {
                normalizeMoneyInput(receivedAmountInput);
                updateSummary();
            });
            protectNumberInputFromWheel(receivedAmountInput);
        }

        if (paymentMethodSelect) {
            paymentMethodSelect.addEventListener('change', updateSummary);
        }

        if (branchNameInput) {
            branchNameInput.addEventListener('change', () => {
                updateDepositQrPanels();
                updateSummary();
            });
        }

        depositSlipGroups.forEach((item) => {
            if (item.amountInput instanceof HTMLInputElement) {
                item.amountInput.addEventListener('input', updateDepositSlipFields);
                item.amountInput.addEventListener('input', updateDepositQrPanels);
                item.amountInput.addEventListener('blur', () => {
                    updateDepositSlipFields();
                    updateDepositQrPanels();
                });
            }

            if (item.methodSelect instanceof HTMLSelectElement) {
                item.methodSelect.addEventListener('change', () => {
                    updateDepositSlipFields();
                    updateDepositQrPanels();
                });
            }
        });

        if (isQuickServiceForm) {
            bookingForm.addEventListener('keydown', (event) => {
                if (event.key !== 'Enter') {
                    return;
                }

                const target = event.target;
                if (!(target instanceof HTMLElement)) {
                    return;
                }

                const tagName = target.tagName;
                const inputType = target instanceof HTMLInputElement ? target.type : '';
                const isSubmitControl = tagName === 'BUTTON' || inputType === 'submit' || inputType === 'button';

                if (tagName !== 'TEXTAREA' && !isSubmitControl) {
                    event.preventDefault();
                }
            });

            bookingForm.addEventListener('submit', (event) => {
                const submitter = event.submitter;
                const submitAction = submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement
                    ? submitter.value
                    : '';

                if (!['hold', 'pay'].includes(submitAction)) {
                    event.preventDefault();

                    if (paymentValidationMessage) {
                        paymentValidationMessage.textContent = supportsHoldAction
                            ? t('Use the Hold bill or Payment button to continue.')
                            : t('Use the Payment button to continue.');
                        paymentValidationMessage.classList.remove('is-success');
                        paymentValidationMessage.classList.add('is-danger');
                    }

                    return;
                }

                if (submitAction === 'pay') {
                    const state = quickServicePaymentState();

                    if (!state.allowPayment) {
                        event.preventDefault();
                        updateSummary();
                    }
                }
            });
        }

        if (!isQuickServiceForm) {
            bookingForm.addEventListener('submit', (event) => {
                const submitter = event.submitter;
                const usesAlternateSubmitFlow = submitter instanceof HTMLElement
                    && (submitter.hasAttribute('formnovalidate') || (submitter.getAttribute('formmethod') || '').toUpperCase() === 'GET');

                if (usesAlternateSubmitFlow) {
                    return;
                }

                if (!validateBookingTotal()) {
                    event.preventDefault();
                    totalPriceInput?.reportValidity();
                }
            });
        }

        if (lookupButton) {
            lookupButton.addEventListener('click', searchCustomer);
        }

        if (newCustomerButton) {
            newCustomerButton.addEventListener('click', startNewCustomerRegistration);
        }

        [lookupPhoneField].filter(Boolean).forEach((field) => {
            field.addEventListener('keydown', (event) => {
                if (event.key === 'Enter') {
                    searchCustomer(event);
                }
            });
        });

        checkboxes.forEach((checkbox) => checkbox.addEventListener('change', updateSummary));
        packageUsageSelects.forEach((select) => select.addEventListener('change', updateSummary));
        mainServiceGroupInputs.forEach((input) => input.addEventListener('change', updateSummary));
        syncQuickServiceCustomerFields();
        updateSummary();
        updateDepositSlipFields();
        updateDepositQrPanels();
    }

    document.querySelectorAll('[data-deposit-manage-form]').forEach((depositForm) => {
        const amountInput = depositForm.querySelector('[data-deposit-amount-input]');
        const methodSelect = depositForm.querySelector('[data-deposit-method-select]');
        const slipGroup = depositForm.querySelector('[data-deposit-slip-group]');
        const slipInput = depositForm.querySelector('[data-deposit-slip-input]');
        const qrPanel = depositForm.querySelector('[data-deposit-qr-panel]');
        const qrHint = depositForm.querySelector('[data-deposit-qr-hint]');
        const qrImage = depositForm.querySelector('[data-deposit-qr-image]');
        const qrDownload = depositForm.querySelector('[data-deposit-qr-download]');
        const qrEndpoint = depositForm.dataset.qrEndpoint || '';
        const qrScope = depositForm.dataset.qrScope || '';
        const qrBranch = (depositForm.dataset.qrBranch || '').trim();
        const methodsWithSlipSupport = ['transfer', 'qr', 'card'];

        const formatMoney = (value) => Number(value || 0).toLocaleString('en-US', {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2,
        });

        const parseAmount = () => {
            if (!amountInput) {
                return 0;
            }

            const numericValue = Number(String(amountInput.value || '').replace(/,/g, '').trim() || 0);
            return Number.isFinite(numericValue) ? Math.max(0, numericValue) : 0;
        };

        const updateSlipVisibility = () => {
            if (!slipGroup || !methodSelect) {
                return;
            }

            const showSlip = methodsWithSlipSupport.includes(methodSelect.value) && parseAmount() > 0;
            slipGroup.hidden = !showSlip;

            if (!showSlip && slipInput instanceof HTMLInputElement) {
                slipInput.value = '';
            }
        };

        const updateQrVisibility = () => {
            if (!qrPanel || !qrHint || !qrImage || !methodSelect) {
                return;
            }

            const amount = parseAmount();
            const showQr = methodSelect.value === 'qr' && amount > 0;
            qrPanel.hidden = !showQr;

            if (!showQr) {
                qrImage.removeAttribute('src');
                qrHint.textContent = t('Scan to receive payment.');
                if (qrDownload) {
                    qrDownload.hidden = true;
                    qrDownload.removeAttribute('href');
                }
                return;
            }

            const params = new URLSearchParams();
            params.set('amount', amount.toFixed(2));
            if (qrScope) {
                params.set('scope', qrScope);
            }
            if (qrBranch !== '') {
                params.set('branch', qrBranch);
            }

            const queryString = params.toString();
            if (qrEndpoint !== '') {
                qrImage.src = `${qrEndpoint}?${queryString}`;
                if (qrDownload) {
                    qrDownload.href = `${qrEndpoint}?${queryString}&download=1`;
                    qrDownload.hidden = false;
                }
            }

            qrHint.textContent = interpolate(t('Scan QR for :amount THB.'), { amount: formatMoney(amount) });
        };

        if (amountInput) {
            amountInput.addEventListener('input', () => {
                updateSlipVisibility();
                updateQrVisibility();
            });
        }

        if (methodSelect) {
            methodSelect.addEventListener('change', () => {
                updateSlipVisibility();
                updateQrVisibility();
            });
        }

        updateSlipVisibility();
        updateQrVisibility();
    });

    document.querySelectorAll('[data-service-edit-form]').forEach((serviceForm) => {
        const checkboxes = Array.from(serviceForm.querySelectorAll('[data-service-option]'));
        const countNode = serviceForm.querySelector('[data-selected-count]');
        const totalNode = serviceForm.querySelector('[data-selected-total]');
        const packageDiscountNode = serviceForm.querySelector('[data-selected-package-discount]');
        const totalPriceInput = serviceForm.querySelector('[data-total-price-input]');
        const packageUsageSelects = Array.from(serviceForm.querySelectorAll('[data-package-usage]'));
        const mainServiceGroupInputs = Array.from(serviceForm.querySelectorAll('[data-main-service-group]'));
        let totalPriceEdited = totalPriceInput ? totalPriceInput.value.trim() !== '' : false;

        const formatMoney = (value) => Number(value || 0).toLocaleString('en-US', {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2,
        });

        const updateSummary = () => {
            let count = 0;
            let total = 0;
            let packageDiscount = 0;

            syncLinkedGroupPanels(serviceForm);
            syncMainServiceFlows(serviceForm);
            syncWindowFilmPanels(serviceForm);

            checkboxes.forEach((checkbox) => {
                const packageSelect = serviceForm.querySelector(`[name="package_usage[${checkbox.value}]"]`);

                if (packageSelect) {
                    const container = packageSelect.closest('[data-package-usage-container]');
                    packageSelect.disabled = !checkbox.checked || !!container?.hidden;
                }

                if (!checkbox.checked) {
                    return;
                }

                count += 1;
                total += Number(checkbox.dataset.price || 0);

                if (packageSelect && packageSelect.value !== '') {
                    packageDiscount += Number(checkbox.dataset.price || 0);
                }
            });

            if (countNode) {
                countNode.textContent = String(count);
            }

            if (totalNode) {
                totalNode.textContent = formatMoney(Math.max(0, total - packageDiscount));
            }

            if (packageDiscountNode) {
                packageDiscountNode.textContent = formatMoney(packageDiscount);
            }

            if (totalPriceInput && !totalPriceEdited) {
                totalPriceInput.value = Math.max(0, total - packageDiscount).toFixed(2);
            }
        };

        if (totalPriceInput) {
            totalPriceInput.addEventListener('input', () => {
                totalPriceEdited = true;
            });
        }

        checkboxes.forEach((checkbox) => checkbox.addEventListener('change', updateSummary));
        packageUsageSelects.forEach((select) => select.addEventListener('change', updateSummary));
        mainServiceGroupInputs.forEach((input) => input.addEventListener('change', updateSummary));
        updateSummary();
    });

    document.querySelectorAll('[data-service-structure-form]').forEach((form) => {
        const typeSelect = form.querySelector('[data-service-type-select]');
        const categorySelect = form.querySelector('[data-service-category-select]');
        const mainServiceSelect = form.querySelector('[data-service-main-select]');
        const sync = () => syncServiceStructureForm(form);

        if (typeSelect) {
            typeSelect.addEventListener('change', sync);
        }

        if (categorySelect) {
            categorySelect.addEventListener('change', sync);
        }

        if (mainServiceSelect) {
            mainServiceSelect.addEventListener('change', sync);
        }

        sync();
    });

    document.querySelectorAll('[data-service-group-form]').forEach((form) => {
        const categorySelect = form.querySelector('[data-service-category-select]');
        const sync = () => syncServiceGroupForm(form);

        if (categorySelect) {
            categorySelect.addEventListener('change', sync);
        }

        sync();
    });

    document.querySelectorAll('[data-edit-toggle]').forEach((button) => {
        const panelId = button.getAttribute('aria-controls') || '';
        const panel = panelId ? document.getElementById(panelId) : null;

        if (!panel) {
            return;
        }

        const labelNode = button.querySelector('.app-action-button-label');
        const closedLabel = button.dataset.closedLabel || (labelNode ? labelNode.textContent.trim() : button.textContent.trim()) || t('Edit');
        const openLabel = button.dataset.openLabel || t('Hide');

        const setOpen = (isOpen) => {
            panel.hidden = !isOpen;
            button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            if (labelNode) {
                labelNode.textContent = isOpen ? openLabel : closedLabel;
            } else {
                button.textContent = isOpen ? openLabel : closedLabel;
            }
            button.setAttribute('title', isOpen ? openLabel : closedLabel);
            button.setAttribute('aria-label', isOpen ? openLabel : closedLabel);

            if (isOpen) {
                panel.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
                const firstField = panel.querySelector('input:not([type="hidden"]), select, textarea');

                if (firstField) {
                    firstField.focus({ preventScroll: true });
                }
            }
        };

        button.addEventListener('click', () => {
            setOpen(panel.hidden);
        });
    });

    document.querySelectorAll('[data-multi-role-permissions]').forEach((form) => {
        const toggleAll = form.querySelector('[data-multi-role-toggle-all]');
        const roleToggles = Array.from(form.querySelectorAll('[data-multi-role-toggle]'));

        if (roleToggles.length === 0) {
            return;
        }

        const syncMultiRoleColumns = () => {
            let checkedCount = 0;

            roleToggles.forEach((toggle) => {
                const columnId = toggle.dataset.columnId || '';
                const isVisible = toggle.checked;

                if (isVisible) {
                    checkedCount += 1;
                }

                form.querySelectorAll(`[data-multi-role-column-head="${columnId}"], [data-multi-role-column="${columnId}"]`).forEach((cell) => {
                    cell.hidden = !isVisible;
                });
            });

            if (toggleAll) {
                toggleAll.checked = checkedCount === roleToggles.length;
                toggleAll.indeterminate = checkedCount > 0 && checkedCount < roleToggles.length;
            }
        };

        if (toggleAll) {
            toggleAll.addEventListener('change', () => {
                roleToggles.forEach((toggle) => {
                    toggle.checked = toggleAll.checked;
                });

                syncMultiRoleColumns();
            });
        }

        roleToggles.forEach((toggle) => {
            toggle.addEventListener('change', syncMultiRoleColumns);
        });

        syncMultiRoleColumns();
    });
});
