/**
 * FlowDino — PDF to thermal image pipeline
 * Vanilla JS, no framework dependency.
 * Used for printing shipping labels on 100×150 mm thermal printers.
 */
(function (global) {
    'use strict';

    // Initialise pdf.js worker — URL passed from PHP via wp_localize_script
    if (typeof pdfjsLib !== 'undefined' && typeof flowdinoPdf !== 'undefined') {
        pdfjsLib.GlobalWorkerOptions.workerSrc = flowdinoPdf.workerSrc;
    }

    // =========================================================================
    // cropToMainLabel(canvas, forceFullWidth)
    // Detects the label zone inside an A4-sized rendered canvas.
    // =========================================================================
    function cropToMainLabel(canvas, forceFullWidth) {
        forceFullWidth = forceFullWidth || false;

        var ctx    = canvas.getContext('2d');
        var width  = canvas.width;
        var height = canvas.height;
        var data   = ctx.getImageData(0, 0, width, height).data;

        var DARK_T    = 80;
        var DENSITY_T = 0.20;
        var STRIP_W   = Math.max(4, Math.round(width / 60));

        // --- Horizontal detection: find the strip with the longest dense run ---
        var bestStrip = 0;
        var maxRun    = 0;

        for (var sx = 0; sx < width; sx += STRIP_W) {
            var sw  = Math.min(STRIP_W, width - sx);
            var run = 0;
            var cur = 0;
            for (var y = 0; y < height; y++) {
                var darkCount = 0;
                for (var px = sx; px < sx + sw; px++) {
                    var i = (y * width + px) * 4;
                    if (data[i] < DARK_T && data[i + 1] < DARK_T && data[i + 2] < DARK_T) {
                        darkCount++;
                    }
                }
                var density = darkCount / sw;
                if (density > DENSITY_T) {
                    cur++;
                    if (cur > run) {
                        run = cur;
                    }
                } else {
                    cur = 0;
                }
            }
            if (run > maxRun) {
                maxRun    = run;
                bestStrip = sx;
            }
        }

        // Expand horizontally
        var cropX0 = bestStrip;
        var cropX1 = bestStrip + STRIP_W;

        // Expand left
        for (var lx = bestStrip - STRIP_W; lx >= 0; lx -= STRIP_W) {
            var lw  = Math.min(STRIP_W, width - lx);
            var run2 = 0;
            var cur2 = 0;
            for (var y2 = 0; y2 < height; y2++) {
                var dc = 0;
                for (var px2 = lx; px2 < lx + lw; px2++) {
                    var i2 = (y2 * width + px2) * 4;
                    if (data[i2] < DARK_T && data[i2 + 1] < DARK_T && data[i2 + 2] < DARK_T) dc++;
                }
                if (dc / lw > DENSITY_T) { cur2++; if (cur2 > run2) run2 = cur2; } else { cur2 = 0; }
            }
            if (run2 >= maxRun * 0.10) {
                cropX0 = lx;
            } else {
                break;
            }
        }

        // Expand right
        for (var rx = bestStrip + STRIP_W; rx < width; rx += STRIP_W) {
            var rw  = Math.min(STRIP_W, width - rx);
            var run3 = 0;
            var cur3 = 0;
            for (var y3 = 0; y3 < height; y3++) {
                var dc3 = 0;
                for (var px3 = rx; px3 < rx + rw; px3++) {
                    var i3 = (y3 * width + px3) * 4;
                    if (data[i3] < DARK_T && data[i3 + 1] < DARK_T && data[i3 + 2] < DARK_T) dc3++;
                }
                if (dc3 / rw > DENSITY_T) { cur3++; if (cur3 > run3) run3 = cur3; } else { cur3 = 0; }
            }
            if (run3 >= maxRun * 0.10) {
                cropX1 = rx + rw;
            } else {
                break;
            }
        }

        // Layout detection
        var labelWidthFraction = (cropX1 - cropX0) / width;
        var isColumnLayout     = labelWidthFraction < 0.70;
        var isRightColumn      = isColumnLayout && cropX0 > width * 0.25;

        var finalX0, finalX1;
        if (forceFullWidth) {
            finalX0 = 0;
            finalX1 = width;
        } else if (isRightColumn) {
            finalX0 = cropX0;
            finalX1 = width;
        } else if (isColumnLayout) {
            finalX0 = 0;
            finalX1 = cropX1;
        } else {
            finalX0 = 0;
            finalX1 = width;
        }

        // --- Vertical detection ---
        var SPARSE_T    = 0.03;
        var SPARSE_ROWS = Math.max(100, Math.round(height * 0.05));
        var pad         = 14;

        // Find barcode strip Y range (using finalX0/finalX1 for density)
        var barcodeY0 = 0;
        var barcodeY1 = height - 1;

        // Find first dense row
        for (var fy = 0; fy < height; fy++) {
            var dc4 = 0;
            for (var fx = finalX0; fx < finalX1; fx++) {
                var i4 = (fy * width + fx) * 4;
                if (data[i4] < DARK_T && data[i4 + 1] < DARK_T && data[i4 + 2] < DARK_T) dc4++;
            }
            if (dc4 / (finalX1 - finalX0) > SPARSE_T) { barcodeY0 = fy; break; }
        }

        // Expand barcodeY0 upward: stop at SPARSE_ROWS consecutive sparse rows
        var cropY0 = barcodeY0;
        var sparseCount = 0;
        for (var uy = barcodeY0 - 1; uy >= 0; uy--) {
            var dc5 = 0;
            for (var ux = finalX0; ux < finalX1; ux++) {
                var i5 = (uy * width + ux) * 4;
                if (data[i5] < DARK_T && data[i5 + 1] < DARK_T && data[i5 + 2] < DARK_T) dc5++;
            }
            if (dc5 / (finalX1 - finalX0) < SPARSE_T) {
                sparseCount++;
                if (sparseCount >= SPARSE_ROWS) {
                    cropY0 = uy + sparseCount;
                    break;
                }
            } else {
                sparseCount = 0;
                cropY0      = uy;
            }
        }

        // Find last dense row from bottom
        var cropY1 = height - 1;
        for (var by = height - 1; by >= 0; by--) {
            var dc6 = 0;
            for (var bx = finalX0; bx < finalX1; bx++) {
                var i6 = (by * width + bx) * 4;
                if (data[i6] < DARK_T && data[i6 + 1] < DARK_T && data[i6 + 2] < DARK_T) dc6++;
            }
            if (dc6 / (finalX1 - finalX0) > SPARSE_T) { cropY1 = by; break; }
        }

        cropY0 = Math.max(0, cropY0 - pad);
        cropY1 = Math.min(height - 1, cropY1 + pad);

        var outW  = finalX1 - finalX0;
        var outH  = cropY1 - cropY0;
        var out   = document.createElement('canvas');
        out.width  = outW;
        out.height = outH;
        out.getContext('2d').drawImage(canvas, finalX0, cropY0, outW, outH, 0, 0, outW, outH);
        return out;
    }

    // =========================================================================
    // isCropContentRotated(canvas)
    // Returns true if the label image is rotated 90° CW (e.g. MR Belgique Vinted).
    // =========================================================================
    function isCropContentRotated(canvas) {
        var ctx    = canvas.getContext('2d');
        var width  = canvas.width;
        var height = canvas.height;
        var data   = ctx.getImageData(0, 0, width, height).data;

        var DARK_T    = 80;
        var SAMPLE_H  = 30;
        var SAMPLE_V  = 30;
        var stepH     = Math.max(1, Math.round(width / 150));
        var stepV     = Math.max(1, Math.round(height / 150));
        var totalH    = 0;
        var totalV    = 0;

        // Horizontal transitions
        for (var si = 0; si < SAMPLE_H; si++) {
            var y    = Math.round((si + 0.5) * (height / SAMPLE_H));
            y        = Math.min(y, height - 1);
            var prev = false;
            var trans = 0;
            for (var x = 0; x < width; x += stepH) {
                var i    = (y * width + x) * 4;
                var dark = data[i] < DARK_T && data[i + 1] < DARK_T && data[i + 2] < DARK_T;
                if (x > 0 && dark !== prev) trans++;
                prev = dark;
            }
            totalH += trans;
        }

        // Vertical transitions
        for (var sj = 0; sj < SAMPLE_V; sj++) {
            var cx   = Math.round((sj + 0.5) * (width / SAMPLE_V));
            cx       = Math.min(cx, width - 1);
            var prevV = false;
            var transV = 0;
            for (var yv = 0; yv < height; yv += stepV) {
                var iv   = (yv * width + cx) * 4;
                var darkV = data[iv] < DARK_T && data[iv + 1] < DARK_T && data[iv + 2] < DARK_T;
                if (yv > 0 && darkV !== prevV) transV++;
                prevV = darkV;
            }
            totalV += transV;
        }

        var avgH = totalH / SAMPLE_H;
        var avgV = totalV / SAMPLE_V;
        return avgV > avgH * 1.4;
    }

    // =========================================================================
    // rotateCCW(canvas)
    // Returns a new canvas rotated 90° counter-clockwise.
    // =========================================================================
    function rotateCCW(src) {
        var dst    = document.createElement('canvas');
        dst.width  = src.height;
        dst.height = src.width;
        var ctx    = dst.getContext('2d');
        ctx.translate(0, src.width);
        ctx.rotate(-Math.PI / 2);
        ctx.drawImage(src, 0, 0);
        return dst;
    }

    // =========================================================================
    // renderPdfToCanvas(dataUri, isThermal) → Promise<canvas>
    // Renders page 1 of a PDF and applies crop logic.
    // =========================================================================
    function renderPdfToCanvas(dataUri, isThermal) {
        isThermal = isThermal !== false;
        // Scale 2.0 gives ~1190px wide from A4 — plenty to crop to 787px (100mm@200dpi)
        // Scale 1.5 for original is sufficient for screen/print quality
        var scale  = isThermal ? 2.0 : 1.5;

        return pdfjsLib.getDocument(dataUri).promise.then(function (pdf) {
            return pdf.getPage(1);
        }).then(function (page) {
            var viewport = page.getViewport({ scale: scale });
            var canvas   = document.createElement('canvas');
            canvas.width  = viewport.width;
            canvas.height = viewport.height;

            return page.render({
                canvasContext: canvas.getContext('2d'),
                viewport:      viewport,
            }).promise.then(function () {
                if (!isThermal) return canvas;

                // Detect natively small PDF (thermal native — e.g. 100×150 mm ≈ 283×425 pts)
                var native       = page.getViewport({ scale: 1 });
                var nativeLong   = Math.max(native.width, native.height);
                var nativelySmall = nativeLong < 700;

                if (nativelySmall) return canvas;

                // First crop pass
                var cropped = cropToMainLabel(canvas, false);

                // Rotation detection
                if (isCropContentRotated(cropped)) {
                    // Second crop pass (force full width) then rotate
                    var cropped2 = cropToMainLabel(canvas, true);
                    return rotateCCW(cropped2);
                }

                return cropped;
            });
        });
    }

    // =========================================================================
    // INTERNAL — fetchPdfViaProxy(labelUrl, ajaxUrl, nonce) → Promise<dataUri>
    // =========================================================================
    function fetchPdfViaProxy(labelUrl, ajaxUrl, nonce) {
        var url = ajaxUrl
            + '?action=flowdino_proxy_label'
            + '&nonce=' + encodeURIComponent(nonce)
            + '&url='   + encodeURIComponent(labelUrl);

        return fetch(url)
            .then(function (resp) {
                if (!resp.ok) { throw new Error('HTTP ' + resp.status); }
                return resp.json();
            })
            .then(function (json) {
                if (!json.success) { throw new Error(json.data || 'Proxy error'); }
                return json.data; // "data:application/pdf;base64,..."
            });
    }

    // =========================================================================
    // INTERNAL — renderAllPdfPages(dataUri) → Promise<canvas[]>
    // =========================================================================
    function renderAllPdfPages(dataUri) {
        return pdfjsLib.getDocument(dataUri).promise
            .then(function (pdf) {
                // Render all pages in parallel for speed
                var pagePromises = [];
                for (var n = 1; n <= pdf.numPages; n++) {
                    pagePromises.push(pdf.getPage(n).then(function (page) {
                        var viewport = page.getViewport({ scale: 1.5 });
                        var canvas   = document.createElement('canvas');
                        canvas.width  = viewport.width;
                        canvas.height = viewport.height;
                        return page.render({
                            canvasContext: canvas.getContext('2d'),
                            viewport:      viewport,
                        }).promise.then(function () {
                            return { pageNum: page.pageNumber, canvas: canvas };
                        });
                    }));
                }
                return Promise.all(pagePromises);
            })
            .then(function (results) {
                // Sort by page number (parallel rendering may complete out of order)
                results.sort(function (a, b) { return a.pageNum - b.pageNum; });
                return results.map(function (r) { return r.canvas; });
            });
    }

    // =========================================================================
    // INTERNAL — writeDocAndPrint(win, cssText, bodyHtml)
    // Populates an already-open popup using DOM API — no document.write(), no
    // HTML closing-tag string literals in source. Safe even if a caching plugin
    // inlines or minifies this file (minifiers strip backslash-escapes like <\/,
    // turning them back into </ which breaks the surrounding HTML script block).
    // =========================================================================
    function writeDocAndPrint(win, cssText, bodyHtml) {
        var style = win.document.createElement('style');
        style.textContent = cssText;
        win.document.head.appendChild(style);
        win.document.body.innerHTML = bodyHtml;
        win.focus();
        setTimeout(function () { win.print(); }, 400);
    }

    // =========================================================================
    // INTERNAL — showLoadingMessage(win)
    // Shows a loading indicator in the popup via DOM — no HTML strings at all.
    // =========================================================================
    function showLoadingMessage(win) {
        var p = win.document.createElement('p');
        p.style.fontFamily = 'sans-serif';
        p.style.padding    = '20px';
        p.textContent      = 'Chargement\u2026';
        win.document.body.appendChild(p);
    }

    // =========================================================================
    // INTERNAL — setBusy(btn, busy)
    // =========================================================================
    function setBusy(btn, busy) {
        btn.disabled = busy;
        if (busy) {
            btn.dataset.originalText = btn.textContent;
            btn.textContent          = 'Chargement\u2026';
        } else {
            btn.textContent = btn.dataset.originalText || btn.textContent;
        }
    }

    // =========================================================================
    // INTERNAL — printViaIframe(cssText, bodyHtml)
    // Prints via a hidden off-screen iframe so the entire processing pipeline
    // runs in the current tab — avoids background-tab JS throttling that occurs
    // when window.open() transfers focus to a new tab.
    // =========================================================================
    function printViaIframe(cssText, bodyHtml) {
        var iframe = document.createElement('iframe');
        iframe.style.cssText = 'position:fixed;left:-10000px;top:0;width:210mm;height:297mm;border:none;';
        document.body.appendChild(iframe);
        var iDoc = iframe.contentWindow.document;
        var style = iDoc.createElement('style');
        style.textContent = cssText;
        iDoc.head.appendChild(style);
        iDoc.body.innerHTML = bodyHtml;
        setTimeout(function () {
            iframe.contentWindow.focus();
            iframe.contentWindow.print();
            setTimeout(function () {
                if (iframe.parentNode) { iframe.parentNode.removeChild(iframe); }
            }, 1000);
        }, 200);
    }

    // =========================================================================
    // PUBLIC — printOriginalLabel(btn)
    // Renders all PDF pages via proxy + pdf.js, then prints via hidden iframe.
    // =========================================================================
    function printOriginalLabel(btn) {
        if (typeof pdfjsLib === 'undefined') {
            alert('pdf.js non charg\u00e9. Rechargez la page et r\u00e9essayez.');
            return;
        }

        setBusy(btn, true);

        fetchPdfViaProxy(btn.dataset.url, btn.dataset.ajax, btn.dataset.nonce)
            .then(function (dataUri) { return renderAllPdfPages(dataUri); })
            .then(function (canvases) {
                var images = canvases.map(function (c) {
                    return '<img src="' + c.toDataURL('image/jpeg', 0.85) + '" style="display:block;width:100%;page-break-after:always;">';
                }).join('');

                printViaIframe(
                    '@page{margin:0;size:auto;}body{margin:0;}img{max-width:100%;height:auto;display:block;}',
                    images
                );
            })
            .catch(function (err) { alert('Erreur\u00a0: ' + err.message); })
            .then(function () { setBusy(btn, false); });
    }

    // =========================================================================
    // PUBLIC — printThermalLabel(btn)
    // Fetches, crops and composes a 100×150 mm label, then prints via hidden iframe.
    // =========================================================================
    function printThermalLabel(btn) {
        if (typeof pdfjsLib === 'undefined') {
            alert('pdf.js non charg\u00e9. Rechargez la page et r\u00e9essayez.');
            return;
        }

        setBusy(btn, true);

        fetchPdfViaProxy(btn.dataset.url, btn.dataset.ajax, btn.dataset.nonce)
            .then(function (dataUri) { return renderPdfToCanvas(dataUri, true); })
            .then(function (canvas) {
                var DPI      = 200;
                var MM_TO_PX = DPI / 25.4;
                var pageW    = Math.round(100 * MM_TO_PX);
                var pageH    = Math.round(150 * MM_TO_PX);
                var TITLE_PX = Math.round(9   * MM_TO_PX);
                var imgH     = pageH - TITLE_PX;
                var title    = btn.dataset.title || '';

                var out    = document.createElement('canvas');
                out.width  = pageW;
                out.height = pageH;
                var ctx    = out.getContext('2d');

                ctx.fillStyle = '#ffffff';
                ctx.fillRect(0, 0, pageW, pageH);
                ctx.drawImage(canvas, 0, 0, pageW, imgH);

                if (title) {
                    var fontSize = Math.round(2.5 * MM_TO_PX);
                    ctx.font      = 'bold ' + fontSize + 'px Arial, sans-serif';
                    ctx.fillStyle = '#000000';
                    ctx.textAlign = 'center';

                    var maxWidth = pageW - 20;
                    var lineH    = fontSize * 1.3;
                    var baseY    = imgH + (TITLE_PX - lineH) / 2 + fontSize;
                    var words    = title.split(' ');
                    var lines    = [];
                    var line     = '';

                    for (var wi = 0; wi < words.length; wi++) {
                        var test = line ? line + ' ' + words[wi] : words[wi];
                        if (ctx.measureText(test).width > maxWidth && line) {
                            lines.push(line);
                            line = words[wi];
                            if (lines.length >= 2) { break; }
                        } else {
                            line = test;
                        }
                    }
                    if (line && lines.length < 2) { lines.push(line); }

                    if (lines.length === 1) {
                        ctx.fillText(lines[0], pageW / 2, baseY, maxWidth);
                    } else {
                        ctx.fillText(lines[0], pageW / 2, baseY - lineH / 2, maxWidth);
                        ctx.fillText(lines[1] || '', pageW / 2, baseY + lineH / 2, maxWidth);
                    }
                }

                var jpeg = out.toDataURL('image/jpeg', 0.85);

                printViaIframe(
                    '@page{size:100mm 150mm;margin:0;}body{margin:0;}img{display:block;width:100mm;height:150mm;}',
                    '<img src="' + jpeg + '">'
                );
            })
            .catch(function (err) { alert('Erreur\u00a0: ' + err.message); })
            .then(function () { setBusy(btn, false); });
    }

    // =========================================================================
    // Attach event listeners once DOM is ready
    // =========================================================================
    function attachListeners() {
        document.querySelectorAll('.flowdino-print-original').forEach(function (btn) {
            btn.addEventListener('click', function () { printOriginalLabel(btn); });
        });
        document.querySelectorAll('.flowdino-print-thermal').forEach(function (btn) {
            btn.addEventListener('click', function () { printThermalLabel(btn); });
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', attachListeners);
    } else {
        attachListeners();
    }

    // Expose on global for optional external use
    global.flowdinoLabel = {
        printOriginalLabel: printOriginalLabel,
        printThermalLabel:  printThermalLabel,
    };

}(window));
