import "./app.scss"
import { initEntrepriseSearch } from "./modules/entreprise-search.js"

function initGroupCheckboxes() {
    const memberCheckbox = document.querySelector('input[value="gp_member"]');
    const adminCheckbox = document.querySelector('input[value="superadmin"]');
    if (!memberCheckbox || !adminCheckbox) return;

    const otherGroupCheckboxes = () =>
        [...document.querySelectorAll('input[name="groups[]"]')].filter(cb => cb !== memberCheckbox);

    memberCheckbox.addEventListener('change', () => {
        /* istanbul ignore next */ if (memberCheckbox.checked) otherGroupCheckboxes().forEach(cb => { cb.checked = false; });
    });

    adminCheckbox.addEventListener('change', () => {
        if (!adminCheckbox.checked) return;
        memberCheckbox.checked = false;
        otherGroupCheckboxes().forEach(cb => { cb.checked = true; });
    });
}

function initStatsPeriodSelector() {
    const periodSelect = document.getElementById('stats-period-select');
    const customRange = document.getElementById('stats-custom-range');
    if (periodSelect && customRange) {
        periodSelect.addEventListener('change', () => {
            customRange.classList.toggle('hidden', periodSelect.value !== 'custom');
        });
    }
}

function initConfirmModal() {
    const confirmModal = document.createElement('div');
    confirmModal.id = 'confirm-modal';
    confirmModal.className = 'hidden fixed inset-0 z-[100] flex items-center justify-center';
    confirmModal.innerHTML = `
        <div class="absolute inset-0 bg-black/50 backdrop-blur-sm" id="confirm-overlay"></div>
        <div class="relative glass-heavy w-full max-w-md mx-4 overflow-hidden">
            <div class="glass-dark text-white px-6 py-4 flex items-center gap-3">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                </svg>
                <span class="text-sm font-bold uppercase tracking-widest">Confirmation</span>
            </div>
            <div class="p-6">
                <p id="confirm-message" class="text-sm font-medium text-gray-700 leading-relaxed"></p>
                <div class="flex justify-end gap-3 mt-6">
                    <button type="button" id="confirm-cancel" class="px-5 py-2 glass font-bold uppercase text-xs tracking-wider text-gray-700 hover:bg-gray-900 hover:text-white transition-all">Annuler</button>
                    <button type="button" id="confirm-ok" class="px-5 py-2 bg-red-600 text-white font-bold uppercase text-xs tracking-wider hover:bg-red-700 transition-all">Confirmer</button>
                </div>
            </div>
        </div>`;
    document.body.appendChild(confirmModal);

    let pendingForm = null;
    const confirmMessage = document.getElementById('confirm-message');
    const confirmCancel = document.getElementById('confirm-cancel');
    const confirmOk = document.getElementById('confirm-ok');
    const confirmOverlay = document.getElementById('confirm-overlay');

    const closeConfirm = () => { confirmModal.classList.add('hidden'); pendingForm = null; };

    confirmCancel.addEventListener('click', closeConfirm);
    confirmOverlay.addEventListener('click', closeConfirm);
    document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && pendingForm) closeConfirm(); });

    confirmOk.addEventListener('click', () => {
        /* istanbul ignore next */ if (pendingForm) {
            confirmModal.classList.add('hidden');
            delete pendingForm.dataset.confirm;
            pendingForm.requestSubmit();
        }
    });

    document.querySelectorAll('form[data-confirm]').forEach(form => {
        form.addEventListener('submit', (e) => {
            /* istanbul ignore next */ if (form.dataset.confirm) {
                e.preventDefault();
                pendingForm = form;
                confirmMessage.textContent = form.dataset.confirm;
                confirmModal.classList.remove('hidden');
            }
        });
    });
}

