(function () {
    'use strict';
    if (window.__ICY_COSTS_MODULE_ACTIVE) return;
    window.__ICY_COSTS_MODULE_ACTIVE = true;
    const listenerController = new AbortController();
    const on = (target, eventName, handler) => target.addEventListener(eventName, handler, { signal: listenerController.signal });
    window.ICY_PAGE_CLEANUPS = window.ICY_PAGE_CLEANUPS || [];
    window.ICY_PAGE_CLEANUPS.push(() => { listenerController.abort(); window.__ICY_COSTS_MODULE_ACTIVE = false; document.body.style.overflow = ''; });

    const state = { period: window.ICY_COSTS_INITIAL_PERIOD || new Date().toISOString().slice(0, 7), data: null, allocationSeq: 0, allocationAutomatic: false, allocationContext: 'manual', recurringCenterAutomatic: false };
    const $ = (id) => document.getElementById(id);
    const esc = (value) => String(value == null ? '' : value).replace(/[&<>'"]/g, (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;' }[char]));
    const money = (value) => '$' + Math.round(Number(value || 0)).toLocaleString('es-CL');
    const decimal = (value) => Number(value || 0).toFixed(2);
    const iconRefresh = () => { if (window.lucide && typeof window.lucide.createIcons === 'function') window.lucide.createIcons(); };
    const labels = {
        nature: { fixed: 'Fijo', variable: 'Variable', mixed: 'Mixto' },
        status: { draft: 'Borrador', approved: 'Aprobado', paid: 'Pagado', void: 'Anulado' },
        source: { manual: 'Manual', dte: 'DTE recibido', recurring: 'Recurrente', payroll: 'Costo empresa' },
        iva: { recoverable: 'Recuperable', nonrecoverable: 'No recuperable', no_iva: 'Sin IVA' },
        group: { materials: 'Materiales', labor: 'Mano de obra', services: 'Servicios', administration: 'Administración', sales: 'Ventas', marketing: 'Marketing', taxes: 'Impuestos', capex: 'Inversión', other: 'Otros' },
        cash: { operations: 'Operación', investment: 'Inversión', financing: 'Financiamiento' },
        frequency: { monthly: 'Mensual', quarterly: 'Trimestral', semiannual: 'Semestral', annual: 'Anual' },
        employment: { employee: 'Dependiente', fees: 'Honorarios', other: 'Otro' }
    };

    function showNotice(message, type) {
        const notice = $('costNotice');
        if (!notice) return;
        notice.hidden = !message;
        notice.textContent = message || '';
        notice.className = 'cost-notice' + (type ? ' is-' + type : '');
        if (message && type === 'success') window.setTimeout(() => { if (notice.textContent === message) notice.hidden = true; }, 5000);
    }

    async function request(url, options) {
        const response = await fetch(url, Object.assign({ credentials: 'same-origin' }, options || {}));
        const text = await response.text();
        let payload;
        try { payload = JSON.parse(text); } catch (error) { throw new Error(text || 'El servidor entregó una respuesta no válida.'); }
        if (!response.ok || payload.success === false) throw new Error(payload.message || 'No fue posible completar la operación.');
        return payload;
    }

    async function submitAction(input) {
        let formData;
        if (input instanceof HTMLFormElement) formData = new FormData(input);
        else {
            formData = new FormData();
            Object.keys(input || {}).forEach((key) => formData.append(key, input[key] == null ? '' : input[key]));
        }
        const token = document.querySelector('meta[name="csrf-token"]');
        if (token && !formData.has('_csrf_token')) formData.append('_csrf_token', token.content);
        return request('/modules/costs/action_ajax.php', { method: 'POST', body: formData });
    }

    async function loadData(successMessage) {
        showNotice('', '');
        try {
            const payload = await request('/modules/costs/data_ajax.php?period=' + encodeURIComponent(state.period));
            state.data = payload.data;
            renderAll();
            if (successMessage) showNotice(successMessage, 'success');
        } catch (error) {
            showNotice(error.message, 'error');
            const badge = $('periodStatusBadge');
            if (badge) { badge.textContent = 'No disponible'; badge.className = 'badge cost-period-status badge-error'; }
        }
    }

    function renderAll() {
        const data = state.data;
        if (!data) return;
        const badge = $('periodStatusBadge');
        badge.textContent = data.is_closed ? 'Período cerrado' : 'Período abierto';
        badge.className = 'badge cost-period-status ' + (data.is_closed ? 'badge-error' : 'badge-success');
        $('periodCloseBtn').innerHTML = data.is_closed ? '<i data-lucide="lock-open"></i><span>Reabrir período</span>' : '<i data-lucide="lock"></i><span>Cerrar período</span>';
        renderPermissions();
        renderKpis();
        renderBars('costByCategory', data.charts.by_category);
        renderBars('costByNature', data.charts.by_nature, labels.nature);
        renderBars('costByCenter', data.charts.by_center);
        renderEntries();
        renderRecurring();
        renderCategories();
        renderPayroll();
        fillSelectCatalogs();
        iconRefresh();
    }

    function renderPermissions() {
        const p = state.data.permissions || {};
        $('newCostBtn').hidden = !p.edit || state.data.is_closed;
        $('newCategoryBtn').hidden = !p.edit;
        $('newRecurringBtn').hidden = !p.edit;
        $('syncDteBtn').hidden = !p.approve || state.data.is_closed;
        $('generateRecurringBtn').hidden = !p.approve || state.data.is_closed;
        $('periodCloseBtn').hidden = !p.close;
        $('payrollTabButton').hidden = !p.payroll_view;
        $('newPayrollBtn').hidden = !p.payroll_edit || state.data.is_closed;
        if (!p.payroll_view && document.querySelector('[data-cost-tab="payroll"].is-active')) activateTab('overview');
    }

    function renderKpis() {
        const s = state.data.summary || {};
        const cards = [
            ['Costo gestión', money(s.management_total), 'Neto si IVA es recuperable'],
            ['Costos fijos', money(s.fixed_total), 'Estructura mensual'],
            ['Costos variables', money(s.variable_total), 'Asociados al nivel de actividad'],
            ['Costos mixtos', money(s.mixed_total), 'Componentes fijo y variable'],
            ['IVA trazado', money(s.tax_total), 'No sumado si es recuperable'],
            ['Por clasificar', Number(s.unclassified_count || 0).toLocaleString('es-CL'), Number(s.draft_count || 0) + ' borrador(es)']
        ];
        $('costKpis').innerHTML = cards.map((card) => '<article class="cost-kpi"><span>' + esc(card[0]) + '</span><strong>' + esc(card[1]) + '</strong><small>' + esc(card[2]) + '</small></article>').join('');
    }

    function renderBars(id, rows, labelMap) {
        const container = $(id);
        rows = Array.isArray(rows) ? rows : [];
        const max = Math.max(1, ...rows.map((row) => Number(row.amount || 0)));
        container.innerHTML = rows.length ? rows.map((row) => {
            const label = labelMap && labelMap[row.label] ? labelMap[row.label] : row.label;
            const width = Math.max(1.5, Number(row.amount || 0) * 100 / max);
            return '<div class="cost-bar-row"><div class="cost-bar-meta"><span>' + esc(label || 'Sin clasificación') + '</span><strong>' + esc(money(row.amount)) + '</strong></div><div class="cost-bar-track"><div class="cost-bar-fill" style="width:' + width.toFixed(2) + '%"></div></div></div>';
        }).join('') : '<div class="cost-empty">Sin datos para este período.</div>';
    }

    function statusBadge(status) {
        const css = status === 'paid' ? 'badge-success' : (status === 'void' ? 'badge-error' : (status === 'draft' ? '' : 'badge-info'));
        return '<span class="badge ' + css + '">' + esc(labels.status[status] || status) + '</span>';
    }

    function renderEntries() {
        const search = ($('entrySearch').value || '').trim().toLowerCase();
        const status = $('entryStatusFilter').value;
        const rows = (state.data.entries || []).filter((entry) => {
            if (status && entry.status !== status) return false;
            if (!search) return true;
            return [entry.description, entry.supplier_name, entry.category_name, entry.center_name].join(' ').toLowerCase().includes(search);
        });
        $('entriesSubtitle').textContent = rows.length + ' movimiento(s) · ' + Number(state.data.available_dte_count || 0) + ' DTE nuevo(s) disponible(s) para sincronizar.';
        $('entriesBody').innerHTML = rows.length ? rows.map((entry) => {
            const allocationLabel = entry.allocations && entry.allocations.length > 1 ? entry.allocations.length + ' centros' : (entry.center_name || (entry.allocations && entry.allocations[0] ? entry.allocations[0].center_name : 'Sin centro'));
            const canEdit = state.data.permissions.edit && !state.data.is_closed && entry.source_type !== 'payroll';
            const canVoid = canEdit && ['manual', 'recurring'].includes(entry.source_type) && entry.status !== 'void';
            const sourceDetail = (labels.source[entry.source_type] || entry.source_type) + (entry.source_type === 'dte' ? ' · ' + Number(entry.payment_count || 0) + ' pago(s)' : '');
            return '<tr><td><strong>' + esc(entry.entry_date) + '</strong><small>' + esc(sourceDetail) + '</small></td>' +
                '<td><strong>' + esc(entry.description) + '</strong><small>' + esc(entry.supplier_name || 'Sin proveedor') + '</small></td>' +
                '<td>' + esc(entry.category_code || '—') + '<small>' + esc(entry.category_name || 'Sin categoría') + '</small></td>' +
                '<td>' + esc(allocationLabel) + '</td><td>' + statusBadge(entry.status) + (entry.collection_status ? '<small>CxP · ' + esc(entry.collection_status) + '</small>' : '') + '</td><td class="number"><strong>' + esc(money(entry.management_cost_amount)) + '</strong><small>Neto ' + esc(money(entry.net_amount)) + ' · IVA ' + esc(money(entry.tax_amount)) + '</small></td>' +
                '<td><div class="cost-row-actions">' + (canEdit ? '<button class="btn btn-outline btn-sm" type="button" data-edit-entry="' + entry.id + '" title="Editar"><i data-lucide="pencil"></i></button>' : '') + (canVoid ? '<button class="btn btn-outline btn-sm" type="button" data-void-entry="' + entry.id + '" title="Anular"><i data-lucide="ban"></i></button>' : '') + '</div></td></tr>';
        }).join('') : '<tr><td colspan="7" class="cost-empty">No hay movimientos que coincidan con los filtros.</td></tr>';
    }

    function renderRecurring() {
        const rows = state.data.recurring_rules || [];
        $('recurringBody').innerHTML = rows.length ? rows.map((row) => '<tr><td><strong>' + esc(row.rule_name) + '</strong><small>' + esc(row.description) + '</small></td><td>' + esc(row.category_code + ' · ' + row.category_name) + '</td><td>' + esc(labels.frequency[row.frequency] || row.frequency) + '<small>Día ' + esc(row.day_of_month) + '</small></td><td>' + esc(row.start_period) + '<small>' + esc(row.end_period ? 'Hasta ' + row.end_period : 'Sin fecha final') + '</small></td><td class="number"><strong>' + esc(money(row.management_cost_amount)) + '</strong></td><td><span class="badge ' + (Number(row.is_active) ? 'badge-success' : '') + '">' + (Number(row.is_active) ? 'Activa' : 'Inactiva') + '</span></td><td><div class="cost-row-actions">' + (state.data.permissions.edit ? '<button class="btn btn-outline btn-sm" type="button" data-edit-recurring="' + row.id + '"><i data-lucide="pencil"></i></button>' : '') + '</div></td></tr>').join('') : '<tr><td colspan="7" class="cost-empty">Aún no hay reglas recurrentes.</td></tr>';
    }

    function renderCategories() {
        const rows = state.data.categories || [];
        $('categoriesBody').innerHTML = rows.length ? rows.map((row) => '<tr><td><strong>' + esc(row.category_code + ' · ' + row.category_name) + '</strong><small>' + esc(row.description || '') + '</small></td><td>' + esc(labels.nature[row.nature] || row.nature) + '</td><td>' + esc(labels.group[row.category_group] || row.category_group) + '</td><td>' + esc(labels.iva[row.default_iva_treatment] || row.default_iva_treatment) + '</td><td>' + esc(labels.cash[row.cash_activity] || row.cash_activity) + '</td><td>' + esc(row.default_center_name || 'Sin centro') + '</td><td><span class="badge ' + (Number(row.is_active) ? 'badge-success' : '') + '">' + (Number(row.is_active) ? 'Activa' : 'Inactiva') + '</span></td><td><div class="cost-row-actions">' + (state.data.permissions.edit ? '<button class="btn btn-outline btn-sm" type="button" data-edit-category="' + row.id + '"><i data-lucide="pencil"></i></button>' : '') + '</div></td></tr>').join('') : '<tr><td colspan="8" class="cost-empty">No hay categorías configuradas.</td></tr>';
    }

    function renderPayroll() {
        if (!state.data.permissions.payroll_view) { $('payrollBody').innerHTML = ''; return; }
        const rows = state.data.payroll || [];
        const total = rows.reduce((sum, row) => sum + Number(row.total_company_cost || 0), 0);
        $('payrollSubtitle').textContent = rows.length + ' registro(s) · costo empresa ' + money(total) + '.';
        $('payrollBody').innerHTML = rows.length ? rows.map((row) => '<tr><td><strong>' + esc(row.person_code + ' · ' + row.person_name) + '</strong></td><td>' + esc(row.role_name || 'Sin cargo') + '</td><td>' + esc(labels.employment[row.employment_type] || row.employment_type) + '</td><td>' + esc(row.center_name || 'Sin centro') + '</td><td>' + esc(row.effective_payment_date || row.expected_payment_date || 'Sin fecha') + '<small>' + (row.effective_payment_date ? 'Efectivo' : 'Previsto') + '</small></td><td class="number"><strong>' + esc(money(row.total_company_cost)) + '</strong></td><td><div class="cost-row-actions">' + (state.data.permissions.payroll_edit && !state.data.is_closed ? '<button class="btn btn-outline btn-sm" type="button" data-edit-payroll="' + row.id + '"><i data-lucide="pencil"></i></button>' : '') + '</div></td></tr>').join('') : '<tr><td colspan="7" class="cost-empty">No hay remuneraciones registradas para este período.</td></tr>';
    }

    function optionHtml(rows, valueKey, labelFn, selected, emptyLabel) {
        const empty = emptyLabel == null ? '' : '<option value="">' + esc(emptyLabel) + '</option>';
        return empty + (rows || []).map((row) => '<option value="' + esc(row[valueKey]) + '" ' + (String(row[valueKey]) === String(selected || '') ? 'selected' : '') + '>' + esc(labelFn(row)) + '</option>').join('');
    }

    function fillSelectCatalogs() {
        const activeCategories = (state.data.categories || []).filter((row) => Number(row.is_active));
        const categories = optionHtml(activeCategories, 'id', (row) => row.category_code + ' · ' + row.category_name, '', 'Seleccionar categoría');
        const suppliers = optionHtml(state.data.suppliers, 'id', (row) => row.display_name + (row.customer_tax_id ? ' · ' + row.customer_tax_id : ''), '', 'Sin proveedor');
        const centers = optionHtml(state.data.centers, 'id', (row) => row.center_name, '', 'Sin centro');
        ['entryCategory', 'recurringCategory'].forEach((id) => { if ($(id)) $(id).innerHTML = categories; });
        ['entrySupplier', 'recurringSupplier'].forEach((id) => { if ($(id)) $(id).innerHTML = suppliers; });
        ['categoryCenter', 'recurringCenter', 'payrollCenter'].forEach((id) => { if ($(id)) $(id).innerHTML = centers; });
        $('categoryGroup').innerHTML = Object.keys(labels.group).map((key) => '<option value="' + key + '">' + esc(labels.group[key]) + '</option>').join('');
    }

    function activateTab(tab) {
        document.querySelectorAll('[data-cost-tab]').forEach((button) => button.classList.toggle('is-active', button.dataset.costTab === tab));
        document.querySelectorAll('[data-cost-panel]').forEach((panel) => panel.classList.toggle('is-active', panel.dataset.costPanel === tab));
    }

    function openModal(id) { const modal = $(id); modal.classList.add('is-open'); modal.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; iconRefresh(); }
    function closeModal(id) { const modal = $(id); modal.classList.remove('is-open'); modal.setAttribute('aria-hidden', 'true'); if (!document.querySelector('.cost-modal-backdrop.is-open')) document.body.style.overflow = ''; }

    function setFormValue(id, value) { const element = $(id); if (element) element.value = value == null ? '' : value; }
    function todayForPeriod() { const today = new Date().toISOString().slice(0, 10); return today.slice(0, 7) === state.period ? today : state.period + '-01'; }

    function openEntry(entry) {
        $('entryForm').reset();
        setFormValue('entryId', entry ? entry.id : '');
        setFormValue('entryDescription', entry ? entry.description : '');
        setFormValue('entryDate', entry ? entry.entry_date : todayForPeriod());
        setFormValue('entryDueDate', entry ? entry.due_date : '');
        setFormValue('entryCategory', entry ? entry.category_id : '');
        setFormValue('entrySupplier', entry ? entry.supplier_id : '');
        setFormValue('entryIva', entry ? entry.iva_treatment : 'recoverable');
        setFormValue('entryStatus', entry ? entry.status : 'draft');
        setFormValue('entryNet', entry ? decimal(entry.net_amount) : '0.00');
        setFormValue('entryTax', entry ? decimal(entry.tax_amount) : '0.00');
        setFormValue('entryGross', entry ? decimal(entry.gross_amount) : '0.00');
        setFormValue('entryPaymentDate', entry ? entry.payment_date : '');
        setFormValue('entryNotes', entry ? entry.notes : '');
        $('entryModalTitle').textContent = entry ? 'Editar costo' : 'Nuevo costo';
        const lockedSource = entry && entry.source_type === 'dte';
        ['entryDescription', 'entryDate', 'entryDueDate', 'entryNet', 'entryTax', 'entryGross', 'entryPaymentDate'].forEach((id) => { $(id).readOnly = !!lockedSource; $(id).classList.toggle('is-readonly', !!lockedSource); });
        $('entrySupplier').disabled = !!lockedSource;
        $('entryStatus').disabled = !!lockedSource;
        state.allocationAutomatic = !entry || !(entry.allocations || []).length;
        state.allocationContext = lockedSource ? 'dte' : 'manual';
        renderAllocationEditor(entry ? entry.allocations : []);
        if (state.allocationAutomatic) applyAutomaticCenterSuggestion(); else showCenterSuggestion('Asignación manual conservada.');
        openModal('entryModal');
    }

    function allocationRow(allocation) {
        const wrapper = document.createElement('div');
        wrapper.className = 'allocation-row';
        wrapper.dataset.allocationRow = String(++state.allocationSeq);
        wrapper.innerHTML = '<select class="select allocation-center">' + optionHtml(state.data.centers, 'id', (row) => row.center_name, allocation ? allocation.finance_center_id : '', 'Seleccionar centro') + '</select><input class="input allocation-percent" type="number" min="0.01" max="100" step="0.01" value="' + esc(allocation ? allocation.allocation_percent : '') + '" placeholder="%"><button class="btn btn-outline" type="button" data-remove-allocation title="Quitar"><i data-lucide="x"></i></button>';
        return wrapper;
    }
    function renderAllocationEditor(rows) { $('allocationRows').innerHTML = ''; (rows || []).forEach((row) => $('allocationRows').appendChild(allocationRow(row))); updateAllocationTotal(); iconRefresh(); }
    function addAllocation() { state.allocationAutomatic = false; showCenterSuggestion('Asignación manual.'); $('allocationRows').appendChild(allocationRow({ allocation_percent: $('allocationRows').children.length ? '' : 100 })); updateAllocationTotal(); iconRefresh(); }
    function updateAllocationTotal() { const total = Array.from(document.querySelectorAll('.allocation-percent')).reduce((sum, input) => sum + Number(input.value || 0), 0); $('allocationTotal').textContent = total.toFixed(2).replace('.00', '') + '%'; $('allocationTotal').style.color = Math.abs(total - 100) < .01 || total === 0 ? 'var(--color-success)' : 'var(--color-error)'; }
    function collectAllocations() { return Array.from(document.querySelectorAll('[data-allocation-row]')).map((row) => ({ finance_center_id: row.querySelector('.allocation-center').value, allocation_percent: row.querySelector('.allocation-percent').value })).filter((row) => row.finance_center_id && Number(row.allocation_percent) > 0); }

    function showCenterSuggestion(message) { const element = $('entryCenterSuggestion'); if (element) element.textContent = message || ''; }
    function automaticCenterSuggestion(context) {
        return policyCenterSuggestion($('entrySupplier').value, $('entryCategory').value, context);
    }
    function policyCenterSuggestion(supplierId, categoryId, context) {
        const policy = state.data && state.data.assignment_policy ? state.data.assignment_policy : {};
        const flag = context === 'dte' ? 'auto_apply_dte' : (context === 'recurring' ? 'auto_apply_recurring' : 'auto_apply_manual_costs');
        if (!Number(policy[flag])) return null;
        const supplier = (state.data.suppliers || []).find((row) => String(row.id) === String(supplierId || ''));
        const category = (state.data.categories || []).find((row) => String(row.id) === String(categoryId || ''));
        const candidates = {
            supplier: supplier && supplier.supplier_default_cost_center_id ? { id: supplier.supplier_default_cost_center_id, source: 'proveedor' } : null,
            category: category && category.default_finance_center_id ? { id: category.default_finance_center_id, source: 'categoría' } : null,
            global: policy.global_cost_center_id ? { id: policy.global_cost_center_id, source: 'respaldo global' } : null
        };
        const order = policy.assignment_priority === 'category_supplier_global' ? ['category','supplier','global'] : ['supplier','category','global'];
        for (const key of order) if (candidates[key]) return candidates[key];
        return null;
    }
    function applyAutomaticCenterSuggestion() {
        if (!state.allocationAutomatic) return;
        const suggestion = automaticCenterSuggestion(state.allocationContext);
        renderAllocationEditor(suggestion ? [{ finance_center_id: suggestion.id, allocation_percent: 100 }] : []);
        showCenterSuggestion(suggestion ? 'Sugerencia automática por ' + suggestion.source + '.' : 'Sin coincidencia automática; puedes asignar un centro manualmente.');
    }

    function openCategory(row) {
        $('categoryForm').reset(); setFormValue('categoryId', row ? row.id : ''); setFormValue('categoryCode', row ? row.category_code : ''); setFormValue('categoryName', row ? row.category_name : ''); setFormValue('categoryNature', row ? row.nature : 'fixed'); setFormValue('categoryGroup', row ? row.category_group : 'other'); setFormValue('categoryIva', row ? row.default_iva_treatment : 'recoverable'); setFormValue('categoryCash', row ? row.cash_activity : 'operations'); setFormValue('categoryCenter', row ? row.default_finance_center_id : ''); setFormValue('categoryActive', row ? row.is_active : '1'); setFormValue('categoryDescription', row ? row.description : ''); $('categoryModalTitle').textContent = row ? 'Editar categoría' : 'Nueva categoría'; openModal('categoryModal');
    }

    function openRecurring(row) {
        $('recurringForm').reset(); const pairs = { recurringId: 'id', recurringName: 'rule_name', recurringDescription: 'description', recurringCategory: 'category_id', recurringSupplier: 'supplier_id', recurringCenter: 'finance_center_id', recurringIva: 'iva_treatment', recurringNet: 'net_amount', recurringTax: 'tax_amount', recurringGross: 'gross_amount', recurringFrequency: 'frequency', recurringDay: 'day_of_month', recurringStart: 'start_period', recurringEnd: 'end_period', recurringActive: 'is_active', recurringNotes: 'notes' }; Object.keys(pairs).forEach((id) => setFormValue(id, row ? row[pairs[id]] : '')); if (!row) { setFormValue('recurringIva', 'recoverable'); setFormValue('recurringFrequency', 'monthly'); setFormValue('recurringDay', '1'); setFormValue('recurringStart', state.period); setFormValue('recurringActive', '1'); setFormValue('recurringNet', '0'); setFormValue('recurringTax', '0'); setFormValue('recurringGross', '0'); } $('recurringModalTitle').textContent = row ? 'Editar recurrencia' : 'Nueva recurrencia'; openModal('recurringModal');
        state.recurringCenterAutomatic = !row || !row.finance_center_id;
        if (state.recurringCenterAutomatic) applyRecurringCenterSuggestion();
    }

    function applyRecurringCenterSuggestion() {
        if (!state.recurringCenterAutomatic) return;
        const suggestion = policyCenterSuggestion($('recurringSupplier').value, $('recurringCategory').value, 'recurring');
        $('recurringCenter').value = suggestion ? suggestion.id : '';
    }

    function openPayroll(row) {
        $('payrollForm').reset(); const pairs = { payrollId: 'id', payrollCode: 'person_code', payrollName: 'person_name', payrollRole: 'role_name', payrollType: 'employment_type', payrollBase: 'base_salary', payrollAllowances: 'allowances', payrollBonuses: 'bonuses', payrollCharges: 'employer_charges', payrollFees: 'fees', payrollOther: 'other_costs', payrollCenter: 'finance_center_id', payrollExpected: 'expected_payment_date', payrollEffective: 'effective_payment_date', payrollNotes: 'notes' }; Object.keys(pairs).forEach((id) => setFormValue(id, row ? row[pairs[id]] : '')); ['payrollBase','payrollAllowances','payrollBonuses','payrollCharges','payrollFees','payrollOther'].forEach((id) => { if (!$(id).value) $(id).value = '0'; }); setFormValue('payrollPeriod', state.period); updatePayrollTotal(); openModal('payrollModal');
        if (!row && !$('payrollCenter').value) { const category = (state.data.categories || []).find((item) => item.category_code === 'REM'); const suggestion = policyCenterSuggestion(0, category ? category.id : 0, 'manual'); if (suggestion) $('payrollCenter').value = suggestion.id; }
    }
    function updatePayrollTotal() { const total = Array.from(document.querySelectorAll('.payroll-amount')).reduce((sum, input) => sum + Number(input.value || 0), 0); $('payrollTotal').textContent = money(total); }

    async function handleFormSubmit(event) {
        event.preventDefault();
        const form = event.currentTarget;
        const submit = document.querySelector('[form="' + form.id + '"][type="submit"]');
        if (form.id === 'entryForm') $('entryAllocationsJson').value = JSON.stringify(collectAllocations());
        if (submit) submit.disabled = true;
        try {
            await submitAction(form);
            const modal = form.closest('.cost-modal-backdrop');
            if (modal) closeModal(modal.id);
            await loadData('Información guardada correctamente.');
        } catch (error) { showNotice(error.message, 'error'); }
        finally { if (submit) submit.disabled = false; }
    }

    async function simpleAction(action, confirmation, notesPrompt, extra) {
        const actionsMenu = $('costActionsMenu');
        if (actionsMenu) actionsMenu.removeAttribute('open');
        if (confirmation && !window.confirm(confirmation)) return;
        const payload = Object.assign({ action, period: state.period }, extra || {});
        if (notesPrompt) { const notes = window.prompt(notesPrompt, ''); if (notes === null) return; payload.notes = notes; }
        try {
            const result = await submitAction(payload);
            let message = 'Operación completada correctamente.';
            if (action === 'sync_dte') message = Number(result.data && result.data.processed || 0) + ' DTE procesado(s) sin duplicar costos.';
            if (action === 'generate_recurring') message = Number(result.data && result.data.created || 0) + ' costo(s) recurrente(s) generado(s).';
            await loadData(message);
        } catch (error) { showNotice(error.message, 'error'); }
    }

    on(document, 'click', (event) => {
        const tab = event.target.closest('[data-cost-tab]'); if (tab) { activateTab(tab.dataset.costTab); return; }
        const close = event.target.closest('[data-close-modal]'); if (close) { closeModal(close.dataset.closeModal); return; }
        if (event.target.classList.contains('cost-modal-backdrop')) { closeModal(event.target.id); return; }
        const removeAllocation = event.target.closest('[data-remove-allocation]'); if (removeAllocation) { state.allocationAutomatic = false; showCenterSuggestion('Asignación manual.'); removeAllocation.closest('[data-allocation-row]').remove(); updateAllocationTotal(); return; }
        const editEntry = event.target.closest('[data-edit-entry]'); if (editEntry) { openEntry((state.data.entries || []).find((row) => String(row.id) === editEntry.dataset.editEntry)); return; }
        const editCategory = event.target.closest('[data-edit-category]'); if (editCategory) { openCategory((state.data.categories || []).find((row) => String(row.id) === editCategory.dataset.editCategory)); return; }
        const editRecurring = event.target.closest('[data-edit-recurring]'); if (editRecurring) { openRecurring((state.data.recurring_rules || []).find((row) => String(row.id) === editRecurring.dataset.editRecurring)); return; }
        const editPayroll = event.target.closest('[data-edit-payroll]'); if (editPayroll) { openPayroll((state.data.payroll || []).find((row) => String(row.id) === editPayroll.dataset.editPayroll)); return; }
        const voidEntry = event.target.closest('[data-void-entry]'); if (voidEntry) { simpleAction('void_entry', '¿Anular este costo? Permanecerá disponible para auditoría.', false, { id: voidEntry.dataset.voidEntry }); return; }
    });

    on($('newCostBtn'), 'click', () => openEntry(null));
    on($('newCategoryBtn'), 'click', () => openCategory(null));
    on($('newRecurringBtn'), 'click', () => openRecurring(null));
    on($('newPayrollBtn'), 'click', () => openPayroll(null));
    on($('addAllocationBtn'), 'click', addAllocation);
    on($('allocationRows'), 'input', () => { state.allocationAutomatic = false; showCenterSuggestion('Asignación manual.'); updateAllocationTotal(); });
    on($('allocationRows'), 'change', () => { state.allocationAutomatic = false; showCenterSuggestion('Asignación manual.'); updateAllocationTotal(); });
    on($('entrySearch'), 'input', renderEntries);
    on($('entryStatusFilter'), 'change', renderEntries);
    on($('costPeriod'), 'change', () => { state.period = $('costPeriod').value; const url = new URL(window.location.href); url.searchParams.set('period', state.period); window.history.replaceState({}, '', url); loadData(); });
    on($('syncDteBtn'), 'click', () => simpleAction('sync_dte', '¿Sincronizar las cuentas por pagar productivas del período? Los DTE ya vinculados se actualizarán sin duplicarse.', false));
    on($('generateRecurringBtn'), 'click', () => simpleAction('generate_recurring', '¿Generar las recurrencias que correspondan a este período?', false));
    on($('periodCloseBtn'), 'click', () => simpleAction(state.data.is_closed ? 'reopen_period' : 'close_period', state.data.is_closed ? '¿Reabrir este período para permitir modificaciones?' : '¿Cerrar este período? Después no se podrá modificar hasta reabrirlo.', 'Agrega una nota para la bitácora del período:'));
    ['entryForm', 'categoryForm', 'recurringForm', 'payrollForm'].forEach((id) => on($(id), 'submit', handleFormSubmit));
    document.querySelectorAll('.payroll-amount').forEach((input) => on(input, 'input', updatePayrollTotal));
    ['entryNet', 'entryTax'].forEach((id) => on($(id), 'input', () => { $('entryGross').value = decimal(Number($('entryNet').value || 0) + Number($('entryTax').value || 0)); }));
    ['recurringNet', 'recurringTax'].forEach((id) => on($(id), 'input', () => { $('recurringGross').value = decimal(Number($('recurringNet').value || 0) + Number($('recurringTax').value || 0)); }));
    on($('entryCategory'), 'change', () => { const category = (state.data.categories || []).find((row) => String(row.id) === $('entryCategory').value); if (category) $('entryIva').value = category.default_iva_treatment; if (state.allocationAutomatic) applyAutomaticCenterSuggestion(); });
    on($('recurringCategory'), 'change', () => { const category = (state.data.categories || []).find((row) => String(row.id) === $('recurringCategory').value); if (category) $('recurringIva').value = category.default_iva_treatment; if (state.recurringCenterAutomatic) applyRecurringCenterSuggestion(); });
    on($('recurringSupplier'), 'change', () => { if (state.recurringCenterAutomatic) applyRecurringCenterSuggestion(); });
    on($('recurringCenter'), 'change', () => { state.recurringCenterAutomatic = false; });
    on($('entrySupplier'), 'change', () => { if (state.allocationAutomatic) applyAutomaticCenterSuggestion(); });

    on(document, 'keydown', (event) => { if (event.key === 'Escape') { const open = document.querySelector('.cost-modal-backdrop.is-open'); if (open) closeModal(open.id); } });

    loadData();
})();
