(() => {
    const root = document.documentElement;
    const toggle = document.querySelector('[data-theme-toggle]');
    const updateThemeLabel = () => {
        const label = document.querySelector('[data-theme-label]');
        if (label) label.textContent = root.dataset.theme === 'dark' ? 'Light mode' : 'Dark mode';
    };
    updateThemeLabel();
    toggle?.addEventListener('click', () => {
        root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
        localStorage.setItem('budget-theme', root.dataset.theme);
        updateThemeLabel();
        document.querySelectorAll('[data-trend-chart]').forEach(drawTrendChart);
    });

    const sidebar = document.querySelector('#sidebar');
    const closeMenu = () => sidebar?.classList.remove('open');
    document.querySelector('[data-menu-toggle]')?.addEventListener('click', () => sidebar?.classList.toggle('open'));
    document.querySelector('[data-menu-close]')?.addEventListener('click', closeMenu);
    sidebar?.querySelectorAll('a').forEach((link) => link.addEventListener('click', closeMenu));

    document.querySelector('[data-month-picker]')?.addEventListener('change', (event) => {
        const picker = event.currentTarget;
        const parameters = new URLSearchParams({ route: picker.dataset.route, month: picker.value });
        window.location.href = `?${parameters.toString()}`;
    });

    document.querySelectorAll('[data-dialog-open]').forEach((button) => {
        button.addEventListener('click', () => document.getElementById(button.dataset.dialogOpen)?.showModal());
    });
    document.querySelectorAll('[data-dialog-close]').forEach((button) => {
        button.addEventListener('click', () => button.closest('dialog')?.close());
    });
    document.querySelectorAll('dialog').forEach((dialog) => {
        dialog.addEventListener('click', (event) => {
            if (event.target === dialog) dialog.close();
        });
    });
    const requestedDialog = new URLSearchParams(window.location.search).get('open');
    if (requestedDialog) document.getElementById(requestedDialog)?.showModal();

    document.querySelectorAll('[data-confirm]').forEach((form) => {
        form.addEventListener('submit', (event) => {
            if (!window.confirm(form.dataset.confirm)) event.preventDefault();
        });
    });
    document.querySelectorAll('[data-confirm-button]').forEach((button) => {
        button.addEventListener('click', (event) => {
            if (!window.confirm(button.dataset.confirmButton)) event.preventDefault();
        });
    });

    document.querySelectorAll('.flash button').forEach((button) => {
        button.addEventListener('click', () => button.closest('.flash')?.remove());
    });

    document.querySelectorAll('[data-table-search]').forEach((input) => {
        input.addEventListener('input', () => {
            const table = document.getElementById(input.dataset.tableSearch);
            const query = input.value.trim().toLowerCase();
            table?.querySelectorAll('tbody tr').forEach((row) => {
                row.hidden = query !== '' && !row.textContent.toLowerCase().includes(query);
            });
        });
    });

    const concealed = document.querySelector('[data-concealed-balance]');
    if (concealed) {
        let approved = false;
        concealed.addEventListener('click', () => {
            if (!approved) approved = window.confirm('Reveal the dormant account balance while your pointer remains here?');
            if (approved) {
                concealed.querySelector('.revealed').textContent = concealed.dataset.value;
                concealed.classList.add('reveal');
            }
        });
        concealed.addEventListener('mouseleave', () => {
            concealed.classList.remove('reveal');
            approved = false;
        });
    }

    document.querySelectorAll('[data-dead-transfer-form]').forEach((form) => {
        const direction = form.querySelector('[name="direction"]');
        const restoreFields = form.querySelector('[data-restore-fields]');
        const restoreType = form.querySelector('[data-restore-type]');
        const restoreItem = form.querySelector('[data-restore-item]');
        const syncRestoreFields = () => {
            const restoring = direction?.value === 'restore';
            if (restoreFields) restoreFields.hidden = !restoring;
            if (restoreItem) restoreItem.hidden = !restoring || restoreType?.value !== 'item';
        };
        direction?.addEventListener('change', syncRestoreFields);
        restoreType?.addEventListener('change', syncRestoreFields);
        syncRestoreFields();
    });

    const onboardingForm = document.querySelector('[data-onboarding-form]');
    if (onboardingForm) {
        const steps = [...onboardingForm.querySelectorAll('[data-onboarding-step]')];
        const progress = [...document.querySelectorAll('[data-onboarding-progress]')];
        const cashInput = onboardingForm.querySelector('[data-current-cash]');
        const billCountInput = onboardingForm.querySelector('[data-bill-count]');
        const billRows = onboardingForm.querySelector('[data-bill-rows]');
        const billTemplate = document.querySelector('#onboarding-bill-template');
        const markAllPaid = onboardingForm.querySelector('[data-mark-all-paid]');
        const enableDead = onboardingForm.querySelector('[data-enable-onboarding-dead]');
        const deadFields = onboardingForm.querySelector('[data-onboarding-dead-fields]');
        const deadAmount = onboardingForm.querySelector('[data-dead-amount]');
        const submitButton = onboardingForm.querySelector('[data-onboarding-submit]');
        const warning = onboardingForm.querySelector('[data-onboarding-warning]');
        const symbol = onboardingForm.dataset.symbol || '$';
        let currentStep = 1;
        let generatedCount = -1;

        const money = (value) => `${value < 0 ? '-' : ''}${symbol}${Math.abs(value).toLocaleString(undefined, {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2,
        })}`;

        const showStep = (target) => {
            currentStep = target;
            steps.forEach((step) => {
                const active = Number(step.dataset.onboardingStep) === target;
                step.hidden = !active;
                step.classList.toggle('active', active);
            });
            progress.forEach((item) => {
                const number = Number(item.dataset.onboardingProgress);
                item.classList.toggle('active', number === target);
                item.classList.toggle('complete', number < target);
            });
            window.scrollTo({ top: 0, behavior: 'smooth' });
        };

        const validateCurrentStep = () => {
            const step = steps.find((item) => Number(item.dataset.onboardingStep) === currentStep);
            const fields = [...step.querySelectorAll('input, select, textarea')].filter((field) => !field.disabled);
            for (const field of fields) {
                if (!field.checkValidity()) {
                    field.reportValidity();
                    return false;
                }
            }
            return true;
        };

        const paidInputs = () => [...billRows.querySelectorAll('[data-bill-field="paid_now"]')];
        const syncPaidMaster = () => {
            const inputs = paidInputs();
            const checked = inputs.filter((input) => input.checked).length;
            markAllPaid.checked = inputs.length > 0 && checked === inputs.length;
            markAllPaid.indeterminate = checked > 0 && checked < inputs.length;
        };

        const updateReview = () => {
            const cash = Number(cashInput.value) || 0;
            let paid = 0;
            billRows.querySelectorAll('.onboarding-bill-card').forEach((card) => {
                if (card.querySelector('[data-bill-field="paid_now"]').checked) {
                    paid += Number(card.querySelector('[data-bill-field="amount"]').value) || 0;
                }
            });
            const reserved = enableDead.checked ? Number(deadAmount.value) || 0 : 0;
            const remaining = cash - paid - reserved;
            onboardingForm.querySelector('[data-review-cash]').textContent = money(cash);
            onboardingForm.querySelector('[data-review-paid]').textContent = money(paid);
            onboardingForm.querySelector('[data-review-dead]').textContent = money(reserved);
            onboardingForm.querySelector('[data-review-remaining]').textContent = money(remaining);
            onboardingForm.querySelector('.review-remaining').classList.toggle('negative', remaining < 0);
            warning.hidden = remaining >= 0;
            submitButton.disabled = remaining < 0;
        };

        const generateBillRows = () => {
            const count = Math.max(0, Math.min(50, Number.parseInt(billCountInput.value, 10) || 0));
            billCountInput.value = String(count);
            if (generatedCount === count) return count;
            generatedCount = count;
            billRows.replaceChildren();
            for (let index = 0; index < count; index++) {
                const fragment = billTemplate.content.cloneNode(true);
                const card = fragment.querySelector('.onboarding-bill-card');
                card.querySelector('[data-bill-index]').textContent = String(index + 1).padStart(2, '0');
                const title = card.querySelector('[data-bill-title]');
                title.textContent = `Bill ${index + 1}`;
                card.querySelectorAll('[data-bill-field]').forEach((field) => {
                    const key = field.dataset.billField;
                    field.name = `bills[${index}][${key}]`;
                });
                const nameInput = card.querySelector('[data-bill-field="name"]');
                const amountInput = card.querySelector('[data-bill-field="amount"]');
                const paidInput = card.querySelector('[data-bill-field="paid_now"]');
                const categoryInput = card.querySelector('[data-bill-field="category"]');
                const customWrap = card.querySelector('[data-custom-category]');
                const customInput = card.querySelector('[data-bill-field="custom_category"]');

                nameInput.addEventListener('input', () => {
                    title.textContent = nameInput.value.trim() || `Bill ${index + 1}`;
                });
                amountInput.addEventListener('input', updateReview);
                paidInput.addEventListener('change', () => {
                    syncPaidMaster();
                    updateReview();
                });
                categoryInput.addEventListener('change', () => {
                    const custom = categoryInput.value === '__custom__';
                    customWrap.hidden = !custom;
                    customInput.required = custom;
                    if (!custom) customInput.value = '';
                });
                paidInput.checked = markAllPaid.checked;
                billRows.append(fragment);
            }
            syncPaidMaster();
            updateReview();
            return count;
        };

        onboardingForm.querySelectorAll('[data-onboarding-next]').forEach((button) => {
            button.addEventListener('click', () => {
                if (!validateCurrentStep()) return;
                if (currentStep === 2) {
                    const count = generateBillRows();
                    showStep(count === 0 ? 4 : 3);
                    updateReview();
                    return;
                }
                showStep(Math.min(4, currentStep + 1));
                updateReview();
            });
        });

        onboardingForm.querySelectorAll('[data-onboarding-back]').forEach((button) => {
            button.addEventListener('click', () => {
                if (currentStep === 4 && generatedCount === 0) {
                    showStep(2);
                    return;
                }
                showStep(Math.max(1, currentStep - 1));
            });
        });

        markAllPaid.addEventListener('change', () => {
            paidInputs().forEach((input) => {
                input.checked = markAllPaid.checked;
            });
            markAllPaid.indeterminate = false;
            updateReview();
        });
        enableDead.addEventListener('change', () => {
            deadFields.hidden = !enableDead.checked;
            deadAmount.required = enableDead.checked;
            if (!enableDead.checked) deadAmount.value = '';
            updateReview();
        });
        cashInput.addEventListener('input', updateReview);
        deadAmount.addEventListener('input', updateReview);
        onboardingForm.addEventListener('submit', (event) => {
            generateBillRows();
            updateReview();
            if (submitButton.disabled) {
                event.preventDefault();
                warning.hidden = false;
            }
        });
        updateReview();
    }

    function drawTrendChart(canvas) {
        let data;
        try {
            data = JSON.parse(canvas.dataset.trendChart || '[]');
        } catch {
            return;
        }
        if (!data.length) return;
        const width = canvas.clientWidth || 600;
        const height = Number(canvas.getAttribute('height')) || 220;
        const ratio = window.devicePixelRatio || 1;
        canvas.width = width * ratio;
        canvas.height = height * ratio;
        canvas.style.height = `${height}px`;
        const context = canvas.getContext('2d');
        context.scale(ratio, ratio);

        const styles = getComputedStyle(document.documentElement);
        const text = styles.getPropertyValue('--muted').trim();
        const border = styles.getPropertyValue('--border-strong').trim();
        const purple = styles.getPropertyValue('--purple-light').trim();
        const pink = styles.getPropertyValue('--pink').trim();
        const padding = { top: 20, right: 12, bottom: 32, left: 48 };
        const plotWidth = width - padding.left - padding.right;
        const plotHeight = height - padding.top - padding.bottom;
        const maxValue = Math.max(...data.flatMap((row) => [Number(row.gross_resources) || 0, Number(row.expenses) || 0]), 1) * 1.12;

        context.clearRect(0, 0, width, height);
        context.font = '10px system-ui';
        context.fillStyle = text;
        context.strokeStyle = border;
        context.lineWidth = 1;
        context.setLineDash([3, 5]);
        for (let line = 0; line <= 4; line++) {
            const y = padding.top + plotHeight * (line / 4);
            context.beginPath();
            context.moveTo(padding.left, y);
            context.lineTo(width - padding.right, y);
            context.stroke();
            const value = maxValue * (1 - line / 4);
            context.fillText(compactMoney(value, canvas.dataset.symbol), 0, y + 3);
        }
        context.setLineDash([]);

        const pointX = (index) => padding.left + (data.length === 1 ? plotWidth / 2 : plotWidth * index / (data.length - 1));
        const pointY = (value) => padding.top + plotHeight - (Number(value) / maxValue * plotHeight);

        const drawSeries = (key, color, fill) => {
            context.beginPath();
            data.forEach((row, index) => {
                const x = pointX(index);
                const y = pointY(row[key]);
                index === 0 ? context.moveTo(x, y) : context.lineTo(x, y);
            });
            context.strokeStyle = color;
            context.lineWidth = 2.5;
            context.lineJoin = 'round';
            context.stroke();
            if (fill) {
                context.lineTo(pointX(data.length - 1), padding.top + plotHeight);
                context.lineTo(pointX(0), padding.top + plotHeight);
                context.closePath();
                const gradient = context.createLinearGradient(0, padding.top, 0, padding.top + plotHeight);
                gradient.addColorStop(0, `${color}32`);
                gradient.addColorStop(1, `${color}00`);
                context.fillStyle = gradient;
                context.fill();
            }
            data.forEach((row, index) => {
                context.beginPath();
                context.arc(pointX(index), pointY(row[key]), 3.5, 0, Math.PI * 2);
                context.fillStyle = color;
                context.fill();
            });
        };

        drawSeries('gross_resources', purple, true);
        drawSeries('expenses', pink, false);
        data.forEach((row, index) => {
            const label = new Date(`${row.month}-02T12:00:00`).toLocaleDateString(undefined, { month: 'short' });
            context.fillStyle = text;
            context.textAlign = 'center';
            context.fillText(label, pointX(index), height - 9);
        });
        context.textAlign = 'left';
        context.fillStyle = purple;
        context.fillRect(width - 145, 3, 8, 8);
        context.fillStyle = text;
        context.fillText('Resources', width - 132, 11);
        context.fillStyle = pink;
        context.fillRect(width - 70, 3, 8, 8);
        context.fillStyle = text;
        context.fillText('Spent', width - 57, 11);
    }

    function compactMoney(value, symbol = '$') {
        if (value >= 1000000) return `${symbol}${(value / 1000000).toFixed(1)}m`;
        if (value >= 1000) return `${symbol}${(value / 1000).toFixed(1)}k`;
        return `${symbol}${Math.round(value)}`;
    }

    document.querySelectorAll('[data-trend-chart]').forEach(drawTrendChart);
    let resizeTimer;
    window.addEventListener('resize', () => {
        window.clearTimeout(resizeTimer);
        resizeTimer = window.setTimeout(() => document.querySelectorAll('[data-trend-chart]').forEach(drawTrendChart), 120);
    });
})();