function initSidebarAndMobileMenu() {
    document.querySelectorAll('.sidebar-dropdown-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            const menu = btn.nextElementSibling;
            const arrow = btn.querySelector('.sidebar-dropdown-arrow');
            menu?.classList.toggle('hidden');
            arrow?.classList.toggle('rotate-180');
        });
    });

    const sidebarToggle = document.getElementById('admin-sidebar-toggle');
    const sidebar = document.getElementById('admin-sidebar');
    const overlay = document.getElementById('admin-overlay');

    if (sidebarToggle && sidebar && overlay) {
        sidebarToggle.addEventListener('click', () => sidebar.classList.toggle('open'));
        overlay.addEventListener('click', () => sidebar.classList.remove('open'));
    }

    const mobileMenuBtn = document.getElementById('mobile-menu-btn');
    const mobileMenu = document.getElementById('mobile-menu');
    const menuIconOpen = document.getElementById('menu-icon-open');
    const menuIconClose = document.getElementById('menu-icon-close');

    if (mobileMenuBtn && mobileMenu) {
        mobileMenuBtn.addEventListener('click', () => {
            mobileMenu.classList.toggle('hidden');
            menuIconOpen?.classList.toggle('hidden');
            menuIconClose?.classList.toggle('hidden');
        });
    }
}

function initCookieBanner() {
    const cookieBanner = document.getElementById('cookie-banner');
    const cookieAccept = document.getElementById('cookie-accept');
    const cookieRefuse = document.getElementById('cookie-refuse');

    if (cookieBanner && !localStorage.getItem('cookie_consent')) {
        cookieBanner.classList.remove('hidden');
    }

    cookieAccept?.addEventListener('click', () => {
        localStorage.setItem('cookie_consent', 'accepted');
        cookieBanner?.classList.add('hidden');
    });

    cookieRefuse?.addEventListener('click', () => {
        localStorage.setItem('cookie_consent', 'refused');
        cookieBanner?.classList.add('hidden');
    });
}

