(function () {
    'use strict';

    const __pageBindings = [];
    const on = (target, eventName, handler, options) => {
        if (!target || typeof target.addEventListener !== 'function') return;
        target.addEventListener(eventName, handler, options || false);
        __pageBindings.push([target, eventName, handler, options || false]);
    };
    window.ICY_PAGE_CLEANUPS = window.ICY_PAGE_CLEANUPS || [];
    window.ICY_PAGE_CLEANUPS.push(() => {
        __pageBindings.forEach(([target, eventName, handler, options]) => {
            try { target.removeEventListener(eventName, handler, options); } catch (_) {}
        });
    });

    const boot = window.ICY_MANUAL_BOOTSTRAP || {};
    const requestedType = window.ICY_MANUAL_REQUESTED_TYPE || '';
    const $ = (id) => document.getElementById(id);
    const esc = (value) => String(value == null ? '' : value).replace(/[&<>'"]/g, (ch) => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#039;','"':'&quot;'}[ch]));
    const typeMap = Object.fromEntries((boot.document_types || []).map((row) => [row.document_key, row]));
    const money = (value, currency) => new Intl.NumberFormat('es-CL', {
        minimumFractionDigits: currency && currency !== 'CLP' ? 2 : 0,
        maximumFractionDigits: currency && currency !== 'CLP' ? 2 : 0
    }).format(Number(value || 0));
    const today = () => new Date().toISOString().slice(0, 10);
    const uniqueKey = () => `manual-${Date.now()}-${Math.random().toString(36).slice(2, 12)}`;
    const isNote = (key) => ['credit_note_electronic','debit_note_electronic','credit_note_export_electronic','debit_note_export_electronic'].includes(key);
    const isExport = (key) => ['invoice_export_electronic','credit_note_export_electronic','debit_note_export_electronic'].includes(key);
    const isGuide = (key) => key === 'dispatch_guide_electronic';
    const isReceipt = (key) => ['receipt_electronic','receipt_exempt_electronic'].includes(key);
    const isInvoice = (key) => ['invoice_electronic','invoice_exempt_electronic','invoice_export_electronic'].includes(key);
    const isCredit = (key) => key.indexOf('credit_note') === 0;

    const modal = $('manualIssuerModal');
    const content = $('manualLayerContent');
    const footer = $('manualModalFooter');
    const title = $('manualModalTitle');
    const subtitle = $('manualModalSubtitle');
    const backBtn = $('manualBackBtn');
    const closeBtn = $('manualCloseBtn');
    const documentActions = Array.from(document.querySelectorAll('.manual-document-action[data-document-key]'));

    let customerSearchTimer = null;
    let productSearchTimer = null;

    const state = {
        documentKey: '',
        manualKey: '',
        step: '',
        history: [],
        form: {},
        receiver: {},
        customer: null,
        lines: [],
        source: null,
        preview: null,
        result: null
    };

    function typeLabel(key) {
        return (typeMap[key] && typeMap[key].document_label) || key;
    }

    function emptyLine() {
        return {
            product_id: null,
            sku: '',
            description: '',
            long_description: '',
            quantity: 1,
            unit_measure: 'UN',
            unit_price: 0,
            discount_type: 'amount',
            discount_value: 0,
            tax_treatment: state.documentKey === 'invoice_exempt_electronic' || state.documentKey === 'invoice_export_electronic' ? 'exento' : 'afecto',
            finance_center_id: state.form.finance_center_id || ''
        };
    }

    async function requestJson(url, options) {
        const response = await fetch(url, options || {headers: {'X-Requested-With': 'XMLHttpRequest'}});
        let data;
        try {
            data = await response.json();
        } catch (_) {
            throw new Error('El servidor devolvió una respuesta no válida.');
        }
        if (!response.ok || !data.success) {
            throw new Error(data.message || 'La operación no pudo completarse.');
        }
        if (Object.prototype.hasOwnProperty.call(data, 'result')) return data.result;
        if (Object.prototype.hasOwnProperty.call(data, 'data')) return data.data;
        return data;
    }


    // Misma respuesta simple utilizada por los buscadores de Cotizaciones.
    async function requestSearchArray(url) {
        const response = await fetch(url, {
            credentials: 'same-origin',
            headers: {'X-Requested-With': 'XMLHttpRequest'}
        });
        let data;
        try {
            data = await response.json();
        } catch (_) {
            throw new Error('El buscador devolvió una respuesta no válida.');
        }
        if (!response.ok || !Array.isArray(data)) {
            throw new Error('No fue posible completar la búsqueda.');
        }
        return data;
    }

    function setError(message) {
        const box = $('manualLayerError');
        if (!box) return;
        box.textContent = message || 'Ocurrió un error.';
        box.style.display = 'block';
        box.scrollIntoView({block: 'nearest', behavior: 'smooth'});
    }

    function clearError() {
        const box = $('manualLayerError');
        if (box) {
            box.textContent = '';
            box.style.display = 'none';
        }
    }

    function activateStep(step) {
        document.querySelectorAll('#manualStepbar [data-step]').forEach((node) => {
            node.classList.toggle('is-active', node.dataset.step === step);
        });
    }

    function setLayer(step, options) {
        options = options || {};
        if (!options.replace && state.step && state.step !== step) {
            state.history.push(state.step);
        }
        state.step = step;
        activateStep(step);
        backBtn.style.visibility = state.history.length ? 'visible' : 'hidden';
        if (step === 'document') renderDocumentStep();
        if (step === 'detail') renderDetailStep();
        if (step === 'preview') renderPreviewStep();
        if (step === 'confirm') renderConfirmStep();
        if (step === 'result') renderResultStep();
        const body = modal.querySelector('.manual-modal-body');
        if (body) body.scrollTop = 0;
        if (window.lucide) window.lucide.createIcons();
    }

    function goBack() {
        if (!state.history.length) return;
        const previous = state.history.pop();
        state.step = previous;
        activateStep(previous);
        backBtn.style.visibility = state.history.length ? 'visible' : 'hidden';
        if (previous === 'document') renderDocumentStep();
        if (previous === 'detail') renderDetailStep();
        if (previous === 'preview') renderPreviewStep();
        if (previous === 'confirm') renderConfirmStep();
        if (window.lucide) window.lucide.createIcons();
    }

    function openIssuer(key) {
        const type = typeMap[key];
        if (!type || !type.environment_enabled) return;
        state.documentKey = key;
        state.manualKey = uniqueKey();
        state.step = '';
        state.history = [];
        state.form = {
            environment: boot.environment === 'sandbox' ? 'sandbox' : 'production',
            issue_date: today(),
            payment_terms_days: '0',
            finance_center_id: '',
            observations: '',
            global_discount_type: 'amount',
            global_discount_value: 0,
            guide_type: '1',
            transfer_type: '2',
            currency_code: 'USD',
            exchange_rate_value: '',
            exchange_rate_date: '',
            exchange_rate_source: '',
            exchange_rate_status: '',
            sale_modality: '1',
            sale_clause: '6',
            purchase_order_number: '',
            purchase_order_date: today(),
            create_customer: false
        };
        state.receiver = {
            legal_name: '', tax_id: '', tax_id_type: isExport(key) ? '' : 'CL-RUT', country_id: isExport(key) ? '' : '253',
            address: '', district: '', city: '', phone: '', activity: '', email: ''
        };
        state.customer = null;
        state.lines = [];
        state.source = null;
        state.preview = null;
        state.result = null;
        modal.style.display = 'flex';
        modal.classList.add('show');
        modal.setAttribute('aria-hidden', 'false');
        document.body.classList.add('is-modal-open');
        title.textContent = 'Emitir documento tributario';
        subtitle.textContent = typeLabel(key);
        setLayer('document', {replace: true});
    }

    function closeIssuer() {
        modal.classList.remove('show');
        modal.setAttribute('aria-hidden', 'true');
        window.setTimeout(() => { if (!modal.classList.contains('show')) modal.style.display = 'none'; }, 120);
        document.body.classList.remove('is-modal-open');
    }

    function optionRows(items, valueKey, labelFn, selected) {
        return (items || []).map((item) => {
            const value = item[valueKey];
            return `<option value="${esc(value)}" ${String(value) === String(selected == null ? '' : selected) ? 'selected' : ''}>${esc(labelFn(item))}</option>`;
        }).join('');
    }

    function paymentOptions(selected) {
        return optionRows(boot.payment_terms, 'days', (item) => item.term_label || item.term_key || `${item.days} días`, selected);
    }

    function centerOptions(selected) {
        const centers = boot.finance_centers || boot.income_centers || [];
        return `<option value="">Sin centro de ingreso</option>` + optionRows(centers, 'id', (item) => `${item.center_code ? item.center_code + ' · ' : ''}${item.center_name}`, selected);
    }

    async function refreshManualExchangeRate() {
        if (state.documentKey !== 'invoice_export_electronic') return;
        const currencyEl = $('miCurrency');
        const dateEl = $('miIssueDate');
        const rateEl = $('miExchange');
        const metaEl = $('miExchangeMeta');
        if (!currencyEl || !dateEl || !rateEl || !dateEl.value) return;
        const currency = String(currencyEl.value || 'USD').toUpperCase();
        if (currency === 'CLP') {
            rateEl.value = '1';
            rateEl.readOnly = true;
            state.form.exchange_rate_value = '1';
            state.form.exchange_rate_date = dateEl.value;
            state.form.exchange_rate_source = 'CLP';
            state.form.exchange_rate_status = 'validated';
            if (metaEl) metaEl.textContent = 'CLP · sin conversión';
            return;
        }
        rateEl.readOnly = false;
        rateEl.setAttribute('aria-busy', 'true');
        if (metaEl) metaEl.textContent = 'Consultando Banco Central de Chile vía Findic…';
        try {
            const body = new URLSearchParams({currency_code: currency, date: dateEl.value});
            const result = await requestJson('/modules/billing/exchange_rate_ajax.php', {
                method: 'POST',
                headers: {'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'},
                body: body.toString()
            });
            rateEl.value = Number(result.rate_to_clp || 0).toFixed(6).replace(/0+$/, '').replace(/\.$/, '');
            state.form.exchange_rate_value = rateEl.value;
            state.form.exchange_rate_date = result.rate_date || dateEl.value;
            state.form.exchange_rate_source = result.source || 'FINDIC_BCCH';
            state.form.exchange_rate_status = result.quality_status || 'validated';
            if (metaEl) metaEl.textContent = `Banco Central vía Findic · ${state.form.exchange_rate_date}${result.is_fallback_date ? ' · último día hábil disponible' : ''}`;
        } catch (error) {
            if (metaEl) metaEl.textContent = 'Findic no disponible. Puedes informar una tasa manual autorizada.';
            if (!rateEl.value) rateEl.placeholder = 'Ingresa tasa manual';
        } finally {
            rateEl.removeAttribute('aria-busy');
        }
    }

    function renderDocumentStep() {
        title.textContent = 'Preparar documento';
        subtitle.textContent = `${typeLabel(state.documentKey)} · emisión manual`;
        const payment = isInvoice(state.documentKey)
            ? `<label class="manual-field crm-ui-field"><span>Condición de pago</span><select class="input" id="miPaymentTerms">${paymentOptions(state.form.payment_terms_days)}</select></label>`
            : '';
        const center = !isNote(state.documentKey)
            ? `<label class="manual-field crm-ui-field"><span>Centro de ingreso</span><select class="input" id="miFinanceCenter">${centerOptions(state.form.finance_center_id)}</select></label>`
            : '';
        const purchaseOrder = (isInvoice(state.documentKey) || isGuide(state.documentKey))
            ? `<label class="manual-field crm-ui-field"><span>Orden de compra (opcional)</span><input class="input" id="miPurchaseOrderNumber" maxlength="60" value="${esc(state.form.purchase_order_number)}" placeholder="Ej. OC-2026-001"></label><label class="manual-field crm-ui-field"><span>Fecha orden de compra</span><input class="input" id="miPurchaseOrderDate" type="date" value="${esc(state.form.purchase_order_date || state.form.issue_date)}"></label>`
            : '';
        let special = '';
        if (isGuide(state.documentKey)) {
            special += `<label class="manual-field crm-ui-field"><span>Tipo de guía</span><select class="input" id="miGuideType">${optionRows(boot.guide_types, 'id', (x) => x.label, state.form.guide_type)}</select></label>`;
            special += `<label class="manual-field crm-ui-field"><span>Modalidad de traslado</span><select class="input" id="miTransferType">${optionRows(boot.transfer_types, 'id', (x) => x.label, state.form.transfer_type)}</select></label>`;
        }
        if (state.documentKey === 'invoice_export_electronic') {
            special += `<label class="manual-field crm-ui-field"><span>Moneda</span><select class="input" id="miCurrency">${optionRows(boot.currencies, 'currency_code', (x) => `${x.currency_code} · ${x.currency_name}`, state.form.currency_code)}</select></label>`;
            special += `<label class="manual-field crm-ui-field"><span>Tipo de cambio a CLP</span><input class="input" id="miExchange" type="number" min="0.000001" step="0.000001" value="${esc(state.form.exchange_rate_value)}" placeholder="Consultando Findic…"><small id="miExchangeMeta" class="text-secondary">${state.form.exchange_rate_source ? esc(state.form.exchange_rate_source + ' · ' + state.form.exchange_rate_date) : 'Banco Central de Chile vía Findic'}</small></label>`;
            special += `<label class="manual-field crm-ui-field"><span>Modalidad de venta</span><select class="input" id="miSaleModality">
                <option value="1" ${state.form.sale_modality === '1' ? 'selected' : ''}>Venta acordada</option>
                <option value="2" ${state.form.sale_modality === '2' ? 'selected' : ''}>Venta bajo condición</option>
                <option value="3" ${state.form.sale_modality === '3' ? 'selected' : ''}>Consignación</option>
                <option value="4" ${state.form.sale_modality === '4' ? 'selected' : ''}>Sin pago</option>
                <option value="9" ${state.form.sale_modality === '9' ? 'selected' : ''}>Otra</option>
            </select></label>`;
            special += `<label class="manual-field crm-ui-field"><span>Cláusula de venta</span><select class="input" id="miSaleClause">
                <option value="6" ${state.form.sale_clause === '6' ? 'selected' : ''}>Sin cláusula</option>
                <option value="1" ${state.form.sale_clause === '1' ? 'selected' : ''}>CIF</option>
                <option value="2" ${state.form.sale_clause === '2' ? 'selected' : ''}>CFR</option>
                <option value="3" ${state.form.sale_clause === '3' ? 'selected' : ''}>EXW</option>
                <option value="4" ${state.form.sale_clause === '4' ? 'selected' : ''}>FAS</option>
                <option value="5" ${state.form.sale_clause === '5' ? 'selected' : ''}>FOB</option>
                <option value="9" ${state.form.sale_clause === '9' ? 'selected' : ''}>DDP</option>
                <option value="10" ${state.form.sale_clause === '10' ? 'selected' : ''}>FCA</option>
                <option value="11" ${state.form.sale_clause === '11' ? 'selected' : ''}>CPT</option>
                <option value="12" ${state.form.sale_clause === '12' ? 'selected' : ''}>CIP</option>
                <option value="17" ${state.form.sale_clause === '17' ? 'selected' : ''}>DAT</option>
                <option value="18" ${state.form.sale_clause === '18' ? 'selected' : ''}>DAP</option>
            </select></label>`;
        }
        content.innerHTML = `
            <div id="manualLayerError" class="manual-alert manual-alert-error" style="display:none"></div>
            <section class="manual-panel crm-ui-section">
                <h3>Datos del documento</h3>
                <div class="manual-grid crm-ui-form-grid">
                    <label class="manual-field crm-ui-field"><span>Tipo</span><div class="input" style="display:flex;align-items:center">${esc(typeLabel(state.documentKey))}</div></label>
                    <label class="manual-field crm-ui-field"><span>Fecha de emisión</span><input class="input" id="miIssueDate" type="date" value="${esc(state.form.issue_date)}"></label>
                    ${payment}${center}${purchaseOrder}${special}
                </div>
                <label class="manual-field crm-ui-field" style="margin-top:14px"><span>Observaciones</span><textarea class="input" id="miObservations" rows="3" maxlength="250">${esc(state.form.observations)}</textarea></label>
            </section>`;
        footer.innerHTML = `<button type="button" class="btn btn-primary" id="miDocumentContinue"><span>Continuar</span><i data-lucide="arrow-right"></i></button>`;
        const currencyInput = $('miCurrency');
        const issueDateInput = $('miIssueDate');
        if (currencyInput) currencyInput.addEventListener('change', refreshManualExchangeRate);
        if (issueDateInput && state.documentKey === 'invoice_export_electronic') issueDateInput.addEventListener('change', refreshManualExchangeRate);
        if (state.documentKey === 'invoice_export_electronic' && (!state.form.exchange_rate_value || !state.form.exchange_rate_date)) {
            window.setTimeout(refreshManualExchangeRate, 0);
        }
        $('miDocumentContinue').addEventListener('click', () => {
            try {
                state.form.issue_date = $('miIssueDate').value;
                if (!state.form.issue_date) throw new Error('Debes indicar la fecha de emisión.');
                if ($('miPaymentTerms')) state.form.payment_terms_days = $('miPaymentTerms').value;
                if ($('miFinanceCenter')) state.form.finance_center_id = $('miFinanceCenter').value;
                if ($('miGuideType')) state.form.guide_type = $('miGuideType').value;
                if ($('miTransferType')) state.form.transfer_type = $('miTransferType').value;
                if ($('miCurrency')) state.form.currency_code = $('miCurrency').value;
                if ($('miExchange')) state.form.exchange_rate_value = $('miExchange').value;
                if ($('miSaleModality')) state.form.sale_modality = $('miSaleModality').value;
                if ($('miSaleClause')) state.form.sale_clause = $('miSaleClause').value;
                if ($('miPurchaseOrderNumber')) state.form.purchase_order_number = $('miPurchaseOrderNumber').value.trim();
                if ($('miPurchaseOrderDate')) state.form.purchase_order_date = $('miPurchaseOrderDate').value || state.form.issue_date;
                state.form.observations = $('miObservations').value.trim();
                if (state.documentKey === 'invoice_export_electronic' && state.form.currency_code !== 'CLP' && Number(state.form.exchange_rate_value) <= 0) {
                    throw new Error('Debes indicar un tipo de cambio mayor a cero.');
                }
                setLayer('detail');
            } catch (error) {
                setError(error.message);
            }
        });
    }

    function renderDetailStep() {
        if (isNote(state.documentKey)) {
            renderNoteStep();
            return;
        }
        title.textContent = 'Receptor y detalle';
        subtitle.textContent = `${typeLabel(state.documentKey)} · emisión manual`;
        const exportDocument = state.documentKey === 'invoice_export_electronic';
        content.innerHTML = `
            <div id="manualLayerError" class="manual-alert manual-alert-error" style="display:none"></div>
            <section class="manual-panel crm-ui-section">
                <h3>Receptor</h3>
                <div class="manual-quote-selector-row">
                    <div class="manual-autocomplete-wrap">
                        <label class="manual-field crm-ui-field"><span>Cliente</span><input type="search" class="input" id="miCustomerSearch" placeholder="Buscar cliente por nombre o RUT" autocomplete="off"></label>
                        <div id="miCustomerResults" class="manual-search-results manual-autocomplete-results"></div>
                    </div>
                    <button type="button" class="btn btn-outline" id="miNewCustomerBtn"><i data-lucide="user-plus"></i><span>Crear cliente</span></button>
                </div>
                <div id="miSelectedCustomer" style="margin-top:12px">${selectedCustomerHtml()}</div>
                <div class="manual-grid crm-ui-form-grid" style="margin-top:14px">
                    ${exportDocument ? exportReceiverFields() : nationalReceiverFields()}
                </div>
                ${state.customer ? '<div class="manual-master-data-note">Datos cargados desde la ficha del cliente. Para modificarlos, edita primero al cliente o quita la selección.</div>' : '<label style="display:flex;align-items:center;gap:9px;margin-top:14px"><input type="checkbox" id="miCreateCustomer" ' + (state.form.create_customer ? 'checked' : '') + '> <span>Guardar este receptor como cliente al emitir</span></label>'}
            </section>
            ${isGuide(state.documentKey) ? guideFields() : ''}
            <section class="manual-panel crm-ui-section">
                <h3>Productos y glosas</h3>
                <div class="manual-quote-selector-row manual-product-selector-row">
                    <div class="manual-autocomplete-wrap">
                        <label class="manual-field crm-ui-field"><span>Productos</span><input type="search" class="input" id="miProductSearch" placeholder="Buscar producto por nombre o SKU" autocomplete="off"></label>
                        <div id="miProductResults" class="manual-search-results manual-autocomplete-results"></div>
                    </div>
                    <button type="button" class="btn btn-outline" id="miAddManualLine"><i data-lucide="plus"></i><span>Manual</span></button>
                </div>
                <div class="manual-lines-wrap crm-ui-inner-table-wrap"><table class="manual-lines-table manual-lines-table-quote crm-ui-inner-table"><thead><tr><th>Producto</th><th>SKU</th><th>Cant.</th><th>Unidad</th><th>Precio U. neto</th><th>Total fila</th><th></th></tr></thead><tbody id="miLinesBody"></tbody></table></div>
                <div class="manual-grid manual-grid-2 crm-ui-form-grid" style="margin-top:14px">
                    <label class="manual-field crm-ui-field"><span>Tipo descuento global</span><select class="input" id="miGlobalDiscountType"><option value="amount" ${state.form.global_discount_type === 'amount' ? 'selected' : ''}>Monto</option><option value="percent" ${state.form.global_discount_type === 'percent' ? 'selected' : ''}>Porcentaje</option></select></label>
                    <label class="manual-field crm-ui-field"><span>Valor descuento global</span><input class="input" id="miGlobalDiscountValue" type="number" min="0" step="0.01" value="${esc(state.form.global_discount_value || 0)}"></label>
                </div>
                <div class="manual-total-grid crm-ui-summary-grid" id="miTotals"></div>
            </section>`;
        footer.innerHTML = `<button type="button" class="btn btn-primary" id="miPreviewBtn"><i data-lucide="file-search"></i><span>Previsualizar</span></button>`;

        $('miCustomerSearch').addEventListener('input', () => {
            clearTimeout(customerSearchTimer);
            customerSearchTimer = setTimeout(searchCustomers, 220);
        });
        $('miCustomerSearch').addEventListener('keydown', (event) => { if (event.key === 'Enter') { event.preventDefault(); clearTimeout(customerSearchTimer); searchCustomers(); } });
        $('miNewCustomerBtn').addEventListener('click', useNewCustomer);
        $('miProductSearch').addEventListener('input', () => {
            clearTimeout(productSearchTimer);
            productSearchTimer = setTimeout(searchProducts, 220);
        });
        $('miProductSearch').addEventListener('keydown', (event) => { if (event.key === 'Enter') { event.preventDefault(); clearTimeout(productSearchTimer); searchProducts(); } });
        $('miAddManualLine').addEventListener('click', () => { state.lines.push(emptyLine()); renderLines(); });
        if ($('miCreateCustomer')) $('miCreateCustomer').addEventListener('change', () => { state.form.create_customer = $('miCreateCustomer').checked; });
        $('miGlobalDiscountType').addEventListener('change', syncDetailState);
        $('miGlobalDiscountValue').addEventListener('input', syncDetailState);
        $('miPreviewBtn').addEventListener('click', previewCurrent);
        bindReceiverFields();
        renderLines();
    }

    function selectedCustomerHtml() {
        if (!state.customer) return '<div class="manual-alert manual-alert-info">Busca un cliente existente o usa “Crear cliente” para completar un receptor nuevo.</div>';
        const name = state.customer.legal_name || state.customer.commercial_name || state.customer.customer_name;
        const tax = state.customer.customer_tax_id || state.customer.export_tax_id_code || '';
        const activity = state.customer.business_activity || 'Sin giro informado';
        const email = state.customer.email_dte || state.customer.email || 'Sin email';
        return `<div class="manual-source-selected"><strong>${esc(name)}</strong><div>${esc(tax)} · ${esc(activity)}</div><div>${esc(email)}</div><button type="button" class="btn btn-sm btn-outline" id="miClearCustomer" style="margin-top:8px">Quitar selección</button></div>`;
    }

    function nationalReceiverFields() {
        const locked = !!state.customer;
        return `
            ${receiverInput('receiver_legal_name','Razón social',state.receiver.legal_name,'text','',locked)}
            ${receiverInput('receiver_tax_id_code','RUT',state.receiver.tax_id,'text','',locked)}
            ${receiverInput('receiver_activity','Giro / actividad económica',state.receiver.activity,'text','',locked)}
            ${receiverInput('receiver_address','Dirección',state.receiver.address,'text','',locked)}
            ${receiverInput('receiver_district','Comuna',state.receiver.district,'text','',locked)}
            ${receiverInput('receiver_city','Ciudad',state.receiver.city,'text','',locked)}
            ${receiverInput('receiver_email','Email DTE',state.receiver.email,'email','',locked)}
            ${receiverInput('receiver_phone','Teléfono',state.receiver.phone,'text','',locked)}`;
    }

    function exportReceiverFields() {
        const locked = !!state.customer;
        return `
            ${receiverInput('receiver_legal_name','Razón social',state.receiver.legal_name,'text','',locked)}
            ${receiverInput('receiver_tax_id_code','Identificación tributaria',state.receiver.tax_id,'text','',locked)}
            ${receiverInput('receiver_tax_id_type','Tipo de identificación',state.receiver.tax_id_type,'text','Ej. US-EIN, PE-RUC',locked)}
            <label class="manual-field crm-ui-field"><span>País</span><select class="input receiver-field" data-key="receiver_country_id" ${locked ? 'disabled' : ''}><option value="">Seleccionar país</option>${optionRows(boot.countries,'facto_country_id',(x)=>x.country_name,state.receiver.country_id)}</select></label>
            ${receiverInput('receiver_activity','Actividad económica',state.receiver.activity,'text','',locked)}
            ${receiverInput('receiver_address','Dirección',state.receiver.address,'text','',locked)}
            ${receiverInput('receiver_district','Estado / región',state.receiver.district,'text','',locked)}
            ${receiverInput('receiver_city','Ciudad',state.receiver.city,'text','',locked)}
            ${receiverInput('receiver_email','Email',state.receiver.email,'email','',locked)}
            ${receiverInput('receiver_phone','Teléfono',state.receiver.phone,'text','',locked)}`;
    }

    function receiverInput(key, label, value, type, placeholder, locked) {
        return `<label class="manual-field crm-ui-field"><span>${esc(label)}</span><input class="input receiver-field" data-key="${esc(key)}" type="${esc(type || 'text')}" value="${esc(value)}" placeholder="${esc(placeholder || '')}" ${locked ? 'readonly' : ''}></label>`;
    }

    function useNewCustomer() {
        state.customer = null;
        state.form.customer_id = '';
        state.form.create_customer = true;
        state.receiver = {
            legal_name: '', tax_id: '', tax_id_type: isExport(state.documentKey) ? '' : 'CL-RUT', country_id: isExport(state.documentKey) ? '' : '253',
            address: '', district: '', city: '', phone: '', activity: '', email: ''
        };
        renderDetailStep();
    }

    function bindReceiverFields() {
        document.querySelectorAll('.receiver-field').forEach((node) => node.addEventListener('input', () => {
            const map = {
                receiver_legal_name: 'legal_name', receiver_tax_id_code: 'tax_id', receiver_tax_id_type: 'tax_id_type',
                receiver_country_id: 'country_id', receiver_address: 'address', receiver_district: 'district',
                receiver_city: 'city', receiver_phone: 'phone', receiver_activity: 'activity', receiver_email: 'email'
            };
            state.receiver[map[node.dataset.key] || node.dataset.key] = node.value.trim();
        }));
        const clear = $('miClearCustomer');
        if (clear) clear.addEventListener('click', () => {
            state.customer = null;
            state.form.customer_id = '';
            state.form.create_customer = false;
            renderDetailStep();
        });
    }

    function guideFields() {
        return `<section class="manual-panel crm-ui-section"><h3>Destino de despacho</h3><div class="manual-grid crm-ui-form-grid">
            ${guideInput('dispatch_address','Dirección de despacho',state.form.dispatch_address || state.receiver.address)}
            ${guideInput('dispatch_district','Comuna',state.form.dispatch_district || state.receiver.district)}
            ${guideInput('dispatch_city','Ciudad',state.form.dispatch_city || state.receiver.city)}
        </div></section>`;
    }

    function guideInput(key, label, value) {
        return `<label class="manual-field crm-ui-field"><span>${esc(label)}</span><input class="input guide-field" data-key="${esc(key)}" value="${esc(value || '')}"></label>`;
    }

    async function searchCustomers() {
        clearError();
        const input = $('miCustomerSearch');
        const box = $('miCustomerResults');
        const q = input ? input.value.trim() : '';
        if (!box) return;
        if (q.length < 2) {
            box.innerHTML = '';
            box.classList.remove('show');
            return;
        }
        box.innerHTML = '<div class="search-result-item is-muted"><span>Buscando clientes...</span></div>';
        box.classList.add('show');
        try {
            const items = await requestSearchArray(`/modules/crm/api_customers.php?q=${encodeURIComponent(q)}`);
            box.innerHTML = items.length ? items.map((item, index) => `<button type="button" class="search-result-item manual-search-item" data-index="${index}"><strong>${esc(item.display_name || item.legal_name || item.customer_name)}</strong><span>${esc(item.customer_tax_id || item.export_tax_id_code || 'Sin RUT')} · ${esc(item.business_activity || 'Sin giro')} · ${esc(item.email_dte || item.email || 'Sin email')}</span></button>`).join('') : '<div class="search-result-item is-muted"><span>Sin coincidencias. Puedes completar un receptor nuevo.</span></div>';
            box.classList.add('show');
            box.querySelectorAll('[data-index]').forEach((node) => node.addEventListener('click', () => selectCustomer(items[Number(node.dataset.index)])));
        } catch (error) {
            box.innerHTML = '<div class="search-result-item is-muted"><span>Error buscando clientes.</span></div>';
            box.classList.add('show');
            setError(error.message);
        }
    }

    function selectCustomer(customer) {
        state.customer = customer;
        state.form.customer_id = customer.id;
        if (!state.form.finance_center_id && customer.default_income_center_id) state.form.finance_center_id = customer.default_income_center_id;
        if ((state.form.payment_terms_days === '0' || !state.form.payment_terms_days) && customer.payment_terms) {
            const match = String(customer.payment_terms).match(/(\d+)/);
            if (match) state.form.payment_terms_days = match[1];
        }
        if (state.documentKey === 'invoice_export_electronic') {
            state.receiver = {
                legal_name: customer.legal_name || customer.commercial_name || customer.customer_name || '',
                tax_id: customer.export_tax_id_code || customer.customer_tax_id || '',
                tax_id_type: customer.export_tax_id_type || customer.tax_id_type || '',
                country_id: customer.export_country_id || customer.country_id || '',
                address: customer.address || '', district: customer.district || customer.region || '', city: customer.city || '',
                phone: customer.phone || '', activity: customer.business_activity || '', email: customer.email_dte || customer.email || ''
            };
        } else {
            state.receiver = {
                legal_name: customer.legal_name || customer.commercial_name || customer.customer_name || '',
                tax_id: customer.customer_tax_id || '', tax_id_type: 'CL-RUT', country_id: '253',
                address: customer.address || '', district: customer.district || '', city: customer.city || '',
                phone: customer.phone || '', activity: customer.business_activity || '', email: customer.email_dte || customer.email || ''
            };
        }
        state.form.create_customer = false;
        renderDetailStep();
    }

    async function searchProducts() {
        clearError();
        const input = $('miProductSearch');
        const box = $('miProductResults');
        const q = input ? input.value.trim() : '';
        if (!box) return;
        if (q.length < 2) {
            box.innerHTML = '';
            box.classList.remove('show');
            return;
        }
        box.innerHTML = '<div class="search-result-item is-muted"><span>Buscando productos...</span></div>';
        box.classList.add('show');
        try {
            const items = await requestSearchArray(`/modules/crm/api_products.php?q=${encodeURIComponent(q)}`);
            box.innerHTML = items.length ? items.map((item, index) => `<button type="button" class="search-result-item manual-search-item" data-index="${index}"><strong>${esc(item.name || '')}</strong><span>${esc(item.sku || 'S/S')} · $${money(item.price || 0,'CLP')} · ${esc(item.unit || 'unidad')}</span></button>`).join('') : '<div class="search-result-item is-muted"><span>Sin productos encontrados.</span></div>';
            box.classList.add('show');
            box.querySelectorAll('[data-index]').forEach((node) => node.addEventListener('click', () => addProduct(items[Number(node.dataset.index)])));
        } catch (error) {
            box.innerHTML = '<div class="search-result-item is-muted"><span>Error buscando productos.</span></div>';
            box.classList.add('show');
            setError(error.message);
        }
    }

    function addProduct(product) {
        state.lines.push({
            product_id: product.id,
            sku: product.sku || '',
            description: state.documentKey === 'invoice_export_electronic' ? (product.export_name || product.name || '') : (product.name || ''),
            long_description: state.documentKey === 'invoice_export_electronic' ? (product.export_description || product.description || '') : (product.description || ''),
            quantity: 1,
            unit_measure: product.dte_unit_code || product.unit || 'UN',
            unit_price: state.documentKey === 'invoice_export_electronic' ? 0 : Number(product.price || 0),
            discount_type: 'amount', discount_value: 0,
            tax_treatment: state.documentKey === 'invoice_exempt_electronic' || state.documentKey === 'invoice_export_electronic' ? 'exento' : (product.tax_treatment || 'afecto'),
            finance_center_id: product.default_income_center_id || state.form.finance_center_id || ''
        });
        $('miProductResults').innerHTML = '';
        $('miProductSearch').value = '';
        renderLines();
    }

    function renderLines() {
        const tbody = $('miLinesBody');
        if (!tbody) return;
        if (!state.lines.length) {
            tbody.innerHTML = '<tr><td colspan="7"><div class="manual-empty-lines">Busca un producto o agrega una glosa manual.</div></td></tr>';
            renderTotals();
            return;
        }
        tbody.innerHTML = state.lines.map((line, index) => `
            <tr class="manual-primary-line" data-line="${index}">
                <td><input class="input manual-line-description line-input" data-key="description" value="${esc(line.description)}" placeholder="Producto o glosa"><input class="input line-input" data-key="long_description" value="${esc(line.long_description)}" placeholder="Descripción larga" style="margin-top:6px"></td>
                <td><input class="input line-input" data-key="sku" value="${esc(line.sku)}"></td>
                <td><input class="input line-input" data-key="quantity" type="number" min="0.0001" step="0.0001" value="${esc(line.quantity)}"></td>
                <td><input class="input line-input" data-key="unit_measure" maxlength="10" value="${esc(line.unit_measure)}"></td>
                <td><input class="input line-input" data-key="unit_price" type="number" min="0" step="0.0001" value="${esc(line.unit_price)}"></td>
                <td class="manual-line-total">${money(lineTotal(line), state.form.currency_code || 'CLP')}</td>
                <td><button type="button" class="btn btn-sm btn-danger line-remove" aria-label="Eliminar"><i data-lucide="trash-2"></i></button></td>
            </tr>
            <tr class="manual-advanced-line" data-line="${index}">
                <td colspan="7"><div class="manual-line-adjustments">
                    <label class="manual-field crm-ui-field"><span>Tipo descuento</span><select class="input line-input" data-key="discount_type"><option value="amount" ${line.discount_type === 'amount' ? 'selected' : ''}>Monto</option><option value="percent" ${line.discount_type === 'percent' ? 'selected' : ''}>Porcentaje</option></select></label>
                    <label class="manual-field crm-ui-field"><span>Valor descuento</span><input class="input line-input" data-key="discount_value" type="number" min="0" step="0.01" value="${esc(line.discount_value)}"></label>
                    <label class="manual-field crm-ui-field"><span>Tributación</span><select class="input line-input" data-key="tax_treatment" ${state.documentKey === 'invoice_export_electronic' || state.documentKey === 'invoice_exempt_electronic' ? 'disabled' : ''}><option value="afecto" ${line.tax_treatment === 'afecto' ? 'selected' : ''}>Afecto</option><option value="exento" ${line.tax_treatment === 'exento' ? 'selected' : ''}>Exento</option></select></label>
                    <label class="manual-field crm-ui-field"><span>Centro de ingreso</span><select class="input line-input" data-key="finance_center_id">${centerOptions(line.finance_center_id)}</select></label>
                </div></td>
            </tr>`).join('');
        tbody.querySelectorAll('.line-input').forEach((node) => node.addEventListener('input', () => updateLine(node)));
        tbody.querySelectorAll('.line-remove').forEach((node) => node.addEventListener('click', () => {
            const index = Number(node.closest('tr').dataset.line);
            state.lines.splice(index, 1);
            renderLines();
        }));
        renderTotals();
        if (window.lucide) window.lucide.createIcons();
    }

    function updateLine(node) {
        const row = node.closest('tr');
        const index = Number(row.dataset.line);
        const line = state.lines[index];
        const key = node.dataset.key;
        line[key] = ['quantity','unit_price','discount_value'].includes(key) ? Number(node.value || 0) : node.value;
        if (state.documentKey === 'invoice_export_electronic' || state.documentKey === 'invoice_exempt_electronic') line.tax_treatment = 'exento';
        const totalCell = document.querySelector(`#miLinesBody .manual-primary-line[data-line="${index}"] .manual-line-total`);
        if (totalCell) totalCell.textContent = money(lineTotal(line), state.form.currency_code || 'CLP');
        renderTotals();
    }

    function lineTotal(line) {
        const gross = Math.max(0, Number(line.quantity || 0) * Number(line.unit_price || 0));
        let discount = Math.max(0, Number(line.discount_value || 0));
        if (line.discount_type === 'percent') discount = gross * discount / 100;
        return Math.max(0, gross - discount);
    }

    function totals() {
        const subtotal = state.lines.reduce((sum, line) => sum + lineTotal(line), 0);
        let global = Math.max(0, Number(state.form.global_discount_value || 0));
        if (state.form.global_discount_type === 'percent') global = subtotal * global / 100;
        global = Math.min(subtotal, global);
        const after = subtotal - global;
        let net = after;
        let exempt = 0;
        let tax = 0;
        if (state.documentKey === 'invoice_export_electronic' || state.documentKey === 'invoice_exempt_electronic') {
            exempt = after;
            net = 0;
        } else {
            const taxableBase = state.lines.reduce((sum, line) => sum + (line.tax_treatment === 'exento' ? 0 : lineTotal(line)), 0);
            const exemptBase = Math.max(0, subtotal - taxableBase);
            const factor = subtotal > 0 ? after / subtotal : 1;
            net = taxableBase * factor;
            exempt = exemptBase * factor;
            tax = Math.round(net * 0.19);
        }
        return {subtotal, global, net, exempt, tax, total: net + exempt + tax};
    }

    function renderTotals() {
        const box = $('miTotals');
        if (!box) return;
        syncDetailState(false);
        const t = totals();
        const currency = state.documentKey === 'invoice_export_electronic' ? state.form.currency_code : 'CLP';
        box.innerHTML = `
            <div class="manual-total-card crm-ui-summary-card"><span>Subtotal</span><strong>${money(t.subtotal,currency)}</strong></div>
            <div class="manual-total-card crm-ui-summary-card"><span>Descuento</span><strong>${money(t.global,currency)}</strong></div>
            <div class="manual-total-card crm-ui-summary-card"><span>Neto / Exento</span><strong>${money(t.net,currency)} / ${money(t.exempt,currency)}</strong></div>
            <div class="manual-total-card crm-ui-summary-card"><span>IVA / Total</span><strong>${money(t.tax,currency)} / ${money(t.total,currency)}</strong></div>`;
    }

    function syncDetailState(render) {
        if ($('miGlobalDiscountType')) state.form.global_discount_type = $('miGlobalDiscountType').value;
        if ($('miGlobalDiscountValue')) state.form.global_discount_value = Number($('miGlobalDiscountValue').value || 0);
        document.querySelectorAll('.receiver-field').forEach((node) => node.dispatchEvent(new Event('input')));
        document.querySelectorAll('.guide-field').forEach((node) => { state.form[node.dataset.key] = node.value.trim(); });
        if (render !== false) renderTotals();
    }

    function renderNoteStep() {
        title.textContent = 'Seleccionar documento de origen';
        subtitle.textContent = `${typeLabel(state.documentKey)} · referencia obligatoria`;
        const exportNote = isExport(state.documentKey);
        content.innerHTML = `
            <div id="manualLayerError" class="manual-alert manual-alert-error" style="display:none"></div>
            <section class="manual-panel crm-ui-section">
                <h3>Documento tributario de origen</h3>
                <div class="manual-search-row"><input class="input" id="miSourceSearch" placeholder="Buscar por folio, cliente, RUT o document_id"><button class="btn btn-outline" type="button" id="miSourceSearchBtn"><i data-lucide="search"></i><span>Buscar</span></button></div>
                <div id="miSourceResults" class="manual-search-results"></div>
                <div id="miSourceSelected" style="margin-top:12px">${sourceHtml()}</div>
            </section>
            <section class="manual-panel crm-ui-section"><h3>Ajuste</h3><div class="manual-grid crm-ui-form-grid">
                ${isCredit(state.documentKey) ? `<label class="manual-field crm-ui-field"><span>Tipo de corrección</span><select class="input" id="miNoteMode"><option value="partial" ${state.form.note_mode !== 'full' ? 'selected' : ''}>Corrección parcial de monto</option><option value="full" ${state.form.note_mode === 'full' ? 'selected' : ''}>Anulación completa</option></select></label>` : `<div class="manual-field crm-ui-field"><span>Operación</span><div class="input">Aumento de monto</div></div>`}
                <label class="manual-field crm-ui-field"><span>Monto total del ajuste ${exportNote && state.source ? '(' + esc(state.source.currency_code || state.source.collection_currency_code || '') + ')' : ''}</span><input class="input" id="miNoteAmount" type="number" min="0.01" step="0.01" value="${esc(state.form.note_amount || '')}"></label>
                <label class="manual-field crm-ui-field"><span>Fecha de emisión</span><input class="input" id="miNoteDate" type="date" value="${esc(state.form.issue_date || today())}"></label>
            </div><label class="manual-field crm-ui-field" style="margin-top:14px"><span>Motivo tributario</span><textarea class="input" id="miNoteReason" rows="3" maxlength="255">${esc(state.form.note_reason || '')}</textarea></label></section>`;
        footer.innerHTML = `<button type="button" class="btn btn-primary" id="miNotePreview"><i data-lucide="file-search"></i><span>Previsualizar</span></button>`;
        $('miSourceSearchBtn').addEventListener('click', searchSources);
        $('miSourceSearch').addEventListener('keydown', (event) => { if (event.key === 'Enter') { event.preventDefault(); searchSources(); } });
        $('miNotePreview').addEventListener('click', previewCurrent);
        if (!state.source) searchSources();
    }

    async function searchSources() {
        clearError();
        try {
            const q = $('miSourceSearch').value.trim();
            const items = await requestJson(`/modules/billing/manual_issuer_data_ajax.php?kind=sources&document_key=${encodeURIComponent(state.documentKey)}&environment=${encodeURIComponent(state.form.environment)}&q=${encodeURIComponent(q)}&limit=40`);
            const box = $('miSourceResults');
            box.innerHTML = items.length ? items.map((item, index) => `<button type="button" class="manual-search-item" data-index="${index}"><strong>${esc(item.document_number || item.document_id)}</strong><small>${esc(item.receiver_legal_name || '')} · ${esc(item.receiver_tax_id_code || '')} · ${esc(item.currency_code || item.collection_currency_code || 'CLP')} ${money(item.foreign_balance_amount != null ? item.foreign_balance_amount : (item.balance_amount != null ? item.balance_amount : item.total_amount), item.currency_code || item.collection_currency_code || 'CLP')}</small></button>`).join('') : '<div class="text-secondary">No se encontraron documentos compatibles.</div>';
            box.querySelectorAll('[data-index]').forEach((node) => node.addEventListener('click', () => {
                state.source = items[Number(node.dataset.index)];
                state.form.note_amount = '';
                renderNoteStep();
            }));
        } catch (error) {
            setError(error.message);
        }
    }

    function sourceHtml() {
        if (!state.source) return '<div class="manual-alert manual-alert-info">Selecciona una factura emitida compatible.</div>';
        const currency = state.source.currency_code || state.source.collection_currency_code || 'CLP';
        const balance = state.source.foreign_balance_amount != null ? state.source.foreign_balance_amount : (state.source.balance_amount != null ? state.source.balance_amount : state.source.total_amount);
        return `<div class="manual-source-selected"><strong>Factura ${esc(state.source.document_number || '-')}</strong><div>${esc(state.source.receiver_legal_name || '')} · ${esc(state.source.receiver_tax_id_code || '')}</div><div>Saldo ajustable: ${esc(currency)} ${money(balance,currency)}</div></div>`;
    }

    function collectPayload() {
        if (isNote(state.documentKey)) {
            if (!state.source) throw new Error('Debes seleccionar el documento tributario de origen.');
            const mode = isCredit(state.documentKey) ? $('miNoteMode').value : 'partial';
            const amount = Number($('miNoteAmount').value || 0);
            const reason = $('miNoteReason').value.trim();
            const issueDate = $('miNoteDate').value;
            if (!issueDate) throw new Error('Debes indicar la fecha de emisión.');
            if (!reason) throw new Error('Debes indicar el motivo tributario.');
            if (mode !== 'full' && amount <= 0) throw new Error('El monto del ajuste debe ser mayor a cero.');
            state.form.note_mode = mode;
            state.form.note_amount = amount;
            state.form.note_reason = reason;
            state.form.issue_date = issueDate;
            return {
                client_request_id: state.manualKey,
                document_key: state.documentKey,
                environment: state.form.environment,
                source_document_local_id: Number(state.source.id),
                adjustment_mode: mode,
                adjustment_amount: amount,
                adjustment_reason: reason,
                issue_date: issueDate
            };
        }

        syncDetailState(false);
        document.querySelectorAll('.receiver-field').forEach((node) => node.dispatchEvent(new Event('input')));
        const r = state.receiver;
        if (state.customer) {
            r.legal_name = state.customer.legal_name || state.customer.commercial_name || state.customer.customer_name || r.legal_name;
            r.tax_id = state.documentKey === 'invoice_export_electronic'
                ? (state.customer.export_tax_id_code || state.customer.customer_tax_id || r.tax_id)
                : (state.customer.customer_tax_id || r.tax_id);
            r.activity = state.customer.business_activity || r.activity;
            r.address = state.customer.address || r.address;
            r.district = state.customer.district || state.customer.region || r.district;
            r.city = state.customer.city || r.city;
            r.phone = state.customer.phone || r.phone;
            r.email = state.customer.email_dte || state.customer.email || r.email;
        }
        if (!r.legal_name) throw new Error('Debes indicar la razón social del receptor.');
        if (!r.tax_id) throw new Error(isExport(state.documentKey) ? 'Debes indicar la identificación tributaria.' : 'Debes indicar el RUT.');
        if (!isReceipt(state.documentKey) && !r.activity) throw new Error('Debes indicar el giro o actividad económica.');
        if (!r.address || !r.city) throw new Error('Debes indicar dirección y ciudad.');
        if (!isExport(state.documentKey) && !r.district) throw new Error('Debes indicar la comuna.');
        if (state.documentKey === 'invoice_export_electronic' && (!r.tax_id_type || !r.country_id)) throw new Error('Debes indicar tipo de identificación y país del receptor extranjero.');
        if (!state.lines.length) throw new Error('Debes agregar al menos una línea.');
        state.lines.forEach((line, index) => {
            if (!line.description) throw new Error(`La línea ${index + 1} no tiene descripción.`);
            if (Number(line.quantity) <= 0) throw new Error(`La línea ${index + 1} tiene cantidad inválida.`);
            if (Number(line.unit_price) < 0) throw new Error(`La línea ${index + 1} tiene precio inválido.`);
        });

        const payload = {
            manual_key: state.manualKey,
            client_request_id: state.manualKey,
            document_key: state.documentKey,
            environment: state.form.environment,
            issue_date: state.form.issue_date,
            payment_terms: state.form.payment_terms_days,
            payment_terms_days: state.form.payment_terms_days,
            finance_center_id: state.form.finance_center_id || null,
            observations: state.form.observations,
            purchase_order_number: state.form.purchase_order_number || '',
            purchase_order_date: state.form.purchase_order_date || state.form.issue_date,
            customer_id: state.customer ? state.customer.id : null,
            create_customer: state.form.create_customer ? 1 : 0,
            receiver_legal_name: r.legal_name,
            receiver_tax_id_code: r.tax_id,
            receiver_tax_id_type: r.tax_id_type || 'CL-RUT',
            receiver_country_id: r.country_id || 253,
            receiver_address: r.address,
            receiver_district: r.district,
            receiver_city: r.city,
            receiver_phone: r.phone,
            receiver_activity: r.activity,
            receiver_email: r.email,
            global_discount_type: state.form.global_discount_type,
            global_discount_value: state.form.global_discount_value,
            lines: state.lines.map((line) => Object.assign({}, line))
        };
        if (isGuide(state.documentKey)) {
            payload.guide_type = state.form.guide_type;
            payload.transfer_type = state.form.transfer_type;
            payload.dispatch_address = state.form.dispatch_address || r.address;
            payload.dispatch_district = state.form.dispatch_district || r.district;
            payload.dispatch_city = state.form.dispatch_city || r.city;
        }
        if (state.documentKey === 'invoice_export_electronic') {
            payload.currency_code = state.form.currency_code;
            payload.exchange_rate_value = state.form.currency_code === 'CLP' ? 1 : Number(state.form.exchange_rate_value || 0);
            payload.sale_modality = state.form.sale_modality;
            payload.sale_clause = state.form.sale_clause;
        }
        return payload;
    }

    async function previewCurrent() {
        clearError();
        const button = $('miPreviewBtn') || $('miNotePreview');
        try {
            const payload = collectPayload();
            if (button) button.disabled = true;
            const body = new URLSearchParams({payload: JSON.stringify(payload)});
            state.preview = await requestJson('/modules/billing/manual_issuer_preview_ajax.php', {
                method: 'POST', headers: {'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'}, body: body.toString()
            });
            if (!state.preview || !state.preview.operation_id) throw new Error('La respuesta de preview no contiene la operación esperada.');
            state.preview.driver = state.preview.driver || state.preview.adapter;
            if (!state.preview.driver) throw new Error('La respuesta de preview no identifica el flujo de emisión.');
            if (!state.preview.preview || !state.preview.preview.has_pdf) throw new Error('FACTO no devolvió un PDF verificable para el preview.');
            setLayer('preview');
        } catch (error) {
            setError(error.message);
        } finally {
            if (button) button.disabled = false;
        }
    }

    function renderPreviewStep() {
        title.textContent = 'Revisar documento';
        subtitle.textContent = `${typeLabel(state.documentKey)} · borrador sin validez tributaria`;
        const p = state.preview || {};
        const preview = p.preview || {};
        const summary = p.summary || {};
        const currency = summary.currency_code || (isExport(state.documentKey) ? state.form.currency_code : 'CLP');
        content.innerHTML = `
            <div id="manualLayerError" class="manual-alert manual-alert-error" style="display:none"></div>
            <div class="manual-alert manual-alert-success"><strong>VISTA PREVIA — NO VÁLIDA TRIBUTARIAMENTE.</strong> FACTO generó un borrador verificable. No se ha informado al SII ni modificado saldos.</div>
            <div class="manual-summary-grid crm-ui-summary-grid">
                <div class="manual-summary-card crm-ui-summary-card"><span>Documento</span><strong>${esc(typeLabel(state.documentKey))}</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>Referencia temporal</span><strong>${esc(preview.document_number || '-')}</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>Total</span><strong>${esc(currency)} ${money(summary.total_amount || 0,currency)}</strong></div>
                ${currency!=='CLP'?`<div class="manual-summary-card crm-ui-summary-card"><span>Tipo de cambio</span><strong>${esc(summary.exchange_rate_value||state.form.exchange_rate_value||'-')} CLP</strong><small>${esc(summary.exchange_rate_source||state.form.exchange_rate_source||'FINDIC_BCCH')} · ${esc(summary.exchange_rate_date||state.form.exchange_rate_date||state.form.issue_date)}</small></div><div class="manual-summary-card crm-ui-summary-card"><span>Equivalente CLP</span><strong>${money(summary.total_amount_clp||Number(summary.total_amount||0)*Number(summary.exchange_rate_value||state.form.exchange_rate_value||0),'CLP')}</strong></div>`:''}
            </div>
            <section class="manual-panel crm-ui-section"><div class="manual-grid manual-grid-2 crm-ui-form-grid"><div><h3>Vista preliminar</h3><p><strong>Identificador temporal:</strong> ${esc(preview.facto_document_id || '-')}</p><p><strong>PDF:</strong> ${preview.has_pdf ? 'Disponible' : 'No disponible'}</p><p><strong>XML:</strong> ${preview.has_xml ? 'Disponible' : 'No disponible'}</p></div><div><h3>Receptor / origen</h3><p><strong>Receptor:</strong> ${esc(summary.receiver_legal_name || '-')}</p>${summary.source_document_number ? `<p><strong>Factura origen:</strong> ${esc(summary.source_document_number)}</p>` : ''}</div></div></section>
            ${preview.pdf_url ? `<iframe class="manual-preview-frame" src="${esc(preview.pdf_url)}" title="Preview FACTO"></iframe>` : ''}`;
        footer.innerHTML = `<button type="button" class="btn btn-primary" id="miPreviewContinue"><span>Continuar</span><i data-lucide="arrow-right"></i></button>`;
        $('miPreviewContinue').addEventListener('click', () => setLayer('confirm'));
    }

    function renderConfirmStep() {
        title.textContent = 'Confirmar emisión';
        subtitle.textContent = `${typeLabel(state.documentKey)} · operación tributaria`;
        const summary = (state.preview && state.preview.summary) || {};
        const currency = summary.currency_code || (isExport(state.documentKey) ? state.form.currency_code : 'CLP');
        content.innerHTML = `
            <div id="manualLayerError" class="manual-alert manual-alert-error" style="display:none"></div>
            <div class="manual-alert manual-alert-warning"><strong>${state.form.environment==='production'?'Emisión tributaria real.':'Emisión en ambiente de pruebas.'}</strong> FACTO generará un documento definitivo distinto de la vista preliminar.</div>
            <div class="manual-summary-grid crm-ui-summary-grid">
                <div class="manual-summary-card crm-ui-summary-card"><span>Documento</span><strong>${esc(typeLabel(state.documentKey))}</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>Receptor</span><strong>${esc(summary.receiver_legal_name || state.receiver.legal_name || '-')}</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>Total</span><strong>${esc(currency)} ${money(summary.total_amount || 0,currency)}</strong></div>
            </div>
            <section class="manual-panel crm-ui-section"><label class="manual-field crm-ui-field"><span>Confirmación</span><input class="input" id="miConfirmation" autocomplete="off" placeholder="Escribe ${state.form.environment==='production'?'EMITIR PRODUCCION':'EMITIR'}"></label></section>`;
        footer.innerHTML = `<button type="button" class="btn btn-danger" id="miIssueBtn" disabled><i data-lucide="send"></i><span>Emitir documento</span></button>`;
        $('miConfirmation').addEventListener('input', () => {
            $('miIssueBtn').disabled = $('miConfirmation').value.trim().toUpperCase() !== (state.form.environment==='production'?'EMITIR PRODUCCION':'EMITIR');
        });
        $('miIssueBtn').addEventListener('click', issueCurrent);
    }

    async function issueCurrent() {
        clearError();
        const button = $('miIssueBtn');
        try {
            button.disabled = true;
            const body = new URLSearchParams({
                driver: state.preview.driver,
                operation_id: String(state.preview.operation_id),
                confirmation: $('miConfirmation').value.trim()
            });
            state.result = await requestJson('/modules/billing/manual_issuer_issue_ajax.php', {
                method: 'POST', headers: {'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'}, body: body.toString()
            });
            setLayer('result');
        } catch (error) {
            setError(error.message);
            button.disabled = false;
        }
    }

    function renderResultStep() {
        title.textContent = 'Emisión completada';
        const result = state.result || {};
        const issue = result.issue || result;
        subtitle.textContent = issue.document_number ? `${typeLabel(state.documentKey)} · ${issue.document_number}` : typeLabel(state.documentKey);
        content.innerHTML = `
            <div class="manual-alert manual-alert-success"><strong>Documento emitido y registrado correctamente.</strong></div>
            <div class="manual-summary-grid crm-ui-summary-grid">
                <div class="manual-summary-card crm-ui-summary-card"><span>Estado</span><strong>Emitido</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>FACTO document_id</span><strong>${esc(issue.facto_document_id || '-')}</strong></div>
                <div class="manual-summary-card crm-ui-summary-card"><span>Folio definitivo</span><strong>${esc(issue.document_number || '-')}</strong></div>
            </div>
            <section class="manual-panel crm-ui-section"><div style="display:flex;gap:10px;flex-wrap:wrap">
                ${issue.pdf_url ? `<a class="btn btn-outline" href="${esc(issue.pdf_url)}" target="_blank" rel="noopener"><i data-lucide="file-text"></i><span>Ver PDF</span></a>` : ''}
                ${issue.xml_url ? `<a class="btn btn-outline" href="${esc(issue.xml_url)}"><i data-lucide="code-2"></i><span>Descargar XML</span></a>` : ''}
                ${issue.transfer_copy_url ? `<a class="btn btn-outline" href="${esc(issue.transfer_copy_url)}" target="_blank" rel="noopener"><i data-lucide="files"></i><span>Copia cedible</span></a>` : ''}
            </div></section>`;
        footer.innerHTML = `<a class="btn btn-outline" href="/modules/billing/documents.php"><i data-lucide="files"></i><span>Consulta tributaria</span></a><button type="button" class="btn btn-primary" id="miFinishBtn"><span>Cerrar</span></button>`;
        $('miFinishBtn').addEventListener('click', closeIssuer);
    }

    documentActions.forEach((button) => {
        const key = button.dataset.documentKey || '';
        const enabled = !!(typeMap[key] && typeMap[key].environment_enabled);
        button.disabled = !enabled;
        if (!enabled) button.title = 'Este tipo documental no está habilitado.';
        on(button, 'click', () => openIssuer(key));
    });
    on(closeBtn,'click', closeIssuer);
    on(backBtn,'click', goBack);
    on(modal,'click', (event) => { if (event.target === modal) closeIssuer(); });
    on(document, 'keydown', (event) => { if (event.key === 'Escape' && modal.classList.contains('show')) closeIssuer(); });

    if (requestedType && typeMap[requestedType] && typeMap[requestedType].environment_enabled) {
        openIssuer(requestedType);
    }
})();
