/* Page copie/import : bargraph live (blocs 'pending') + clic-trou → champ IP actif. ES5/IIFE. */
(function () {
    if (!window.SubnetBargraph || typeof SUBNET === 'undefined') return;
    var mount = document.getElementById('subnet-bargraph');
    if (!mount) return;

    var SBG = window.SubnetBargraph;

    // IP "a.b.c.d" → entier non signé, ou null si invalide
    function ipToInt(ip) {
        var m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(ip);
        if (!m) return null;
        var a = +m[1], b = +m[2], c = +m[3], d = +m[4];
        if (a > 255 || b > 255 || c > 255 || d > 255) return null;
        return (a * 16777216 + b * 65536 + c * 256 + d) >>> 0;
    }

    // IP déjà détenues par une réservation existante du cible : ip_int → host_id détenteur.
    // Permet de rejeter une IP déjà prise, tout en autorisant, pour un conflit MAC en MAJ,
    // l'IP qui appartient déjà à la réservation qu'on met à jour (ownHostId).
    var EXISTING_BY_IP = {};
    (window.EXISTING_ROWS || []).forEach(function (r) {
        var n = (r.ip_int >>> 0);
        if (n) EXISTING_BY_IP[n] = (r.host_id | 0);
    });

    // Validation optimiste (affichage) : format + dans firstUsable..lastUsable + hors pools +
    // hors réservé + non déjà prise par une AUTRE réservation existante. La barrière qui fait
    // foi reste le garde-fou serveur (subnet_copy.php étape 2). ownHostId (optionnel) = host_id
    // de la réservation qu'on met à jour, dont l'IP propre ne doit pas compter comme "prise".
    function ipValidForPreview(ip, ownHostId) {
        var n = ipToInt(ip);
        if (n === null) return false;
        if (n < (SUBNET.firstUsable >>> 0) || n > (SUBNET.lastUsable >>> 0)) return false;
        if (SUBNET.dynStartInt != null && n >= (SUBNET.dynStartInt >>> 0) && n <= (SUBNET.dynEndInt >>> 0)) return false;
        if (SUBNET.shortStartInt != null && n >= (SUBNET.shortStartInt >>> 0) && n <= (SUBNET.shortEndInt >>> 0)) return false;
        if (SUBNET.reservedIps && SBG.parseReserved) {
            var blocks = SBG.parseReserved(SUBNET.reservedIps, SUBNET.networkInt >>> 0);
            for (var i = 0; i < blocks.length; i++) {
                if (n >= (blocks[i].startInt >>> 0) && n <= (blocks[i].endInt >>> 0)) return false;
            }
        }
        // Déjà prise par une réservation existante du cible (sauf la sienne en MAJ conflit MAC).
        var owner = EXISTING_BY_IP[n];
        if (owner !== undefined && owner !== (ownHostId | 0)) return false;
        return true;
    }

    // Parcourt le tableau, retourne les lignes cochées & créables → rows _pending pour le bargraph.
    function collectPendingRows() {
        var pend = [], seen = {};
        var trs = document.querySelectorAll('.copy-row');
        for (var i = 0; i < trs.length; i++) {
            var tr = trs[i];
            var cb = tr.querySelector('input[type=checkbox][name$="[do]"]');
            if (cb && (cb.disabled || !cb.checked)) continue;   // page copie : décochée/désactivée → ignorer
            // page import : pas de case [do] → ligne active par défaut
            var stEl = tr.querySelector('input[name$="[status]"]');
            var status = stEl ? stEl.value : '';
            if (status === 'skip' || status === 'pool' || status === 'invalid') continue;
            // mac_conflict : ne compte comme "pending" (nouvelle occupation) que si l'action choisie
            // pointe vers une IP (update/custom_ip) ET que l'IP est libre. En 'keep' (conserver
            // l'existant), la résa existante est déjà dans EXISTING_ROWS → ne rien ajouter.
            var ownHostId = 0;
            if (status === 'mac_conflict') {
                var sel = tr.querySelector('select[name$="[mac_action]"], select[name$="[action]"]');
                var act = sel ? sel.value : 'update';
                if (act === 'keep') continue; // conserver l'existant → déjà dans EXISTING_ROWS
                // update / custom_ip → pending si l'IP valide (vérifiée plus bas)
                var mh = tr.querySelector('input[name$="[mac_host_id]"]');
                ownHostId = mh ? (parseInt(mh.value, 10) || 0) : 0;
            }
            var ipField = tr.querySelector('.ip-field');
            var ip = ipField ? ipField.value.trim()
                             : (function () { var h = tr.querySelector('input[name$="[ip]"]'); return h ? h.value.trim() : ''; })();
            if (!ip || !ipValidForPreview(ip, ownHostId)) continue;
            var n = ipToInt(ip);
            if (seen[n]) continue;
            seen[n] = true;
            pend.push({ ip_int: n, host_id: 0, hostname: '(à créer)', group_name: '', short_lease: 0, _pending: true });
        }
        return pend;
    }

    // Recoloration live des lignes (copie uniquement : agit sur les lignes portant une classe vis-*).
    // Règles : décochée → gris (vis-ignored) ; conflit dont l'IP ressaisie devient valide → vert
    // (vis-ok) ; sinon on garde la couleur d'origine dérivée du hidden [status].
    function baseVis(status) {
        if (status === 'clean') return 'vis-ok';
        if (status === 'skip' || status === 'pool' || status === 'invalid') return 'vis-ignored';
        return 'vis-conflict';
    }
    function setVis(tr, cls) {
        if (tr.classList.contains(cls)) return;
        tr.classList.remove('vis-ok', 'vis-conflict', 'vis-ignored');
        tr.classList.add(cls);
    }
    function recolor() {
        var nOk = 0, nConflict = 0, nIgnored = 0;
        var trs = document.querySelectorAll('.copy-row');
        for (var i = 0; i < trs.length; i++) {
            var tr = trs[i];
            // Ne recolore que les lignes gérées côté copie (celles qui ont une classe vis-*).
            if (!tr.className || tr.className.indexOf('vis-') === -1) continue;
            var stEl = tr.querySelector('input[name$="[status]"]');
            var status = stEl ? stEl.value : '';
            var cb = tr.querySelector('input[type=checkbox][name$="[do]"]');
            var vis, unchecked = false;
            // Décochée par l'utilisateur (case active mais non cochée) → ignorée (gris).
            // Une case désactivée (skip/pool/invalid) est ignorée par statut, pas "décochée".
            if (cb && !cb.disabled && !cb.checked) {
                vis = 'vis-ignored'; unchecked = true;
            } else if (cb && cb.disabled) {
                vis = 'vis-ignored';                     // ignorée par statut (skip/pool/invalid)
            } else if (status === 'ip_conflict' || status === 'reserved' || status === 'overflow') {
                // Conflit éditable : vert si l'IP ressaisie est valide/libre, rouge sinon.
                var f = tr.querySelector('.ip-field');
                var ip = f ? f.value.trim() : '';
                vis = (ip && ipValidForPreview(ip)) ? 'vis-ok' : 'vis-conflict';
            } else if (status === 'mac_conflict') {
                // keep → reste "conflit" (on ignore la copie) ; update/custom_ip valide → vert.
                var sel = tr.querySelector('select[name$="[mac_action]"], select[name$="[action]"]');
                var act = sel ? sel.value : 'update';
                if (act === 'keep') {
                    vis = 'vis-conflict';
                } else {
                    var mh = tr.querySelector('input[name$="[mac_host_id]"]');
                    var own = mh ? (parseInt(mh.value, 10) || 0) : 0;
                    var mf = tr.querySelector('.ip-field');
                    var mip = mf ? mf.value.trim() : (function () { var h = tr.querySelector('input[name$="[ip]"]'); return h ? h.value.trim() : ''; })();
                    vis = (mip && ipValidForPreview(mip, own)) ? 'vis-ok' : 'vis-conflict';
                }
            } else {
                // clean / ignoré : couleur d'origine.
                vis = baseVis(status);
            }
            setVis(tr, vis);
            setStatusNote(tr, vis, status, unchecked);
            if (vis === 'vis-ok') nOk++;
            else if (vis === 'vis-conflict') nConflict++;
            else nIgnored++;
        }
        setText('cnt-ok', nOk);
        setText('cnt-conflict', nConflict);
        setText('cnt-ignored', nIgnored);
    }
    // Met à jour le texte de statut de la ligne pour qu'il suive l'état résolu.
    function setStatusNote(tr, vis, status, unchecked) {
        var noteEl = tr.querySelector('.status-note');
        if (!noteEl) return;
        var orig = noteEl.getAttribute('data-note') || '';
        var txt;
        if (unchecked) {
            txt = 'Ignorée (décochée)';               // décochée par l'utilisateur
        } else if (vis === 'vis-ignored') {
            txt = orig;                                // skip/pool/invalid → garder le pourquoi
        } else if (vis === 'vis-ok') {
            txt = (status === 'mac_conflict') ? 'Prête (mise à jour)' : 'Prête à créer';
        } else {
            txt = orig;                                // toujours en conflit → note serveur
        }
        if (noteEl.textContent !== txt) noteEl.textContent = txt;
    }
    function setText(id, val) {
        var el = document.getElementById(id);
        if (el) el.textContent = val;
    }

    var activeIpField = null; // cible du prochain clic-trou (capturée au mousedown)

    function attachHoleCapture() {
        var segs = mount.querySelectorAll('.sbg-seg');
        for (var i = 0; i < segs.length; i++) {
            segs[i].addEventListener('mousedown', function () {
                var el = document.activeElement;
                activeIpField = (el && el.classList && el.classList.contains('ip-field')) ? el : null;
            });
        }
    }

    function flash(el) {
        el.classList.add('ip-filled-flash');
        setTimeout(function () { el.classList.remove('ip-filled-flash'); }, 1000);
    }

    var hintEl = null;
    function hint(msg) {
        if (!hintEl) { hintEl = document.createElement('div'); hintEl.className = 'copy-hint'; document.body.appendChild(hintEl); }
        hintEl.textContent = msg;
        hintEl.style.opacity = '1';
        setTimeout(function () { hintEl.style.opacity = '0'; }, 1500);
    }

    function render() {
        var rows = (window.EXISTING_ROWS || []).slice();
        rows = rows.concat(collectPendingRows());
        SBG.init({
            mount: mount,
            rows: rows,
            subnet: SUBNET,
            canEdit: false,
            onDraw: attachHoleCapture, // ré-attaché après CHAQUE draw réel (segments créés) — évite la race du premier paint 0px
            onHoleClick: function (ip) {
                if (!activeIpField) { hint('Clique d\'abord dans un champ IP'); return; }
                activeIpField.value = ip;
                flash(activeIpField);
                // C'est l'événement 'input' qui déclenche le re-render débounce (voir listener plus bas).
                activeIpField.dispatchEvent(new Event('input', { bubbles: true }));
                // 'blur' est inerte ici (rien n'écoute), gardé par cohérence / évolutivité.
                activeIpField.dispatchEvent(new Event('blur', { bubbles: true }));
            }
        });
        // (plus de attachHoleCapture() synchrone : c'est onDraw qui s'en charge, après création réelle des segments)
    }

    var t = null;
    function schedule() { clearTimeout(t); t = setTimeout(render, 150); recolor(); }

    var form = document.getElementById('copy-apply-form');
    if (form) {
        form.addEventListener('change', function (e) {
            if (e.target.matches && (e.target.matches('input[type=checkbox]') || e.target.matches('select'))) { render(); recolor(); }
        });
        form.addEventListener('input', function (e) {
            if (e.target.classList && e.target.classList.contains('ip-field')) schedule();
        });
    }

    render();
    recolor();
}());