document.addEventListener('DOMContentLoaded', () => {
    initGroupCheckboxes();
    initStatsPeriodSelector();
    initConfirmModal();
    initSidebarAndMobileMenu();
    initCookieBanner();

    // Search (customers & revendeurs)
    const renderHit = (h, linkPrefix) => {
        const id = h.customerId || h.id;
        const name = h.fqdn || h.name || h.fullName || h.raisonSociale || (h.firstName + ' ' + h.lastName);
        let sub = '';
        if (h.customerName) sub = `<span class="text-gray-400 ml-2">${h.customerName}</span>`;
        else if (h.email) sub = `<span class="text-gray-400 ml-2">${h.email}</span>`;
        return `<a href="${linkPrefix}${id}" class="block px-4 py-2 hover:bg-gray-50 border-b border-gray-100 text-xs">
            <span class="font-bold">${name}</span>
            ${sub}
            ${h.codeRevendeur ? `<span class="ml-2 px-1 py-0.5 bg-gray-900 text-[#fabf04] text-[9px] font-bold">${h.codeRevendeur}</span>` : ''}
        </a>`;
    };

    const performSearch = async (searchUrl, linkPrefix, results, q) => {
        const resp = await fetch(`${searchUrl}?q=${encodeURIComponent(q)}`);
        const hits = await resp.json();
        results.innerHTML = hits.length === 0
            ? '<div class="px-4 py-3 text-xs text-gray-400">Aucun resultat.</div>'
            : hits.map(h => renderHit(h, linkPrefix)).join('');
        results.classList.remove('hidden');
    };

    const setupSearch = (inputId, resultsId, searchUrl, linkPrefix) => {
        const input = document.getElementById(inputId);
        const results = document.getElementById(resultsId);
        if (!input || !results) return;

        let debounce;
        input.addEventListener('input', () => {
            clearTimeout(debounce);
            const q = input.value.trim();
            if (q.length < 2) {
                results.classList.add('hidden');
                results.innerHTML = '';
                return;
            }
            debounce = setTimeout(() => performSearch(searchUrl, linkPrefix, results, q), 300);
        });

        document.addEventListener('click', (e) => {
            /* istanbul ignore next */ if (!results.contains(e.target) && e.target !== input) {
                results.classList.add('hidden');
            }
        });
    };

    setupSearch('search-customers', 'search-results', '/admin/clients/search', '/admin/clients/');
    setupSearch('search-revendeurs', 'search-results-revendeurs', '/admin/revendeurs/search', '/admin/revendeurs/');
    setupSearch('search-ndd', 'search-ndd-results', '/admin/services/ndd/search', '/admin/clients/');
    setupSearch('search-websites', 'search-websites-results', '/admin/services/esyweb/search', '/admin/clients/');

    // Tarif tabs
    const tarifTabs = document.getElementById('tarif-tabs');
    if (tarifTabs) {
        const active = 'px-6 py-3 font-bold uppercase text-sm tracking-wider glass-dark text-white border border-white/20 border-b-0 cursor-pointer';
        const inactive = 'px-6 py-3 font-bold uppercase text-sm tracking-wider glass text-gray-700 border border-gray-200 border-b-0 cursor-pointer hover:bg-white/80 transition-all';

        tarifTabs.querySelectorAll('[data-tab]').forEach(btn => {
            btn.addEventListener('click', () => {
                const tab = btn.dataset.tab;
                tarifTabs.querySelectorAll('[data-tab]').forEach(b => {
                    b.className = b.dataset.tab === tab ? active : inactive;
                });
                document.querySelectorAll('[id^="content-"]').forEach(el => {
                    /* istanbul ignore next */ if (el.closest('#tarif-tabs')) return;
                    el.classList.toggle('hidden', el.id !== 'content-' + tab);
                });
            });
        });
    }

    // Recherche entreprise (page creation client)
    initEntrepriseSearch();

    // Recherche globale (navbar admin)
    const globalInput = document.getElementById('global-search');
    const globalResults = document.getElementById('global-search-results');
    if (globalInput && globalResults) {
        const typeIcons = {
            client: '&#128100;',
            ndd: '&#127760;',
            site: '&#128187;',
            contact: '&#128101;',
            revendeur: '&#127970;',
        };
        const typeLabels = {
            client: 'Client',
            ndd: 'NDD',
            site: 'Site',
            contact: 'Contact',
            revendeur: 'Revendeur',
        };

        let globalDebounce;
        globalInput.addEventListener('input', () => {
            clearTimeout(globalDebounce);
            const q = globalInput.value.trim();
            if (q.length < 2) {
                globalResults.classList.add('hidden');
                globalResults.innerHTML = '';
                return;
            }
            globalDebounce = setTimeout(async () => {
                const resp = await fetch('/admin/global-search?q=' + encodeURIComponent(q));
                const hits = await resp.json();
                if (hits.length === 0) {
                    globalResults.innerHTML = '<div class="px-4 py-3 text-xs text-gray-400">Aucun resultat.</div>';
                } else {
                    globalResults.innerHTML = hits.map(h =>
                        `<a href="${h.url}" class="flex items-center gap-3 px-4 py-2 hover:bg-white/80 border-b border-white/10 transition-colors">
                            <span class="text-sm">${typeIcons[h.type] || ''}</span>
                            <div class="min-w-0 flex-1">
                                <span class="text-xs font-bold block truncate">${h.label}</span>
                                ${h.sub ? `<span class="text-[10px] text-gray-400 block truncate">${h.sub}</span>` : ''}
                            </div>
                            <span class="px-1.5 py-0.5 bg-gray-100 text-gray-500 font-bold uppercase text-[8px] rounded shrink-0">${typeLabels[h.type] || h.type}</span>
                        </a>`
                    ).join('');
                }
                globalResults.classList.remove('hidden');
            }, 250);
        });

        document.addEventListener('click', (e) => {
            /* istanbul ignore next */ if (!globalResults.contains(e.target) && e.target !== globalInput) {
                globalResults.classList.add('hidden');
            }
        });

        /* istanbul ignore next */
        globalInput.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') globalResults.classList.add('hidden');
        });
    }

    // ──────── Stripe Payment Modal ────────
    initStripePayment();

    // ──────── Tab search devis / avis ────────
    initTabSearch('search-devis', 'search-devis-results');
    initTabSearch('search-adverts', 'search-adverts-results');
    initTabSearch('search-factures', 'search-factures-results');

    // ──────── Devis lines repeater + drag & drop ────────
    initDevisLines();

    // ──────── Devis process : toggle formulaire de refus ────────
    const refuseBtn = document.getElementById('refuse-toggle-btn');
    const refuseForm = document.getElementById('refuse-form');
    if (refuseBtn && refuseForm) {
        refuseBtn.addEventListener('click', () => refuseForm.classList.toggle('hidden'));
    }

    // ──────── Modal open/close generique (data-modal-open / data-modal-close) ────────
    document.querySelectorAll('[data-modal-open]').forEach(btn => {
        btn.addEventListener('click', () => {
            const modal = document.getElementById(btn.dataset.modalOpen);
            if (modal) modal.classList.remove('hidden');
        });
    });
    document.querySelectorAll('[data-modal-close]').forEach(btn => {
        btn.addEventListener('click', () => {
            const modal = document.getElementById(btn.dataset.modalClose);
            if (modal) modal.classList.add('hidden');
        });
    });

    // ──────── Prestataire : recherche SIRET via API entreprise.data.gouv.fr ────────
    const siretSearchBtn = document.getElementById('siret-search-btn');
    const siretInput = document.getElementById('siret-search-input');
    const siretResults = document.getElementById('siret-search-results');

    /* istanbul ignore next -- fallback branches */
    const renderSiretResult = (r) => {
        const siege = r.siege || {};
        const siret = siege.siret || '';
        const nom = r.nom_complet || r.nom_raison_sociale || '';
        const adresse = siege.adresse || '';
        const cp = siege.code_postal || '';
        const ville = siege.libelle_commune || '';
        return '<button type="button" class="siret-result-item block w-full text-left px-3 py-2 hover:bg-white/70 border-b border-white/20 transition-all"'
            + ' data-nom="' + nom.replaceAll('"', '&quot;') + '"'
            + ' data-siret="' + siret + '"'
            + ' data-adresse="' + adresse.replaceAll('"', '&quot;') + '"'
            + ' data-cp="' + cp + '"'
            + ' data-ville="' + ville.replaceAll('"', '&quot;') + '">'
            + '<span class="font-bold text-xs">' + nom + '</span>'
            + '<span class="text-[10px] text-gray-400 ml-2">' + siret + '</span>'
            + '<br><span class="text-[10px] text-gray-400">' + adresse + ' ' + cp + ' ' + ville + '</span>'
            + '</button>';
    };

    const bindSiretResultClick = (item) => {
        item.addEventListener('click', () => {
            const form = siretSearchBtn.closest('form');
            if (!form) return;
            /* istanbul ignore next */
            const set = (name, val) => { const el = form.querySelector('[name="' + name + '"]'); if (el) el.value = val; };
            set('raisonSociale', item.dataset.nom);
            set('siret', item.dataset.siret);
            set('address', item.dataset.adresse);
            set('zipCode', item.dataset.cp);
            set('city', item.dataset.ville);
            siretResults.classList.add('hidden');
            siretInput.value = '';
        });
    };

    const handleSiretSearch = () => {
        const q = siretInput.value.trim();
        if (q.length < 3) { siretResults.innerHTML = '<p class="text-xs text-gray-400 p-2">Saisissez au moins 3 caracteres.</p>'; siretResults.classList.remove('hidden'); return; }

        siretResults.innerHTML = '<p class="text-xs text-gray-400 p-2">Recherche...</p>';
        siretResults.classList.remove('hidden');

        fetch('/admin/prestataires/entreprise-search?q=' + encodeURIComponent(q))
            .then(r => r.json())
            .then(data => {
                /* istanbul ignore next */
                const results = data.results || [];
                if (results.length === 0) {
                    siretResults.innerHTML = '<p class="text-xs text-gray-400 p-2">Aucun resultat.</p>';
                    return;
                }
                siretResults.innerHTML = results.map(renderSiretResult).join('');
                siretResults.querySelectorAll('.siret-result-item').forEach(bindSiretResultClick);
            })
            .catch(() => {
                siretResults.innerHTML = '<p class="text-xs text-red-500 p-2">Erreur lors de la recherche.</p>';
            });
    };

    if (siretSearchBtn && siretInput && siretResults) {
        siretSearchBtn.addEventListener('click', handleSiretSearch);
        siretInput.addEventListener('keydown', (e) => { /* istanbul ignore next */ if (e.key === 'Enter') { e.preventDefault(); siretSearchBtn.click(); } });
        /* istanbul ignore next */ document.addEventListener('click', (e) => { if (!siretResults.contains(e.target) && e.target !== siretInput && e.target !== siretSearchBtn) siretResults.classList.add('hidden'); });
    }
});

