(() => {
    const root = document.documentElement;
    const mobileDrawer = document.querySelector('[data-mobile-drawer]');
    const mobileOverlay = document.querySelector('[data-mobile-overlay]');
    const openButtons = document.querySelectorAll('[data-open-sidebar]');
    const closeButtons = document.querySelectorAll('[data-close-sidebar]');
    const themeButtons = document.querySelectorAll('[data-theme-toggle]');
    const globalSearchInput = document.querySelector('[data-global-search-input]');
    const globalSearchForm = document.querySelector('[data-global-search-form]');

    const setDrawer = (open) => {
        mobileDrawer?.classList.toggle('is-open', open);
        mobileOverlay?.classList.toggle('is-open', open);
        document.body.style.overflow = open ? 'hidden' : '';
        openButtons.forEach((button) => button.setAttribute('aria-expanded', open ? 'true' : 'false'));
    };

    openButtons.forEach((button) => button.addEventListener('click', () => setDrawer(true)));
    closeButtons.forEach((button) => button.addEventListener('click', () => setDrawer(false)));
    mobileOverlay?.addEventListener('click', () => setDrawer(false));
    document.addEventListener('keydown', (event) => event.key === 'Escape' && setDrawer(false));

    document.addEventListener('keydown', (event) => {
        if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== 'k') return;
        event.preventDefault();
        if (globalSearchInput && globalSearchInput.offsetParent !== null) {
            globalSearchInput.focus();
            globalSearchInput.select();
            return;
        }
        if (globalSearchForm?.action) window.location.assign(globalSearchForm.action);
    });

    const storedTheme = localStorage.getItem('microfinance-theme');
    if (storedTheme === 'dark' || (!storedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
        root.classList.add('dark');
    }

    const updateThemeIcon = () => {
        const dark = root.classList.contains('dark');
        themeButtons.forEach((button) => {
            button.setAttribute('aria-label', dark ? 'Switch to light mode' : 'Switch to dark mode');
            button.querySelector('[data-sun]')?.classList.toggle('hidden', !dark);
            button.querySelector('[data-moon]')?.classList.toggle('hidden', dark);
        });
    };

    themeButtons.forEach((button) => button.addEventListener('click', () => {
        root.classList.toggle('dark');
        localStorage.setItem('microfinance-theme', root.classList.contains('dark') ? 'dark' : 'light');
        updateThemeIcon();
    }));

    updateThemeIcon();

    // Phone inputs are stored internationally (e.g. 2557XXXXXXXX) but entered locally.
    // The business-level country code is displayed as a fixed prefix and never typed by users.
    const phoneCountryMeta = document.querySelector('meta[name="phone-country-code"]');
    let phoneCountryCode = (phoneCountryMeta?.content || '255').replace(/\D/g, '') || '255';

    const isPhoneInput = (input) => {
        if (!(input instanceof HTMLInputElement)) return false;
        const name = (input.name || '').toLowerCase();
        if (!name || name === 'phone_country_code') return false;

        return name === 'phone'
            || name.endsWith('_phone')
            || name.startsWith('phone_')
            || name === 'mobile'
            || name.endsWith('_mobile')
            || name === 'telephone'
            || name.endsWith('_telephone');
    };

    const localPhoneValue = (value) => {
        const digits = String(value || '').replace(/\D/g, '');
        if (!digits) return '';

        if (digits.startsWith(phoneCountryCode)) {
            const local = digits.slice(phoneCountryCode.length);
            if (/^[67]\d{8}$/.test(local)) return local;
        }

        return digits.slice(0, 10);
    };

    const refreshPhonePrefixes = () => {
        document.querySelectorAll('[data-phone-prefix]').forEach((prefix) => {
            prefix.textContent = `+${phoneCountryCode}`;
        });
    };

    const enhancePhoneInput = (input) => {
        if (!isPhoneInput(input) || input.dataset.phoneEnhanced === '1') return;
        input.dataset.phoneEnhanced = '1';
        input.type = 'tel';
        input.inputMode = 'numeric';
        input.autocomplete = 'tel-national';
        input.maxLength = 10;
        input.pattern = '(?:0[67][0-9]{8}|[67][0-9]{8})';
        input.placeholder = '7XXXXXXXX or 07XXXXXXXX';
        input.value = localPhoneValue(input.value);

        const wrapper = document.createElement('div');
        wrapper.className = 'mf-phone-control';
        const prefix = document.createElement('span');
        prefix.className = 'mf-phone-prefix';
        prefix.dataset.phonePrefix = '1';
        prefix.textContent = `+${phoneCountryCode}`;

        input.parentNode?.insertBefore(wrapper, input);
        wrapper.append(prefix, input);

        input.addEventListener('input', () => {
            input.value = input.value.replace(/\D/g, '').slice(0, 10);
        });
    };

    // Business Settings owns the country calling code. Add the compact field beside
    // the existing business phone controls without duplicating it across templates.
    const businessPhoneInput = document.querySelector('form[action*="business-settings"] input[name="phone"]');
    if (businessPhoneInput && !document.querySelector('input[name="phone_country_code"]')) {
        const field = document.createElement('div');
        field.className = 'mf-col-3';
        field.innerHTML = '<label class="mf-field-label">Phone country code</label><div class="mf-country-code-control"><span>+</span><input class="mf-input" name="phone_country_code" inputmode="numeric" maxlength="4" required></div><p class="mf-field-help">Used as the fixed prefix for every phone number in the system.</p>';
        const input = field.querySelector('input[name="phone_country_code"]');
        input.value = phoneCountryCode;
        businessPhoneInput.closest('[class*="mf-col-"]')?.before(field);

        input.addEventListener('input', () => {
            input.value = input.value.replace(/\D/g, '').slice(0, 4);
            if (/^[1-9]\d{0,3}$/.test(input.value)) {
                phoneCountryCode = input.value;
                if (phoneCountryMeta) phoneCountryMeta.content = phoneCountryCode;
                refreshPhonePrefixes();
            }
        });
    }

    document.querySelectorAll('input[name]').forEach(enhancePhoneInput);

    // Some loan/customer forms add contact controls dynamically; keep them masked too.
    new MutationObserver((mutations) => {
        mutations.forEach((mutation) => mutation.addedNodes.forEach((node) => {
            if (!(node instanceof Element)) return;
            if (node.matches('input[name]')) enhancePhoneInput(node);
            node.querySelectorAll?.('input[name]').forEach(enhancePhoneInput);
        }));
    }).observe(document.body, { childList: true, subtree: true });

    document.querySelectorAll('[data-table-column-toggle]').forEach((toggle) => {
        toggle.addEventListener('change', () => {
            const table = document.getElementById(toggle.dataset.tableColumnToggle);
            const column = toggle.dataset.column;
            if (!table || !column) return;

            table.querySelectorAll('[data-table-column]').forEach((cell) => {
                if (cell.dataset.tableColumn === column) cell.hidden = !toggle.checked;
            });
        });
    });

    const csvCell = (value) => `"${value.replace(/\s+/g, ' ').trim().replace(/"/g, '""')}"`;

    document.querySelectorAll('[data-table-export]').forEach((button) => {
        button.addEventListener('click', () => {
            const table = document.getElementById(button.dataset.tableExport);
            if (!table) return;

            const lines = [...table.querySelectorAll('tr')].map((row) => {
                const cells = [...row.children].filter((cell) => !cell.hidden && !cell.hasAttribute('data-no-export'));
                return cells.map((cell) => csvCell(cell.innerText)).join(',');
            }).filter(Boolean);

            if (!lines.length) return;

            const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8;' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = button.dataset.exportFilename || 'export.csv';
            document.body.appendChild(link);
            link.click();
            link.remove();
            URL.revokeObjectURL(link.href);
        });
    });

    // Dashboard charts use a lightweight SVG renderer so one/few time buckets never
    // stretch into giant blocks. Users can switch among the most useful time-series views.
    const dashboardChartTypes = ['bar', 'line', 'area'];
    const svgNamespace = 'http://www.w3.org/2000/svg';

    const createSvgElement = (name, attributes = {}) => {
        const element = document.createElementNS(svgNamespace, name);
        Object.entries(attributes).forEach(([key, value]) => element.setAttribute(key, String(value)));
        return element;
    };

    const chartStorageGet = (key) => {
        try {
            const value = localStorage.getItem(key);
            return dashboardChartTypes.includes(value) ? value : 'bar';
        } catch (_) {
            return 'bar';
        }
    };

    const chartStorageSet = (key, value) => {
        try {
            localStorage.setItem(key, value);
        } catch (_) {
            // Storage can be unavailable in privacy-restricted browsers; the chart still works.
        }
    };

    const renderDashboardChart = (chart, model, type) => {
        const width = 680;
        const height = 240;
        const padding = { top: 14, right: 12, bottom: 34, left: 12 };
        const plotWidth = width - padding.left - padding.right;
        const plotHeight = height - padding.top - padding.bottom;
        const baseline = padding.top + plotHeight;
        const count = Math.max(1, model.labels.length);
        const bandWidth = plotWidth / count;
        const xFor = (index) => padding.left + (bandWidth * (index + 0.5));
        const yFor = (percentage) => baseline - ((Math.max(0, Math.min(100, percentage)) / 100) * plotHeight);

        const svg = createSvgElement('svg', {
            viewBox: `0 0 ${width} ${height}`,
            role: 'img',
            'aria-label': `${model.title} ${type} chart`,
            preserveAspectRatio: 'none',
        });
        svg.classList.add('mf-dashboard-chart-svg');

        for (let gridIndex = 0; gridIndex <= 4; gridIndex += 1) {
            const y = padding.top + ((plotHeight / 4) * gridIndex);
            svg.appendChild(createSvgElement('line', {
                x1: padding.left,
                x2: width - padding.right,
                y1: y,
                y2: y,
                class: gridIndex === 4 ? 'mf-dashboard-chart-baseline' : 'mf-dashboard-chart-gridline',
            }));
        }

        if (type === 'bar') {
            const gap = 6;
            const availableBand = Math.max(12, bandWidth - 16);
            const barWidth = Math.min(60, Math.max(6, (availableBand - (gap * (model.series.length - 1))) / model.series.length));
            const clusterWidth = (barWidth * model.series.length) + (gap * (model.series.length - 1));

            model.labels.forEach((_, pointIndex) => {
                model.series.forEach((series, seriesIndex) => {
                    const percentage = series.percentages[pointIndex] || 0;
                    const y = yFor(percentage);
                    const barHeight = percentage > 0 ? Math.max(3, baseline - y) : 0;
                    const x = xFor(pointIndex) - (clusterWidth / 2) + (seriesIndex * (barWidth + gap));
                    const bar = createSvgElement('rect', {
                        x,
                        y: baseline - barHeight,
                        width: barWidth,
                        height: barHeight,
                        rx: 3,
                        fill: series.color,
                        opacity: series.opacity,
                        class: 'mf-dashboard-chart-bar',
                    });
                    const title = createSvgElement('title');
                    title.textContent = model.tooltips[pointIndex] || `${model.labels[pointIndex]} · ${series.name}`;
                    bar.appendChild(title);
                    svg.appendChild(bar);
                });
            });
        } else {
            model.series.forEach((series) => {
                const points = model.labels.map((_, pointIndex) => ({
                    x: xFor(pointIndex),
                    y: yFor(series.percentages[pointIndex] || 0),
                }));

                if (type === 'area') {
                    if (points.length === 1) {
                        const point = points[0];
                        svg.appendChild(createSvgElement('rect', {
                            x: point.x - 18,
                            y: point.y,
                            width: 36,
                            height: Math.max(0, baseline - point.y),
                            rx: 3,
                            fill: series.color,
                            opacity: Math.min(0.16, series.opacity * 0.3),
                        }));
                    } else if (points.length > 1) {
                        const areaPath = [`M ${points[0].x} ${baseline}`, `L ${points[0].x} ${points[0].y}`]
                            .concat(points.slice(1).map((point) => `L ${point.x} ${point.y}`))
                            .concat([`L ${points[points.length - 1].x} ${baseline}`, 'Z'])
                            .join(' ');
                        svg.appendChild(createSvgElement('path', {
                            d: areaPath,
                            fill: series.color,
                            opacity: Math.min(0.16, series.opacity * 0.3),
                        }));
                    }
                }

                if (points.length > 1) {
                    const linePath = points.map((point, index) => `${index === 0 ? 'M' : 'L'} ${point.x} ${point.y}`).join(' ');
                    svg.appendChild(createSvgElement('path', {
                        d: linePath,
                        fill: 'none',
                        stroke: series.color,
                        'stroke-width': 2.5,
                        'stroke-linecap': 'round',
                        'stroke-linejoin': 'round',
                        opacity: Math.max(0.72, series.opacity),
                        class: 'mf-dashboard-chart-line',
                    }));
                }

                points.forEach((point, pointIndex) => {
                    const marker = createSvgElement('circle', {
                        cx: point.x,
                        cy: point.y,
                        r: 4,
                        fill: series.color,
                        stroke: 'var(--background)',
                        'stroke-width': 2,
                        opacity: Math.max(0.78, series.opacity),
                        class: 'mf-dashboard-chart-point',
                    });
                    const title = createSvgElement('title');
                    title.textContent = model.tooltips[pointIndex] || `${model.labels[pointIndex]} · ${series.name}`;
                    marker.appendChild(title);
                    svg.appendChild(marker);
                });
            });
        }

        const labelStep = Math.max(1, Math.ceil(model.labels.length / 7));
        model.labels.forEach((label, index) => {
            if (index % labelStep !== 0 && index !== model.labels.length - 1) return;
            const text = createSvgElement('text', {
                x: xFor(index),
                y: height - 10,
                'text-anchor': 'middle',
                class: 'mf-dashboard-chart-axis-label',
            });
            text.textContent = label;
            svg.appendChild(text);
        });

        chart.replaceChildren(svg);
        chart.dataset.dashboardChartType = type;
    };

    const enhanceDashboardChart = (chart, chartIndex) => {
        const card = chart.closest('.mf-card');
        const header = card?.querySelector('.mf-card-header');
        const title = header?.querySelector('.mf-card-title')?.textContent?.trim();
        if (!card || !header || !['Collections & Disbursements', 'Income vs Expenses'].includes(title)) return;

        const columns = [...chart.querySelectorAll('.mf-chart-col')];
        if (!columns.length) return;

        const footerItems = [...card.querySelector('.mf-card-body > .mt-3')?.querySelectorAll('span') || []];
        const seriesNames = footerItems.slice(0, 2).map((item) => item.textContent.replace('■', '').trim());
        const firstBars = [...columns[0].querySelectorAll('.mf-chart-bar')];
        const model = {
            title,
            labels: columns.map((column) => column.querySelector('.mf-chart-label')?.textContent?.trim() || ''),
            tooltips: columns.map((column) => column.getAttribute('title') || ''),
            series: firstBars.map((_, seriesIndex) => {
                const sample = firstBars[seriesIndex];
                const computed = getComputedStyle(sample);
                return {
                    name: seriesNames[seriesIndex] || `Series ${seriesIndex + 1}`,
                    color: computed.backgroundColor,
                    opacity: Number.parseFloat(computed.opacity) || 1,
                    percentages: columns.map((column) => {
                        const bar = column.querySelectorAll('.mf-chart-bar')[seriesIndex];
                        return Number.parseFloat(bar?.style.height || '0') || 0;
                    }),
                };
            }),
        };

        chart.style.minWidth = '0';
        chart.dataset.dashboardChartEnhanced = '1';
        header.classList.add('mf-dashboard-chart-header');

        const storageKey = `microfinance-dashboard-chart-${chartIndex}`;
        const switcher = document.createElement('div');
        switcher.className = 'mf-dashboard-chart-switcher';
        switcher.setAttribute('role', 'group');
        switcher.setAttribute('aria-label', `${title} chart type`);

        dashboardChartTypes.forEach((type) => {
            const button = document.createElement('button');
            button.type = 'button';
            button.className = 'mf-dashboard-chart-switch';
            button.dataset.dashboardChartSwitch = type;
            button.textContent = type.charAt(0).toUpperCase() + type.slice(1);
            button.addEventListener('click', () => {
                chartStorageSet(storageKey, type);
                renderDashboardChart(chart, model, type);
                switcher.querySelectorAll('[data-dashboard-chart-switch]').forEach((option) => {
                    const active = option.dataset.dashboardChartSwitch === type;
                    option.classList.toggle('is-active', active);
                    option.setAttribute('aria-pressed', active ? 'true' : 'false');
                });
            });
            switcher.appendChild(button);
        });

        header.appendChild(switcher);
        const initialType = chartStorageGet(storageKey);
        switcher.querySelector(`[data-dashboard-chart-switch="${initialType}"]`)?.click();
    };

    document.querySelectorAll('.mf-chart').forEach(enhanceDashboardChart);

    // Loan application documents already expose a protected download URL. Turn the
    // filename cell into an explicit review link without removing the download action.
    document.querySelectorAll('a[href*="/loans/"][href*="/documents/"][href$="/download"]').forEach((downloadLink) => {
        const downloadHref = downloadLink.getAttribute('href');
        if (!downloadHref) return;
        const reviewHref = downloadHref.replace(/\/download$/, '/review');
        if (reviewHref === downloadHref) return;

        const row = downloadLink.closest('tr');
        const fileCell = row?.children?.[1];
        if (!fileCell || fileCell.querySelector('a')) return;

        const reviewLink = document.createElement('a');
        reviewLink.href = reviewHref;
        reviewLink.target = '_blank';
        reviewLink.rel = 'noopener';
        reviewLink.className = 'font-medium text-primary hover:underline';
        reviewLink.title = 'Open document for review';
        reviewLink.textContent = fileCell.textContent.trim();
        fileCell.replaceChildren(reviewLink);
    });

    document.addEventListener('click', (event) => {
        document.querySelectorAll('.mf-columns-menu[open]').forEach((menu) => {
            if (!menu.contains(event.target)) menu.removeAttribute('open');
        });
    });
})();