(function (window) {
	'use strict';

	function escapeHtml(value) {
		return String(value == null ? '' : value)
			.replace(/&/g, '&amp;')
			.replace(/</g, '&lt;')
			.replace(/>/g, '&gt;')
			.replace(/"/g, '&quot;');
	}

	function displayValue(value) {
		var text = String(value == null ? '' : value).trim();
		return text === '' ? '&nbsp;' : escapeHtml(text);
	}

	function getConfig() {
		var cfg = window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig;
		return cfg || {
			widthMm: 100,
			heightMm: 100,
			logoMode: 'image',
			logoUrl: '',
			logoTextLine1: '',
			logoTextLine2: '',
			allowBlankCajaNum: false,
			cssUrl: ''
		};
	}

	function resolveLogoConfig(config, job) {
		var source = job || {};
		var base = config || getConfig();
		var mode = source.logo_mode || base.logoMode || 'image';
		var logoUrl = String(source.logo_url != null ? source.logo_url : (base.logoUrl || base.defaultLogoUrl || '')).trim();
		var line1 = String(source.logo_text_line1 != null ? source.logo_text_line1 : (base.logoTextLine1 || '')).trim();
		var line2 = String(source.logo_text_line2 != null ? source.logo_text_line2 : (base.logoTextLine2 || '')).trim();

		if ('image' === mode && logoUrl) {
			return { mode: 'image', logoUrl: logoUrl, line1: line1, line2: line2 };
		}

		return { mode: 'text', logoUrl: logoUrl, line1: line1, line2: line2 };
	}

	function logoHtml(config, job) {
		var logo = resolveLogoConfig(config, job);

		if ('image' === logo.mode && logo.logoUrl) {
			return '<img src="' + escapeHtml(logo.logoUrl) + '" alt="Logo">';
		}

		var line1 = displayValue(logo.line1);
		var line2Html = logo.line2 ? ('<span class="friendly">' + displayValue(logo.line2) + '</span>') : '';

		return (
			'<div class="logistica-prod-label__logo-fallback">' +
				'<span class="eco">' + line1 + '</span>' +
				line2Html +
			'</div>'
		);
	}

	function buildLabelHtml(job, index, config, options) {
		options = options || {};
		var paquetes = String(job.paquetes == null ? '' : job.paquetes).trim();
		var undPorPaquete = String(job.und_por_paquete == null ? '' : job.und_por_paquete).trim();
		var cantidad = String(job.cantidad == null ? '' : job.cantidad).trim();
		var widthMm = config && config.widthMm ? parseInt(config.widthMm, 10) : 100;
		var heightMm = config && config.heightMm ? parseInt(config.heightMm, 10) : 100;
		if (!widthMm || widthMm < 1) {
			widthMm = 100;
		}
		if (!heightMm || heightMm < 1) {
			heightMm = 100;
		}
		var sizeStyle = options.preview
			? ''
			: (' style="width:' + widthMm + 'mm;height:' + heightMm + 'mm;"');
		var className = 'logistica-prod-label' + (options.preview ? ' is-preview' : '');

		var cantidadRow =
			'<div class="logistica-prod-label__row is-split">' +
				'<div class="logistica-prod-label__cell">' +
					'<span class="logistica-prod-label__tag">Cantidad:</span>' +
					'<span class="logistica-prod-label__value is-sm">' + displayNumeric(cantidad) + '</span>' +
				'</div>' +
				'<div class="logistica-prod-label__cell">' +
					'<span class="logistica-prod-label__tag">Caja Nº:</span>' +
					'<span class="logistica-prod-label__value is-sm">' + displayValue(job.caja_num) + '</span>' +
				'</div>' +
			'</div>';

		var paquetesRow =
			'<div class="logistica-prod-label__row is-paquetes">' +
				'<span class="logistica-prod-label__value is-sm">' + displayNumeric(paquetes) + '</span>' +
				'<span class="logistica-prod-label__tag">Paquetes por:</span>' +
				'<span class="logistica-prod-label__value is-sm">' + displayNumeric(undPorPaquete) + '</span>' +
				'<span class="logistica-prod-label__tag">Und.</span>' +
			'</div>';

		return (
			'<article class="' + className + '"' + sizeStyle + ' data-label-index="' + index + '">' +
				'<header class="logistica-prod-label__head">' +
					'<div class="logistica-prod-label__logo">' + logoHtml(config, job) + '</div>' +
					'<div class="logistica-prod-label__cliente">' +
						'<span class="logistica-prod-label__tag">Cliente:</span>' +
						'<span class="logistica-prod-label__value">' + displayValue(job.cliente) + '</span>' +
					'</div>' +
				'</header>' +
				'<div class="logistica-prod-label__row">' +
					'<span class="logistica-prod-label__tag">Producto:</span>' +
					'<span class="logistica-prod-label__value is-sm">' + displayValue(job.producto) + '</span>' +
				'</div>' +
				'<div class="logistica-prod-label__row">' +
					'<span class="logistica-prod-label__tag">Ref:</span>' +
					'<span class="logistica-prod-label__value is-sm">' + displayValue(job.ref) + '</span>' +
				'</div>' +
				cantidadRow +
				paquetesRow +
				'<div class="logistica-prod-label__row is-triple">' +
					'<div class="logistica-prod-label__cell is-triple">' +
						'<span class="logistica-prod-label__tag">OP</span>' +
						'<span class="logistica-prod-label__value">' + displayValue(job.op) + '</span>' +
					'</div>' +
					'<div class="logistica-prod-label__cell is-triple">' +
						'<span class="logistica-prod-label__tag">Fecha</span>' +
						'<span class="logistica-prod-label__value">' + displayValue(job.fecha) + '</span>' +
					'</div>' +
					'<div class="logistica-prod-label__cell is-triple">' +
						'<span class="logistica-prod-label__tag">Operario</span>' +
						'<span class="logistica-prod-label__value">' + displayValue(job.operario) + '</span>' +
					'</div>' +
				'</div>' +
				'<div class="logistica-prod-label__barcode">' +
					'<svg class="logistica-prod-label__barcode-svg" data-barcode-target="' + index + '"></svg>' +
				'</div>' +
			'</article>'
		);
	}

	function buildPrintDocument(jobs, barcode, config) {
		var labels = (jobs || []).map(function (job, index) {
			return buildLabelHtml(job, index, config);
		}).join('');

		var pageSize = config.widthMm + 'mm ' + config.heightMm + 'mm';
		var cssHref = config.cssUrl || '';

		return (
			'<!DOCTYPE html><html lang="es"><head><meta charset="utf-8">' +
			'<title>\u200b</title>' +
			(cssHref ? '<link rel="stylesheet" href="' + escapeHtml(cssHref) + '">' : '') +
			'<style>' +
				'@page{size:' + pageSize + ';margin:0;}' +
				'html,body{margin:0!important;padding:0!important;}' +
				'body.logistica-label-print-doc{background:#fff;}' +
				'.logistica-label-print-sheet{margin:0;padding:0;}' +
			'</style>' +
			'</head><body class="logistica-label-print-doc">' +
			'<div class="logistica-label-print-sheet">' + labels + '</div>' +
			'<script src="' + escapeHtml((window.LogisticaAdmin && window.LogisticaAdmin.jsBarcodeUrl) || '') + '"><\/script>' +
			'<script>window.__LOGISTICA_LABEL_BARCODE=' + JSON.stringify(String(barcode || '')) + ';<\/script>' +
			'<script>(function(){' +
				'function render(){' +
					'if(!window.JsBarcode){return;}' +
					'var code=window.__LOGISTICA_LABEL_BARCODE||"";' +
					'document.querySelectorAll("[data-barcode-target]").forEach(function(node){' +
						'try{JsBarcode(node,code,{format:"EAN13",displayValue:true,fontSize:12,height:40,margin:0,textMargin:1});}catch(e){}' +
					'});' +
					'document.title="\\u200b";' +
				'}' +
				'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",render);}else{render();}' +
			'})();<\/script>' +
			'</body></html>'
		);
	}

	function removePrintFrame() {
		var frame = document.getElementById('logistica-label-print-frame');
		if (frame && frame.parentNode) {
			frame.parentNode.removeChild(frame);
		}
	}

	function printLabels(jobs, barcode) {
		var config = getConfig();
		var html = buildPrintDocument(jobs, barcode, config);

		removePrintFrame();

		var iframe = document.createElement('iframe');
		iframe.id = 'logistica-label-print-frame';
		iframe.setAttribute('aria-hidden', 'true');
		iframe.setAttribute('tabindex', '-1');
		iframe.style.cssText = 'border:0;clip:rect(0,0,0,0);height:0;overflow:hidden;position:fixed;right:0;bottom:0;width:0;';
		document.body.appendChild(iframe);

		return new Promise(function (resolve, reject) {
			var finished = false;

			function finish(error) {
				if (finished) {
					return;
				}
				finished = true;
				iframe.onload = null;
				setTimeout(removePrintFrame, 1500);
				if (error) {
					reject(error);
					return;
				}
				resolve({ count: jobs.length });
			}

			iframe.onload = function () {
				var printWindow = iframe.contentWindow;
				if (!printWindow) {
					finish(new Error('No se pudo preparar la impresión.'));
					return;
				}

				try {
					printWindow.document.title = '\u200b';
				} catch (ignore) {}

				setTimeout(function () {
					try {
						printWindow.focus();
						printWindow.print();
						finish(null);
					} catch (printError) {
						finish(printError);
					}
				}, 450);
			};

			iframe.onerror = function () {
				finish(new Error('No se pudo cargar el documento de impresión.'));
			};

			iframe.srcdoc = html;
		});
	}

	function ensureJsBarcodeLoaded() {
		if (window.JsBarcode) {
			return Promise.resolve();
		}

		var src = window.LogisticaAdmin && window.LogisticaAdmin.jsBarcodeUrl;
		if (!src) {
			return Promise.reject(new Error('JsBarcode no disponible.'));
		}

		return new Promise(function (resolve, reject) {
			var existing = document.querySelector('script[data-logistica-jsbarcode="1"]');
			if (existing) {
				existing.addEventListener('load', function () { resolve(); });
				existing.addEventListener('error', reject);
				return;
			}

			var script = document.createElement('script');
			script.src = src;
			script.async = true;
			script.dataset.logisticaJsbarcode = '1';
			script.onload = function () { resolve(); };
			script.onerror = reject;
			document.head.appendChild(script);
		});
	}

	function renderBarcodesInRoot(root, barcode) {
		if (!root || !window.JsBarcode || !barcode) {
			return;
		}

		root.querySelectorAll('[data-barcode-target]').forEach(function (node) {
			try {
				JsBarcode(node, String(barcode), {
					format: 'EAN13',
					displayValue: true,
					fontSize: 11,
					height: 36,
					margin: 0,
					textMargin: 1
				});
			} catch (error) {
				node.parentNode.innerHTML = '<code>' + escapeHtml(barcode) + '</code>';
			}
		});
	}

	function parseLabelNumber(value) {
		var text = String(value == null ? '' : value).trim().replace(/[^\d.,-]/g, '');
		if (!text) {
			return 0;
		}

		var lastDot = text.lastIndexOf('.');
		var lastComma = text.lastIndexOf(',');

		if (lastDot > -1 && lastComma > -1) {
			if (lastComma > lastDot) {
				text = text.replace(/\./g, '').replace(',', '.');
			} else {
				text = text.replace(/,/g, '');
			}
		} else if (lastDot > -1 && /^\d{1,3}(\.\d{3})+$/.test(text)) {
			text = text.replace(/\./g, '');
		} else if (lastComma > -1) {
			if (/^\d{1,3}(,\d{3})+$/.test(text)) {
				text = text.replace(/,/g, '');
			} else {
				text = text.replace(',', '.');
			}
		}

		var parsed = parseFloat(text);
		return isNaN(parsed) ? 0 : parsed;
	}

	function formatMillar(value) {
		var text = String(value == null ? '' : value).trim();
		if (!text) {
			return '';
		}

		if (!/^[\d.,\s-]+$/.test(text)) {
			return text;
		}

		var parsed = parseLabelNumber(text);
		if (parsed === 0 && text !== '0' && text !== '0,0' && text !== '0.0') {
			return text;
		}

		var isNegative = parsed < 0;
		var absolute = Math.abs(parsed);
		var parts = String(absolute).split('.');
		var intPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
		var result = (isNegative ? '-' : '') + intPart;

		if (parts[1] && parseInt(parts[1], 10) > 0) {
			result += ',' + parts[1].replace(/0+$/, '');
		}

		return result;
	}

	function displayNumeric(value) {
		return displayValue(formatMillar(value));
	}

	function computeCantidad(paquetes, undPorPaquete) {
		var pkg = parseLabelNumber(paquetes);
		var und = parseLabelNumber(undPorPaquete);
		if (pkg <= 0 || und <= 0) {
			return '';
		}
		var total = pkg * und;
		return Number.isInteger(total) ? String(total) : String(Math.round(total * 1000) / 1000);
	}

	function formFieldValue(form, name) {
		if (!form) {
			return '';
		}
		var radio = form.querySelector('[data-label-field="' + name + '"]:checked');
		if (radio) {
			return String(radio.value || '').trim();
		}
		var field = form.querySelector('[data-label-field="' + name + '"]');
		return field ? String(field.value || '').trim() : '';
	}

	function computeTotalProduccion(form) {
		if (!form) {
			return { total: '', detail: '' };
		}

		var paquetes = formFieldValue(form, 'paquetes');
		var undPorPaquete = formFieldValue(form, 'und_por_paquete');
		var cantidadCaja = parseLabelNumber(computeCantidad(paquetes, undPorPaquete));
		var numCajas = parseInt(form.querySelector('[data-label-field="num_cajas"]').value, 10) || 0;
		var includeSaldo = form.querySelector('[data-label-field="include_saldo"]').checked;
		var saldoCantidad = 0;
		var total = 0;
		var detailParts = [];

		if (cantidadCaja > 0 && numCajas > 0) {
			total += cantidadCaja * numCajas;
			detailParts.push(formatMillar(String(numCajas)) + ' cajas × ' + formatMillar(String(cantidadCaja)) + ' und');
		}

		if (includeSaldo) {
			var saldoSinCantidad = form.querySelector('[data-label-field="saldo_sin_cantidad"]').checked;
			if (!saldoSinCantidad) {
				saldoCantidad = parseLabelNumber(computeCantidad(formFieldValue(form, 'paquetes_saldo'), undPorPaquete));
				if (saldoCantidad > 0) {
					total += saldoCantidad;
					detailParts.push('saldo ' + formatMillar(String(saldoCantidad)) + ' und');
				}
			} else {
				detailParts.push('saldo sin cantidad');
			}
		}

		if (total <= 0) {
			if (detailParts.length) {
				return {
					total: '—',
					detail: detailParts.join(' + ')
				};
			}
			return { total: '', detail: '' };
		}

		return {
			total: formatMillar(String(total)),
			detail: detailParts.join(' + ')
		};
	}

	function allowBlankCajaNum(config) {
		config = config || getConfig();
		return !!config.allowBlankCajaNum;
	}

	function resolveCajaNum(form, boxIndex) {
		var field = form ? form.querySelector('[data-label-field="caja_inicio"]') : null;
		var raw = field ? String(field.value || '').trim() : '';
		var start;

		if (allowBlankCajaNum() && raw === '') {
			return '';
		}

		start = parseInt(raw, 10);
		if (isNaN(start) || start < 1) {
			start = 1;
		}

		return String(start + (boxIndex || 0));
	}

	function buildJobsFromForm(form, ctx) {
		var paquetes = form.querySelector('[data-label-field="paquetes"]').value.trim();
		var undPorPaquete = form.querySelector('[data-label-field="und_por_paquete"]').value.trim();
		var cantidad = computeCantidad(paquetes, undPorPaquete);
		var numCajas = parseInt(form.querySelector('[data-label-field="num_cajas"]').value, 10) || 0;
		var includeSaldo = form.querySelector('[data-label-field="include_saldo"]').checked;
		var paquetesSaldo = form.querySelector('[data-label-field="paquetes_saldo"]').value.trim();
		var cantidadSaldo = computeCantidad(paquetesSaldo, undPorPaquete);
		var base = {
			cliente: form.querySelector('[data-label-field="cliente"]').value.trim(),
			producto: form.querySelector('[data-label-field="producto"]').value.trim(),
			ref: form.querySelector('[data-label-field="ref"]').value.trim(),
			paquetes: paquetes,
			und_por_paquete: undPorPaquete,
			logo_mode: formFieldValue(form, 'logo_mode') || 'image',
			logo_url: formFieldValue(form, 'logo_url'),
			logo_text_line1: formFieldValue(form, 'logo_text_line1'),
			logo_text_line2: formFieldValue(form, 'logo_text_line2'),
			op: form.querySelector('[data-label-field="op"]').value.trim(),
			fecha: form.querySelector('[data-label-field="fecha"]').value.trim(),
			operario: form.querySelector('[data-label-field="operario"]').value.trim()
		};
		var jobs = [];
		var i;

		for (i = 0; i < numCajas; i++) {
			jobs.push(Object.assign({}, base, {
				cantidad: cantidad,
				caja_num: resolveCajaNum(form, i)
			}));
		}

		if (includeSaldo) {
			var saldoSinCantidad = form.querySelector('[data-label-field="saldo_sin_cantidad"]').checked;
			jobs.push(Object.assign({}, base, {
				paquetes: saldoSinCantidad ? '' : paquetesSaldo,
				und_por_paquete: saldoSinCantidad ? '' : undPorPaquete,
				cantidad: saldoSinCantidad ? '' : cantidadSaldo,
				caja_num: 'SALDO',
				saldo_sin_cantidad: saldoSinCantidad
			}));
		}

		return jobs;
	}

	function validateJobs(jobs, form) {
		if (!jobs.length) {
			return 'Indica al menos una caja o activa la etiqueta saldo.';
		}

		var paquetes = form.querySelector('[data-label-field="paquetes"]').value.trim();
		var undPorPaquete = form.querySelector('[data-label-field="und_por_paquete"]').value.trim();
		if (!paquetes || parseLabelNumber(paquetes) <= 0) {
			return 'Indica el número de paquetes por caja.';
		}
		if (!undPorPaquete || parseLabelNumber(undPorPaquete) <= 0) {
			return 'Indica las unidades por paquete.';
		}
		if (!computeCantidad(paquetes, undPorPaquete)) {
			return 'Cantidad inválida. Verifica paquetes y unidades por paquete.';
		}

		if (form.querySelector('[data-label-field="include_saldo"]').checked) {
			var saldoSinCantidad = form.querySelector('[data-label-field="saldo_sin_cantidad"]').checked;
			if (!saldoSinCantidad) {
				var paquetesSaldo = form.querySelector('[data-label-field="paquetes_saldo"]').value.trim();
				if (!paquetesSaldo || parseLabelNumber(paquetesSaldo) <= 0) {
					return 'Indica los paquetes para la etiqueta saldo.';
				}
				if (!computeCantidad(paquetesSaldo, undPorPaquete)) {
					return 'Cantidad saldo inválida. Verifica paquetes saldo y unidades por paquete.';
				}
			}
		}

		return '';
	}

	function buildPreviewJobFromForm(form) {
		if (!form) {
			return {};
		}

		var paquetes = form.querySelector('[data-label-field="paquetes"]').value.trim();
		var undPorPaquete = form.querySelector('[data-label-field="und_por_paquete"]').value.trim();

		return {
			cliente: form.querySelector('[data-label-field="cliente"]').value.trim(),
			producto: form.querySelector('[data-label-field="producto"]').value.trim(),
			ref: form.querySelector('[data-label-field="ref"]').value.trim(),
			paquetes: paquetes,
			und_por_paquete: undPorPaquete,
			cantidad: computeCantidad(paquetes, undPorPaquete),
			caja_num: resolveCajaNum(form, 0),
			logo_mode: formFieldValue(form, 'logo_mode') || 'image',
			logo_url: formFieldValue(form, 'logo_url'),
			logo_text_line1: formFieldValue(form, 'logo_text_line1'),
			logo_text_line2: formFieldValue(form, 'logo_text_line2'),
			op: form.querySelector('[data-label-field="op"]').value.trim(),
			fecha: form.querySelector('[data-label-field="fecha"]').value.trim(),
			operario: form.querySelector('[data-label-field="operario"]').value.trim()
		};
	}

	function renderPreview(root, job, barcode) {
		if (!root) {
			return Promise.resolve();
		}

		var config = getConfig();
		var widthMm = config.widthMm || 100;
		var heightMm = config.heightMm || 100;

		root.innerHTML =
			'<div class="logistica-prod-label-preview-scaler" style="--prod-preview-w:' + widthMm + ';--prod-preview-h:' + heightMm + ';">' +
				buildLabelHtml(job || {}, 0, config, { preview: true }) +
			'</div>';

		return ensureJsBarcodeLoaded().then(function () {
			renderBarcodesInRoot(root, barcode);
		});
	}

	window.LogisticaLabelPrint = {
		buildJobsFromForm: buildJobsFromForm,
		validateJobs: validateJobs,
		printLabels: printLabels,
		renderPreview: renderPreview,
		buildLabelHtml: buildLabelHtml,
		computeCantidad: computeCantidad,
		computeTotalProduccion: computeTotalProduccion,
		formatMillar: formatMillar,
		buildPreviewJobFromForm: buildPreviewJobFromForm
	};
}(window));