/* istanbul ignore next */
function initStripePayment() {
    const btnStripe = document.getElementById('btn-stripe');
    const modal = document.getElementById('stripe-modal');
    const overlay = document.getElementById('stripe-modal-overlay');
    const closeBtn = document.getElementById('stripe-modal-close');
    const payBtn = document.getElementById('stripe-pay-btn');
    const errorsEl = document.getElementById('stripe-errors');
    const paymentEl = document.getElementById('stripe-payment-element');

    if (!btnStripe || !modal || !paymentEl) return;

    const pk = btnStripe.dataset.pk;
    const intentUrl = btnStripe.dataset.intentUrl;
    const successUrl = btnStripe.dataset.successUrl;
    const amount = btnStripe.dataset.amount;

    if (!pk) return;

    const stripe = Stripe(pk);
    let elements = null;
    let clientSecret = null;
    let isProcessing = false;

    const showModal = () => modal.classList.remove('hidden');
    const hideModal = () => { if (!isProcessing) modal.classList.add('hidden'); };

    const payBtnLabel = (m) => {
        if (m === 'sepa') return 'Prelever ' + amount + ' €';
        if (m === 'paypal') return 'Payer via PayPal ' + amount + ' €';
        return 'Payer ' + amount + ' €';
    };

    closeBtn.addEventListener('click', hideModal);
    overlay.addEventListener('click', hideModal);
    document.addEventListener('keydown', (e) => { if (e.key === 'Escape') hideModal(); });

    let currentMethod = 'card';

    function openStripeModal(method) {
        currentMethod = method;
        showModal();
        payBtn.disabled = true;
        payBtn.textContent = 'Chargement...';
        errorsEl.classList.add('hidden');

        const bodyPayload = method === 'sepa' ? { method: 'sepa' } : {};

        fetch(intentUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(bodyPayload),
        })
        .then(r => r.json())
        .then(data => {
            if (data.error) {
                errorsEl.textContent = data.error;
                errorsEl.classList.remove('hidden');
                payBtn.textContent = payBtnLabel(method);
                return;
            }

            clientSecret = data.clientSecret;

            const elementsOptions = {
                clientSecret: clientSecret,
                locale: 'fr',
                appearance: {
                    theme: 'stripe',
                    variables: {
                        fontFamily: 'Arial, Helvetica, sans-serif',
                        colorPrimary: method === 'sepa' ? '#2563eb' : '#4f46e5',
                        borderRadius: '8px',
                    },
                },
            };

            elements = stripe.elements(elementsOptions);

            const paymentElement = elements.create('payment');
            paymentEl.innerHTML = '';
            paymentElement.mount('#stripe-payment-element');

            paymentElement.on('ready', () => {
                payBtn.disabled = false;
                payBtn.textContent = payBtnLabel(method);
            });

            paymentElement.on('change', (event) => {
                if (event.error) {
                    errorsEl.textContent = event.error.message;
                    errorsEl.classList.remove('hidden');
                } else {
                    errorsEl.classList.add('hidden');
                }
            });
        })
        .catch(() => {
            errorsEl.textContent = 'Erreur de connexion au serveur de paiement.';
            errorsEl.classList.remove('hidden');
            payBtn.textContent = payBtnLabel(method);
        });
    }

    btnStripe.addEventListener('click', () => openStripeModal('card'));

    // Bouton SEPA
    const btnSepa = document.getElementById('btn-sepa');
    if (btnSepa) {
        btnSepa.addEventListener('click', () => openStripeModal('sepa'));
    }

    payBtn.addEventListener('click', async () => {
        if (!clientSecret || !elements || isProcessing) return;

        isProcessing = true;
        payBtn.disabled = true;
        payBtn.textContent = 'Traitement en cours...';
        errorsEl.classList.add('hidden');

        // confirmPayment gere automatiquement le 3D Secure (3DS)
        // Stripe ouvre la modal 3DS dans une iframe si necessaire
        const { error } = await stripe.confirmPayment({
            elements: elements,
            confirmParams: {
                return_url: globalThis.location.origin + successUrl + '?method=' + currentMethod,
            },
        });

        // Si on arrive ici, c'est qu'il y a eu une erreur
        // (en cas de succes, le navigateur est redirige vers return_url)
        if (error) {
            if (error.type === 'card_error' || error.type === 'validation_error') {
                errorsEl.textContent = error.message;
            } else {
                errorsEl.textContent = 'Une erreur inattendue est survenue. Veuillez reessayer.';
            }
            errorsEl.classList.remove('hidden');
        }

        isProcessing = false;
        payBtn.disabled = false;
        payBtn.textContent = currentMethod === 'sepa' ? 'Prelever ' + amount + ' €' : 'Payer ' + amount + ' €';
    });
}

/* istanbul ignore next */ function initTabSearch(inputId, resultsId) {
    const input = document.getElementById(inputId);
    const results = document.getElementById(resultsId);
    if (!input || !results) return;

    const url = input.dataset.url;
    let timeout = null;

    const stateLabels = {
        created: 'Cree', send: 'Envoye', accepted: 'Accepte', refused: 'Refuse', cancel: 'Annule'
    };
    const stateColors = {
        created: 'bg-yellow-100 text-yellow-800', send: 'bg-blue-500/20 text-blue-700',
        accepted: 'bg-green-500/20 text-green-700', refused: 'bg-red-500/20 text-red-700',
        cancel: 'bg-gray-100 text-gray-600'
    };

    input.addEventListener('input', () => {
        clearTimeout(timeout);
        const q = input.value.trim();
        if (q.length < 2) { results.classList.add('hidden'); return; }

        timeout = setTimeout(async () => {
            const resp = await fetch(url + '?q=' + encodeURIComponent(q));
            const hits = await resp.json();
            if (hits.length === 0) {
                results.innerHTML = '<div class="px-4 py-3 text-xs text-gray-400">Aucun resultat.</div>';
            } else {
                results.innerHTML = hits.map(h =>
                    `<div class="flex items-center justify-between px-4 py-2 border-b border-white/10 hover:bg-white/50">
                        <div>
                            <span class="font-mono font-bold text-xs">${h.numOrder}</span>
                            <span class="text-[10px] text-gray-400 ml-2">${/* istanbul ignore next */ h.customerName || ''}</span>
                        </div>
                        <div class="flex items-center gap-2">
                            <span class="font-mono text-xs">${/* istanbul ignore next */ h.totalTtc || '0.00'} €</span>
                            <span class="px-2 py-0.5 ${stateColors[h.state] || 'bg-gray-100'} font-bold uppercase text-[9px] rounded">${stateLabels[h.state] || h.state}</span>
                        </div>
                    </div>`
                ).join('');
            }
            results.classList.remove('hidden');
        }, 250);
    });

    /* istanbul ignore next */ document.addEventListener('click', (e) => {
        if (!results.contains(e.target) && e.target !== input) results.classList.add('hidden');
    });

    /* istanbul ignore next */ input.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') results.classList.add('hidden');
    });
}

function initDevisLines() {
    const container = document.getElementById('lines-container');
    const addBtn = document.getElementById('add-line-btn');
    const tplEl = document.getElementById('line-template');
    const totalEl = document.getElementById('total-ht');
    if (!container || !addBtn || !tplEl || !totalEl) return;

    const template = tplEl.innerHTML;
    let counter = 0;

    function renumber() {
        container.querySelectorAll('.line-row').forEach((row, idx) => {
            row.querySelector('.line-pos').textContent = '#' + (idx + 1);
            row.querySelector('.line-pos-input').value = idx;
        });
    }

    function recalc() {
        let total = 0;
        container.querySelectorAll('.line-price').forEach(input => {
            const v = Number.parseFloat(input.value);
            if (!Number.isNaN(v)) total += v;
        });
        totalEl.textContent = total.toFixed(2) + ' EUR';
    }

    function addLine() {
        const html = template.replaceAll('__INDEX__', counter);
        const wrapper = document.createElement('div');
        wrapper.innerHTML = html.trim();
        const node = wrapper.firstChild;
        container.appendChild(node);
        counter++;
        renumber();
        recalc();
    }

    container.addEventListener('click', e => {
        /* istanbul ignore next */ if (e.target.classList.contains('remove-line-btn')) {
            e.target.closest('.line-row').remove();
            renumber();
            recalc();
        }
    });

    container.addEventListener('input', e => {
        /* istanbul ignore next */ if (e.target.classList.contains('line-price')) recalc();
    });

    addBtn.addEventListener('click', () => addLine());

    // Validation : empeche l'envoi si un type est selectionne mais pas le service
    const form = document.getElementById('devis-form');
    /* istanbul ignore next */ if (form) {
        form.addEventListener('submit', (e) => {
            const rows = container.querySelectorAll('.line-row');
            for (const row of rows) {
                const typeSelect = row.querySelector('.line-type');
                const serviceSelect = row.querySelector('.line-service-id');
                /* istanbul ignore next */ if (!typeSelect || !serviceSelect) continue;

                const type = typeSelect.value;
                /* istanbul ignore next */ if (!type || type === 'hosting' || type === 'maintenance' || type === 'other' || type === 'ndd' || type === 'website') continue;

                // Type avec service obligatoire (esymail) mais pas selectionne — ndd et website autorisent le vide
                if (!serviceSelect.value && !serviceSelect.disabled && serviceSelect.options.length > 1) {
                    e.preventDefault();
                    serviceSelect.focus();
                    serviceSelect.classList.add('border-red-500', 'ring-2', 'ring-red-300');
                    const pos = row.querySelector('.line-pos')?.textContent || /* istanbul ignore next */ '';
                    alert('Ligne ' + pos + ' : veuillez selectionner le service pour le type "' + typeSelect.options[typeSelect.selectedIndex].text + '".');
                    return;
                }
            }
        });
    }

    // Chargement dynamique des services par type
    container.addEventListener('change', async (e) => {
        /* istanbul ignore next */ if (!e.target.classList.contains('line-type')) return;
        const select = e.target;
        const row = select.closest('.line-row');
        const serviceSelect = row.querySelector('.line-service-id');
        const type = select.value;

        serviceSelect.innerHTML = '<option value="">— Selectionner le service —</option>';
        serviceSelect.disabled = true;

        if (!type || type === 'hosting' || type === 'maintenance' || type === 'other') return;

        const url = select.dataset.servicesUrl.replaceAll('__TYPE__', type);
        try {
            const resp = await fetch(url);
            const items = await resp.json();
            /* istanbul ignore next */ if (items.length > 0) {
                items.forEach(item => {
                    const opt = document.createElement('option');
                    opt.value = item.id;
                    opt.textContent = item.label;
                    serviceSelect.appendChild(opt);
                });
                serviceSelect.disabled = false;
            }
        } catch (err) { console.debug('Service loading failed', err); }
    });

    // Boutons prestations rapides : ajoute une ligne pre-remplie avec type auto
    document.querySelectorAll('.quick-price-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            addLine();
            const lastRow = container.querySelector('.line-row:last-child');
            /* istanbul ignore next */ if (!lastRow) return;
            lastRow.querySelector('input[name$="[title]"]').value = /* istanbul ignore next */ btn.dataset.title || '';
            lastRow.querySelector('textarea[name$="[description]"]').value = /* istanbul ignore next */ btn.dataset.description || '';
            const priceInput = lastRow.querySelector('.line-price');
            priceInput.value = /* istanbul ignore next */ btn.dataset.price || '0.00';

            // Auto-set le type de service
            const lineType = btn.dataset.lineType || '';
            /* istanbul ignore next */ if (lineType) {
                const typeSelect = lastRow.querySelector('.line-type');
                /* istanbul ignore next */ if (typeSelect) {
                    typeSelect.value = lineType;
                    // Trigger change pour charger les services du client
                    typeSelect.dispatchEvent(new Event('change', { bubbles: true }));
                }
            }

            recalc();
        });
    });

    // Drag & drop reordering
    let draggedRow = null;

    container.addEventListener('dragstart', e => {
        const row = e.target.closest('.line-row');
        if (!row) return;
        draggedRow = row;
        row.classList.add('dragging');
        e.dataTransfer.effectAllowed = 'move';
    });

    container.addEventListener('dragend', e => {
        const row = e.target.closest('.line-row');
        if (row) row.classList.remove('dragging');
        container.querySelectorAll('.line-row').forEach(r => r.classList.remove('drag-over'));
        draggedRow = null;
        renumber();
    });

    container.addEventListener('dragover', e => {
        e.preventDefault();
        const target = e.target.closest('.line-row');
        if (!target || target === draggedRow) return;
        container.querySelectorAll('.line-row').forEach(r => r.classList.remove('drag-over'));
        target.classList.add('drag-over');
    });

    container.addEventListener('drop', e => {
        e.preventDefault();
        const target = e.target.closest('.line-row');
        if (!target || !draggedRow || target === draggedRow) return;
        const rows = Array.from(container.querySelectorAll('.line-row'));
        const draggedIdx = rows.indexOf(draggedRow);
        const targetIdx = rows.indexOf(target);
        if (draggedIdx < targetIdx) {
            target.after(draggedRow);
        } else {
            target.before(draggedRow);
        }
        target.classList.remove('drag-over');
        renumber();
    });

    // Prefill en mode edition
    const initial = container.dataset.initialLines;
    if (initial) {
        try {
            const arr = JSON.parse(initial);
            /* istanbul ignore next */ arr.sort((a, b) => (a.pos || 0) - (b.pos || 0));
            arr.forEach(async (l) => {
                addLine();
                const row = container.querySelector('.line-row:last-child');
                /* istanbul ignore next */ if (!row) return;
                row.querySelector('input[name$="[title]"]').value = /* istanbul ignore next */ l.title || '';
                row.querySelector('textarea[name$="[description]"]').value = /* istanbul ignore next */ l.description || '';
                row.querySelector('.line-price').value = /* istanbul ignore next */ l.priceHt || '0.00';

                // Pre-select type
                const typeSelect = row.querySelector('.line-type');
                /* istanbul ignore next */ if (l.type && typeSelect) {
                    typeSelect.value = l.type;

                    // Charge les services si type avec serviceId
                    if (l.serviceId && l.type !== 'hosting' && l.type !== 'maintenance' && l.type !== 'other') {
                        const serviceSelect = row.querySelector('.line-service-id');
                        const url = typeSelect.dataset.servicesUrl.replace('__TYPE__', l.type);
                        try {
                            const resp = await fetch(url);
                            const items = await resp.json();
                            serviceSelect.innerHTML = '<option value="">— Selectionner le service —</option>';
                            items.forEach(item => {
                                const opt = document.createElement('option');
                                opt.value = item.id;
                                opt.textContent = item.label;
                                if (item.id === l.serviceId) opt.selected = true;
                                serviceSelect.appendChild(opt);
                            });
                            serviceSelect.disabled = false;
                        } catch (err) { console.debug('Prefill service loading failed', err); }
                    }
                }
            });
            recalc();
        } catch (e) { console.debug('Initial lines parse failed', e); }
    }
}
