"use strict";

const API_ENDPOINT = "https://tipwaste-api.deploy.ecoquimic.com/containermeasurement/rfid";
const RFID_WAITING_LABEL = "Esperando RFID";
const RFID_ERROR_LABEL = "RFID no disponible";
const SCALE_CONNECTING_LABEL = "Conectando...";
const SCALE_ERROR_LABEL = "Báscula no disponible";
const SCALE_READY_LABEL = "Báscula conectada";

const screens = {
	measure: document.getElementById("screen-measure"),
	networks: document.getElementById("screen-networks"),
	password: document.getElementById("screen-password"),
	scaleConfig: document.getElementById("screen-scale-config"),
};

const versionEl = document.getElementById("app-version");
const connectionLabel = document.getElementById("connection-label");
const openWifiBtn = document.getElementById("open-wifi");

const rfidDisplay = document.getElementById("rfid-display");
const weightDisplay = document.getElementById("weight-display");
const measureFeedbackEl = document.getElementById("measure-feedback");
const measureDisplay = document.getElementById("measure-display");
const measureDetails = document.getElementById("measure-details");
const resultDateEl = document.getElementById("result-date");
const resultContainerEl = document.getElementById("result-container");
const resultZoneEl = document.getElementById("result-zone");
const resultWeightEl = document.getElementById("result-weight");

let rfidStatus = {
	tag: null,
	rssi: null,
	tags: [],
	updatedAt: null,
	ready: null,
	error: null,
	port: null,
};

let scaleStatus = {
	weight: null,
	rawWeight: null,
	updatedAt: null,
	ready: null,
	error: null,
	port: null,
};

let rfidUnsubscribe = null;
let scaleUnsubscribe = null;
let scaleSetupLogUnsubscribe = null;
const setupLogBuffer = [];

// Auto-send logic
const STABLE_TIME_MS = 3000; // 3 seconds to consider weight stable
const MIN_WEIGHT_THRESHOLD = 5; // Minimum weight in kg to consider "container present"
let lastStableWeight = null;
let stableStartTime = null;
let isSending = false;
let lastSentRfid = null; // RFID that was last sent successfully
let lastSentWeight = null; // Weight that was last sent

// Scale config screen elements
const openScaleConfigBtn = document.getElementById("open-scale-config");
const scalePortInput = document.getElementById("scale-port");
const rfidPortInput = document.getElementById("rfid-port");
const scaleBaudSelect = document.getElementById("scale-baud");
const scaleSlaveIdInput = document.getElementById("scale-slave-id");
const scaleStartAddrInput = document.getElementById("scale-start-addr");
const scaleWordOrderSelect = document.getElementById("scale-word-order");
const scaleRegistersSelect = document.getElementById("scale-registers");
const scalePollIntervalInput = document.getElementById("scale-poll-interval");
const scaleDivisorInput = document.getElementById("scale-divisor");
const scaleConfigStatusEl = document.getElementById("scale-config-status");
const scaleConfigBackBtn = document.getElementById("scale-config-back");
const scaleOpenLogsBtn = document.getElementById("scale-open-logs");
const scaleConfigSaveBtn = document.getElementById("scale-config-save");
const scaleAppRestartBtn = document.getElementById("scale-app-restart");
const scaleKnownWeightInput = document.getElementById("scale-known-weight");
const scaleKnownWeightKeypad = document.getElementById("scale-known-weight-keypad");
const scaleAutoSetupBtn = document.getElementById("scale-auto-setup");
const scaleAutoSetupRs4Btn = document.getElementById("scale-auto-setup-rs4");
const scaleConfigFeedbackEl = document.getElementById("scale-config-feedback");
const scalePortScanBtn = document.getElementById("scale-port-scan");

// Scale status indicator elements
const scaleStatusIndicator = document.getElementById("scale-status-indicator");
const scaleStatusText = document.getElementById("scale-status-text");

function parseWeightInput(value) {
	return Number.parseFloat(String(value ?? "").trim().replace(",", "."));
}

const SCALE_WEIGHT_NUMPAD_LAYOUT = [
	["7", "8", "9"],
	["4", "5", "6"],
	["1", "2", "3"],
	[",", "0", "."],
	["backspace", "clear", "close"],
];

const networksMessageEl = document.getElementById("networks-message");
const networksListEl = document.getElementById("networks-list");
const networksRefreshBtn = document.getElementById("networks-refresh");
const networksBackBtn = document.getElementById("networks-back");
const networksFeedbackEl = document.getElementById("networks-feedback");

const passwordTitleEl = document.getElementById("password-title");
const passwordField = document.getElementById("password-field");
const passwordConnectBtn = document.getElementById("password-connect");
const passwordBackBtn = document.getElementById("password-back");
const passwordFeedbackEl = document.getElementById("password-feedback");
const keyboardContainer = document.getElementById("keyboard");

// Teclado estilo móvil: tres layouts (letras, números, símbolos)
const LAYOUT_LETTERS = [
	["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
	["a", "s", "d", "f", "g", "h", "j", "k", "l", "\u00F1"],
	["shift", "z", "x", "c", "v", "b", "n", "m", "backspace"],
	["layoutNumbers", "space", "clear"],
];

const LAYOUT_NUMBERS = [
	["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
	["-", "/", ":", ";", "(", ")", "\u20AC", "&", "@", "\""],
	["layoutSymbols", ".", ",", "?", "!", "'", "backspace"],
	["layoutLetters", "space", "clear"],
];

const LAYOUT_SYMBOLS = [
	["[", "]", "{", "}", "#", "%", "^", "*", "+", "="],
	["_", "\\", "|", "~", "<", ">", "\u00A3", "\u00A5", "\u2022", "\u00A7"],
	["layoutNumbers", ".", ",", "?", "!", "'", "backspace"],
	["layoutLetters", "space", "clear"],
];

const KEYBOARD_LAYOUTS = {
	letters: LAYOUT_LETTERS,
	numbers: LAYOUT_NUMBERS,
	symbols: LAYOUT_SYMBOLS,
};

// Mayús en letras: mayúsculas
const NUMBER_SHIFT_MAP = {
	"1": "!", "2": "\"", "3": "#", "4": "$", "5": "%",
	"6": "&", "7": "/", "8": "(", "9": ")", "0": "=",
};

const ACTION_KEY_CONFIG = {
	shift: { label: "\u21E7", className: "key-btn--wide" },
	space: { label: "espacio", className: "key-btn--space" },
	backspace: { label: "\u232B", className: "key-btn--wide" },
	clear: { label: "Reset", className: "key-btn--wide" },
	layoutNumbers: { label: "123", className: "key-btn--layout" },
	layoutLetters: { label: "ABC", className: "key-btn--layout" },
	layoutSymbols: { label: "#+=", className: "key-btn--layout" },
};

const NETWORK_MANAGER_HINT = [
	"NetworkManager no esta activo.",
	"Ejecuta:",
	"sudo systemctl enable --now NetworkManager",
	"sudo systemctl restart NetworkManager",
].join("\n");

let availableNetworks = [];
let selectedNetwork = null;
let shiftEnabled = false;
let currentKeyboardLayout = "letters"; // "letters" | "numbers" | "symbols"
let keyboardBuilt = false;
let configuredScalePort = "";
let configuredRfidPort = "";

const appShell = document.getElementById("app-shell");

function showSuccessBackground() {
	appShell?.classList.add("app-shell--success");
}

function hideSuccessBackground() {
	appShell?.classList.remove("app-shell--success");
}

function setScreen(name) {
	Object.entries(screens).forEach(([key, element]) => {
		if (!element) {
			return;
		}
		if (key === name) {
			element.classList.remove("hidden");
		} else {
			element.classList.add("hidden");
		}
	});
}

function setFeedback(element, message = "", type = "normal") {
	if (!element) {
		return;
	}
	element.textContent = message || "";
	element.classList.remove("feedback--error", "feedback--success");
	if (type === "error" || type === true) {
		element.classList.add("feedback--error");
	} else if (type === "success") {
		element.classList.add("feedback--success");
	}
}

function renderRfidState() {
	if (!rfidDisplay) {
		return;
	}

	if (rfidStatus.tag) {
		rfidDisplay.textContent = rfidStatus.tag;
	} else if (rfidStatus.ready === false) {
		rfidDisplay.textContent = rfidStatus.error || RFID_ERROR_LABEL;
	} else {
		rfidDisplay.textContent = RFID_WAITING_LABEL;
	}
}

function renderScaleState() {
	// Update weight display - but freeze it if we already sent this measurement
	if (lastSentWeight !== null && lastSentRfid !== null) {
		// Keep showing the sent weight, don't update
		renderWeightDisplay(lastSentWeight);
	} else if (scaleStatus.weight !== null && scaleStatus.ready === true) {
		renderWeightDisplay(scaleStatus.weight);
	} else {
		renderWeightDisplay("--");
	}

	// Update status indicator
	if (!scaleStatusIndicator || !scaleStatusText) {
		return;
	}

	if (scaleStatus.ready === true) {
		scaleStatusIndicator.classList.remove("scale-indicator--error", "scale-indicator--connecting");
		scaleStatusIndicator.classList.add("scale-indicator--ready");
		scaleStatusText.textContent = SCALE_READY_LABEL;
	} else if (scaleStatus.ready === false) {
		scaleStatusIndicator.classList.remove("scale-indicator--ready", "scale-indicator--connecting");
		scaleStatusIndicator.classList.add("scale-indicator--error");
		scaleStatusText.textContent = scaleStatus.error || SCALE_ERROR_LABEL;
	} else {
		scaleStatusIndicator.classList.remove("scale-indicator--ready", "scale-indicator--error");
		scaleStatusIndicator.classList.add("scale-indicator--connecting");
		scaleStatusText.textContent = SCALE_CONNECTING_LABEL;
	}
}

function applyScaleStatus(status) {
	if (!status || typeof status !== "object") {
		return;
	}

	const previousWeight = scaleStatus.weight;

	if ("weight" in status) {
		const numericWeight = Number(status.weight);
		scaleStatus.weight = Number.isFinite(numericWeight) ? Math.max(0, numericWeight) : status.weight;
	}

	if ("rawWeight" in status) {
		scaleStatus.rawWeight = status.rawWeight;
	}

	if ("updatedAt" in status) {
		scaleStatus.updatedAt = status.updatedAt;
	}

	if ("ready" in status) {
		scaleStatus.ready = status.ready === null ? null : Boolean(status.ready);
	}

	if ("error" in status) {
		scaleStatus.error = status.error ? String(status.error) : null;
	}

	if ("port" in status) {
		scaleStatus.port = status.port ? String(status.port) : null;
	}

	renderScaleState();

	// Always check for stable weight on each update
	checkStableWeight();
}

function isWeightStable(currentWeight, lastWeight) {
	if (lastWeight === null) return false;
	// Consider stable if difference is less than or equal to 0.5 kg
	return Math.abs(currentWeight - lastWeight) <= 0.5;
}

function checkStableWeight() {
	const currentWeight = scaleStatus.weight;
	const currentRfid = rfidStatus.tag;
	const now = Date.now();

	// Don't process if we're currently sending
	if (isSending) {
		return;
	}

	// Check if container was removed (weight dropped below threshold)
	if (currentWeight === null || currentWeight < MIN_WEIGHT_THRESHOLD) {
		// Container removed - reset everything including sent RFID
		if (lastSentRfid !== null) {
			console.log("[Auto-envío] Contenedor retirado, peso bajo:", currentWeight);
			resetAfterContainerRemoved();
		} else {
			resetStableTracking();
		}
		return;
	}

	// Check if this RFID was already sent (don't send same RFID twice)
	if (lastSentRfid !== null && currentRfid === lastSentRfid) {
		// Already sent this RFID, wait for container to be removed
		setFeedback(measureFeedbackEl, "✓ Enviado. Retire el contenedor.", "success");
		return;
	}

	// Check if weight is stable (within 0.5 kg tolerance)
	if (!isWeightStable(currentWeight, lastStableWeight)) {
		// Weight changed significantly - start new tracking
		lastStableWeight = currentWeight;
		stableStartTime = now;
		updateStabilityFeedback();
		return;
	}

	// Weight is stable - check if stable long enough
	if (stableStartTime) {
		const stableDuration = now - stableStartTime;

		if (stableDuration >= STABLE_TIME_MS) {
			// Weight has been stable for 3 seconds - auto send
			triggerAutoSend();
		} else {
			updateStabilityFeedback();
		}
	}
}

function resetStableTracking() {
	lastStableWeight = null;
	stableStartTime = null;
	if (!isSending && lastSentRfid === null) {
		setFeedback(measureFeedbackEl, "", false);
	}
}

function resetAfterContainerRemoved() {
	console.log("[Auto-envío] Reseteando después de retirar contenedor");
	lastStableWeight = null;
	stableStartTime = null;
	lastSentRfid = null;
	lastSentWeight = null;

	// Clear RFID display
	rfidStatus.tag = null;
	renderRfidState();

	setFeedback(measureFeedbackEl, "", false);
	measureDisplay?.classList.remove("measure-display--compact");
	measureDetails?.classList.add("hidden");

	// Remove green background
	hideSuccessBackground();
}

function updateStabilityFeedback() {
	if (isSending) {
		return;
	}

	// Check if this RFID was already sent
	if (lastSentRfid !== null && rfidStatus.tag === lastSentRfid) {
		setFeedback(measureFeedbackEl, "✓ Enviado. Retire el contenedor.", "success");
		return;
	}

	// Check if we have RFID
	if (!rfidStatus.tag) {
		setFeedback(measureFeedbackEl, "Esperando RFID...", false);
		return;
	}

	if (stableStartTime && lastStableWeight !== null && lastStableWeight >= MIN_WEIGHT_THRESHOLD) {
		const remaining = Math.ceil((STABLE_TIME_MS - (Date.now() - stableStartTime)) / 1000);
		if (remaining > 0) {
			setFeedback(measureFeedbackEl, `Peso estable... enviando en ${remaining}s`, false);
		}
	}
}

async function triggerAutoSend() {
	// Validate before sending
	if (!rfidStatus.tag) {
		setFeedback(measureFeedbackEl, "Esperando RFID para enviar.", false);
		return;
	}

	if (scaleStatus.weight === null || scaleStatus.weight < MIN_WEIGHT_THRESHOLD) {
		return;
	}

	// Don't send if this RFID was already sent
	if (lastSentRfid !== null && rfidStatus.tag === lastSentRfid) {
		return;
	}

	console.log("[Auto-envío] Enviando automáticamente - RFID:", rfidStatus.tag, "Peso:", scaleStatus.weight);
	await sendMeasurement();
}

async function initializeScale() {
	if (!window.scaleApi) {
		console.warn("scaleApi no esta disponible en el contexto de la ventana.");
		applyScaleStatus({ ready: false, error: "Báscula no disponible" });
		return;
	}

	try {
		const status = await window.scaleApi.getStatus();
		applyScaleStatus(status);
	} catch (error) {
		console.warn("No se pudo obtener el estado de la báscula", error);
		applyScaleStatus({
			ready: false,
			error: error?.message || String(error),
		});
	}

	if (typeof window.scaleApi.onUpdate === "function") {
		if (typeof scaleUnsubscribe === "function") {
			scaleUnsubscribe();
		}
		scaleUnsubscribe = window.scaleApi.onUpdate((status) => {
			applyScaleStatus(status);
		});
	}
}

function applyRfidStatus(status) {
	if (!status || typeof status !== "object") {
		return;
	}

	if ("tag" in status) {
		const value = typeof status.tag === "string" && status.tag.trim().length ? status.tag.trim().toUpperCase() : null;
		rfidStatus.tag = value;
	}

	if ("updatedAt" in status) {
		const value = Number.isFinite(status.updatedAt) ? status.updatedAt : null;
		rfidStatus.updatedAt = value;
	}

	if ("ready" in status) {
		rfidStatus.ready = status.ready === null ? null : Boolean(status.ready);
	}

	if ("error" in status) {
		rfidStatus.error = status.error ? String(status.error) : null;
	}

	if ("port" in status) {
		rfidStatus.port = status.port ? String(status.port) : null;
	}

	if ("rssi" in status) {
		const v = status.rssi;
		rfidStatus.rssi = v != null && Number.isFinite(Number(v)) ? Number(v) : null;
	}

	if ("tags" in status && Array.isArray(status.tags)) {
		rfidStatus.tags = status.tags.map((t) => ({
			tag: t && typeof t.tag === "string" ? t.tag.trim().toUpperCase() : "",
			rssi: t && (t.rssi != null && Number.isFinite(Number(t.rssi))) ? Number(t.rssi) : null,
		})).filter((t) => t.tag);
	}

	renderRfidState();
}

function updateConnectionLabel(status) {
	if (!connectionLabel) {
		return;
	}

	if (status?.connected && status?.ssid) {
		const ssid = status.ssid || "Wi-Fi";
		connectionLabel.textContent = ssid.length > 15 ? ssid.substring(0, 12) + "..." : ssid;
		connectionLabel.classList.remove("top-bar__connection--lost", "hidden");
		return;
	}

	if (status?.hasNetwork) {
		const byCable = status?.type === "ethernet";
		if (byCable) {
			connectionLabel.textContent = status?.interfaceName ? status.interfaceName : "Cable";
		} else {
			const interfaceLabel = status?.interfaceName ? status.interfaceName : "Wi-Fi";
			connectionLabel.textContent = interfaceLabel;
		}
		connectionLabel.classList.remove("top-bar__connection--lost", "hidden");
		return;
	}

	connectionLabel.textContent = "Sin conexion";
	connectionLabel.classList.add("top-bar__connection--lost");
	connectionLabel.classList.remove("hidden");
}

function renderWeightDisplay(weight) {
	if (!weightDisplay) {
		return;
	}

	// Handle non-numeric values like "--"
	if (weight === "--" || weight === null || weight === undefined) {
		weightDisplay.textContent = "--";
		weightDisplay.classList.remove("measure-weight--with-decimals");
		return;
	}

	// Always format with 1 decimal place
	const numericWeight = Math.max(0, Number(weight));
	const weightStr = numericWeight.toFixed(1);
	const [integerPart, decimalPart] = weightStr.split(".");

	weightDisplay.innerHTML = `<span class="measure-weight-integer">${integerPart}</span><span class="measure-weight-decimal">.${decimalPart}</span>`;
	weightDisplay.classList.add("measure-weight--with-decimals");
}

function renderMeasureView() {
	renderRfidState();
	renderScaleState();
	setFeedback(measureFeedbackEl, "", false);
	measureDisplay?.classList.remove("measure-display--compact");
	measureDetails?.classList.add("hidden");
	setScreen("measure");
}

function needsPassword(network) {
	const raw = (network?.security || "").toString().toLowerCase();
	if (!raw || raw.includes("open") || raw.includes("none") || raw.includes("sin")) {
		return false;
	}
	return true;
}

function getSignalLabel(network) {
	const quality = Number.isFinite(network?.quality) ? Number(network.quality) : Number(network?.signal_level);
	if (!Number.isFinite(quality)) {
		return "";
	}
	const normalized = quality > 0 && quality <= 100 ? quality : Math.min(Math.max(quality + 100, 0), 100);
	return `${Math.round(normalized)}%`;
}

function renderNetworksLoading(message = "Buscando redes...") {
	networksMessageEl.textContent = message;
	networksListEl.innerHTML = "";
	setFeedback(networksFeedbackEl, "", false);
	setScreen("networks");
}

function renderNetworks(networks) {
	availableNetworks = Array.isArray(networks) ? networks : [];
	networksListEl.innerHTML = "";

	if (!availableNetworks.length) {
		networksMessageEl.textContent = "Sin redes disponibles";
		return;
	}

	networksMessageEl.textContent = "Toca una red";

	availableNetworks.forEach((network, index) => {
		if (!network?.ssid) {
			return;
		}
		const button = document.createElement("button");
		button.type = "button";
		button.className = "network-btn";

		const ssidLabel = document.createElement("strong");
		ssidLabel.textContent = network.ssid;

		const signal = document.createElement("span");
		signal.textContent = getSignalLabel(network);

		button.append(ssidLabel, signal);
		button.addEventListener("click", () => handleNetworkChoice(index, button));
		networksListEl.append(button);
	});
}

function handleNetworkChoice(index, button) {
	const network = availableNetworks[index];
	if (!network?.ssid) {
		return;
	}
	selectedNetwork = network;

	if (needsPassword(network)) {
		showPasswordScreen(network);
		return;
	}

	connectToNetwork(
		{ ssid: network.ssid },
		{
			feedbackEl: networksFeedbackEl,
			buttonToDisable: button,
		}
	);
}

function showPasswordScreen(network) {
	passwordTitleEl.textContent = network?.ssid || "Introduce la contrasena";
	passwordField.value = "";
	setFeedback(passwordFeedbackEl, "", false);
	shiftEnabled = false;
	updateShiftVisual();
	buildKeyboard();
	setScreen("password");

	setTimeout(() => {
		passwordField.focus();
	}, 120);
}

function renderKeyboard() {
	if (!keyboardContainer) {
		return;
	}

	const rows = KEYBOARD_LAYOUTS[currentKeyboardLayout];
	if (!rows) {
		return;
	}

	keyboardContainer.innerHTML = "";

	rows.forEach((rowKeys) => {
		const row = document.createElement("div");
		row.className = "keyboard__row";

		rowKeys.forEach((key) => {
			const button = document.createElement("button");
			button.type = "button";
			button.classList.add("key-btn");

			const config = ACTION_KEY_CONFIG[key];
			if (config) {
				button.dataset.action = key;
				button.textContent = config.label;
				if (config.className) {
					button.classList.add(config.className);
				}
			} else {
				button.dataset.value = key;
				if (/^\d$/.test(key) && currentKeyboardLayout === "letters") {
					button.dataset.digit = key;
				}
				const label = /^[a-z\u00F1]$/i.test(key) ? key.toUpperCase() : key;
				button.textContent = label;
			}

			button.addEventListener("click", () => {
				handleKeyPress(button.dataset.action, button.dataset.value);
			});

			row.append(button);
		});

		keyboardContainer.append(row);
	});

	keyboardBuilt = true;
	updateShiftVisual();
}

function buildKeyboard() {
	if (keyboardBuilt && currentKeyboardLayout === "letters") {
		return;
	}
	currentKeyboardLayout = "letters";
	renderKeyboard();
}

function showScaleWeightKeypad() {
	scaleKnownWeightKeypad?.classList.remove("hidden");
}

function hideScaleWeightKeypad() {
	scaleKnownWeightKeypad?.classList.add("hidden");
}

function insertTextAtCursor(input, text) {
	if (!input) {
		return;
	}

	const start = input.selectionStart ?? input.value.length;
	const end = input.selectionEnd ?? input.value.length;
	input.value = `${input.value.slice(0, start)}${text}${input.value.slice(end)}`;
	const nextPos = start + text.length;
	input.setSelectionRange(nextPos, nextPos);
	input.focus();
	input.dispatchEvent(new Event("input", { bubbles: true }));
}

function backspaceAtCursor(input) {
	if (!input) {
		return;
	}

	const start = input.selectionStart ?? input.value.length;
	const end = input.selectionEnd ?? input.value.length;

	if (start !== end) {
		input.value = `${input.value.slice(0, start)}${input.value.slice(end)}`;
		input.setSelectionRange(start, start);
	} else if (start > 0) {
		input.value = `${input.value.slice(0, start - 1)}${input.value.slice(start)}`;
		input.setSelectionRange(start - 1, start - 1);
	}

	input.focus();
	input.dispatchEvent(new Event("input", { bubbles: true }));
}

function handleScaleWeightKeypadPress(key) {
	if (!scaleKnownWeightInput) {
		return;
	}

	if (key === "close") {
		hideScaleWeightKeypad();
		return;
	}

	if (key === "clear") {
		scaleKnownWeightInput.value = "";
		scaleKnownWeightInput.focus();
		scaleKnownWeightInput.dispatchEvent(new Event("input", { bubbles: true }));
		return;
	}

	if (key === "backspace") {
		backspaceAtCursor(scaleKnownWeightInput);
		return;
	}

	insertTextAtCursor(scaleKnownWeightInput, key);
}

function buildScaleWeightKeypad() {
	if (!scaleKnownWeightKeypad) {
		return;
	}

	scaleKnownWeightKeypad.innerHTML = "";

	SCALE_WEIGHT_NUMPAD_LAYOUT.forEach((rowKeys) => {
		const row = document.createElement("div");
		row.className = "numpad__row";

		rowKeys.forEach((key) => {
			const button = document.createElement("button");
			button.type = "button";
			button.className = "numpad__btn";
			button.textContent = key === "backspace" ? "Borrar" : key === "clear" ? "Limpiar" : key === "close" ? "OK" : key;

			if (key === "close") {
				button.classList.add("numpad__btn--accent");
			}

			if (key === "clear") {
				button.classList.add("numpad__btn--danger");
			}

			button.addEventListener("click", () => {
				handleScaleWeightKeypadPress(key);
			});

			row.append(button);
		});

		scaleKnownWeightKeypad.append(row);
	});
}

async function initializeRfid() {
	if (!window.rfidApi) {
		console.warn("rfidApi no esta disponible en el contexto de la ventana.");
		applyRfidStatus({ ready: false, error: "RFID no disponible" });
		return;
	}

	try {
		const status = await window.rfidApi.getStatus();
		applyRfidStatus(status);
	} catch (error) {
		console.warn("No se pudo obtener el estado RFID", error);
		applyRfidStatus({
			ready: false,
			error: error?.message || String(error),
		});
	}

	if (typeof window.rfidApi.onUpdate === "function") {
		if (typeof rfidUnsubscribe === "function") {
			rfidUnsubscribe();
		}
		rfidUnsubscribe = window.rfidApi.onUpdate((status) => {
			applyRfidStatus(status);
		});
	}
}

function updateShiftVisual() {
	if (!keyboardContainer) {
		return;
	}
	const shiftKey = keyboardContainer.querySelector('[data-action="shift"]');
	if (shiftKey) {
		shiftKey.classList.toggle("key-btn--active", shiftEnabled);
	}
	keyboardContainer.querySelectorAll("[data-digit]").forEach((btn) => {
		const digit = btn.dataset.digit;
		btn.textContent = shiftEnabled && digit && NUMBER_SHIFT_MAP[digit] != null
			? NUMBER_SHIFT_MAP[digit]
			: digit;
	});
}

function handleKeyPress(action, value) {
	if (!passwordField) {
		return;
	}

	// Cambio de layout (estilo móvil: 123 / ABC / #+=)
	if (action === "layoutNumbers") {
		currentKeyboardLayout = "numbers";
		shiftEnabled = false;
		renderKeyboard();
		return;
	}
	if (action === "layoutLetters") {
		currentKeyboardLayout = "letters";
		shiftEnabled = false;
		renderKeyboard();
		return;
	}
	if (action === "layoutSymbols") {
		currentKeyboardLayout = "symbols";
		shiftEnabled = false;
		renderKeyboard();
		return;
	}

	if (action === "shift") {
		shiftEnabled = !shiftEnabled;
		updateShiftVisual();
		return;
	}

	if (action === "space") {
		passwordField.value += " ";
		passwordField.focus();
		return;
	}

	if (action === "backspace") {
		passwordField.value = passwordField.value.slice(0, -1);
		passwordField.focus();
		return;
	}

	if (action === "clear") {
		passwordField.value = "";
		passwordField.focus();
		return;
	}

	if (!value) {
		return;
	}

	let character;
	if (/^\d$/.test(value) && shiftEnabled && NUMBER_SHIFT_MAP[value] != null) {
		character = NUMBER_SHIFT_MAP[value];
	} else {
		character = /^[a-z\u00F1]$/i.test(value) && shiftEnabled ? value.toUpperCase() : value;
	}
	passwordField.value += character;
	passwordField.focus();

	if (shiftEnabled) {
		shiftEnabled = false;
		updateShiftVisual();
	}
}

async function loadNetworks() {
	networksRefreshBtn.disabled = true;
	setFeedback(networksFeedbackEl, "", false);

	try {
		const response = await window.wifiApi.scan();

		if (!response?.ok) {
			renderNetworks(response?.networks || []);
			if (response?.error) {
				setFeedback(networksFeedbackEl, formatError(response.error), true);
			}
			return;
		}

		renderNetworks(response.networks || []);
	} catch (error) {
		renderNetworks([]);
		setFeedback(networksFeedbackEl, formatError(error?.message || String(error)), true);
	} finally {
		networksRefreshBtn.disabled = false;
	}
}

async function connectToNetwork({ ssid, password }, options = {}) {
	if (!ssid) {
		return;
	}

	const { feedbackEl, buttonToDisable, onSuccess } = options;

	if (buttonToDisable) {
		buttonToDisable.disabled = true;
	}

	if (feedbackEl) {
		setFeedback(feedbackEl, "Conectando...", false);
	}

	try {
		const result = await window.wifiApi.connect({ ssid, password });

		if (result?.ok) {
			if (typeof onSuccess === "function") {
				onSuccess();
			}
			if (feedbackEl) {
				setFeedback(feedbackEl, "Conectado", false);
			}
			await new Promise((resolve) => setTimeout(resolve, 800));
			await refreshStatus({ skipNetworkScan: true });
			return;
		}

		const errorMessage = result?.error ? formatError(result.error) : "No se pudo conectar.";
		if (feedbackEl) {
			setFeedback(feedbackEl, errorMessage, true);
		}
	} catch (error) {
		if (feedbackEl) {
			setFeedback(feedbackEl, formatError(error?.message || String(error)), true);
		}
	} finally {
		if (buttonToDisable) {
			buttonToDisable.disabled = false;
		}
	}
}

function isNetworkManagerDown(message) {
	if (!message) {
		return false;
	}
	const normalized = message.toString().toLowerCase();
	return normalized.includes("networkmanager is not running") || normalized.includes("org.freedesktop.networkmanager");
}

function formatError(message) {
	if (!message) {
		return "Ha ocurrido un error desconocido.";
	}

	if (isNetworkManagerDown(message)) {
		return NETWORK_MANAGER_HINT;
	}

	return message;
}

async function refreshStatus(options = {}) {
	const { skipNetworkScan = false } = options;
	let status = null;
	try {
		status = await window.wifiApi.getStatus();
	} catch (error) {
		console.warn("No se pudo obtener el estado Wi-Fi", error);
	}

	updateConnectionLabel(status);

	if ((status?.connected && status?.ssid) || status?.hasNetwork) {
		renderMeasureView();
		return true;
	}

	if (!skipNetworkScan) {
		renderNetworksLoading("Buscando redes...");
		await loadNetworks();
	} else {
		renderNetworksLoading("Selecciona red");
	}
	return false;
}

async function sendMeasurement() {
	if (!rfidStatus.tag) {
		setFeedback(measureFeedbackEl, "Acerca un contenedor para leer el RFID.", true);
		return;
	}

	if (scaleStatus.weight === null || scaleStatus.ready !== true) {
		setFeedback(measureFeedbackEl, "Esperando lectura de peso de la báscula.", true);
		return;
	}

	if (scaleStatus.weight < MIN_WEIGHT_THRESHOLD) {
		setFeedback(measureFeedbackEl, `Peso mínimo para enviar: ${MIN_WEIGHT_THRESHOLD} kg. Actual: ${scaleStatus.weight.toFixed(1)} kg.`, true);
		return;
	}

	const currentWeight = scaleStatus.weight;

	setFeedback(measureFeedbackEl, "Enviando datos...", false);
	isSending = true;

	// Format weight to 1 decimal place before sending (round to avoid floating point precision issues)
	// Use Number() to ensure clean conversion from toFixed() string
	const formattedWeight = typeof currentWeight === "number" ? Number(Math.max(0, currentWeight).toFixed(1)) : currentWeight;

	const requestBody = {
		rfid: rfidStatus.tag,
		totalWeight: formattedWeight,
	};

	console.log("[Envío] Enviando medición:", requestBody);
	console.log("[Envío] Endpoint:", API_ENDPOINT);

	try {
		const response = await fetch(API_ENDPOINT, {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
			},
			body: JSON.stringify(requestBody),
		});

		const payload = await safeJson(response);

		console.log("[Envío] Respuesta status:", response.status);
		console.log("[Envío] Respuesta payload:", payload);

		if (!response.ok) {
			const message = payload?.error || payload?.message || `Error ${response.status}: ${response.statusText}`;
			console.error("[Envío] Error del servidor:", message);
			console.error("[Envío] Payload completo:", payload);
			throw new Error(message);
		}

		console.log("[Envío] Éxito! Medición enviada correctamente");

		// Mark this RFID as sent so we don't send it again
		lastSentRfid = rfidStatus.tag;
		lastSentWeight = currentWeight;
		console.log("[Envío] RFID marcado como enviado:", lastSentRfid);

		updateMeasurementResult(payload);

		setFeedback(measureFeedbackEl, "✓ Enviado. Retire el contenedor.", "success");

		// Set green background on success
		showSuccessBackground();
	} catch (error) {
		console.error("[Envío] Error:", error);
		console.error("[Envío] Mensaje:", error?.message || String(error));
		setFeedback(measureFeedbackEl, formatError(error?.message || String(error)), true);
		try {
			await refreshStatus();
		} catch (_) {
			// ignore secondary errors
		}
	} finally {
		isSending = false;
	}
}

// Scale configuration functions
async function loadScaleConfig() {
	if (!window.scaleApi?.getConfig) {
		return;
	}

	try {
		const config = await window.scaleApi.getConfig();

		configuredScalePort = (config.port || "").trim();
		configuredRfidPort = (config.rfidPort || "").trim();
		if (scaleBaudSelect) scaleBaudSelect.value = String(config.baudRate || 9600);
		if (scaleBaudSelect) scaleBaudSelect.value = String(config.baudRate || 9600);
		if (scaleSlaveIdInput) scaleSlaveIdInput.value = config.slaveId || 1;
		if (scaleStartAddrInput) scaleStartAddrInput.value = config.startAddr ?? 0;
		if (scaleStartAddrInput) scaleStartAddrInput.value = config.startAddr ?? 0;
		if (scaleWordOrderSelect) scaleWordOrderSelect.value = config.wordOrder || "BA";
		if (scaleRegistersSelect) scaleRegistersSelect.value = config.registers || "holding";
		if (scalePollIntervalInput) scalePollIntervalInput.value = config.pollInterval || 500;
		if (scaleDivisorInput) scaleDivisorInput.value = config.divisor || 1000;
		if (scaleDivisorInput) scaleDivisorInput.value = config.divisor || 1000;
	} catch (error) {
		console.error("Error loading scale config:", error);
	}
}

function renderStablePortSelectors(ports) {
	const entries = Array.isArray(ports) ? [...ports] : [];
	if (!scalePortInput || !rfidPortInput) {
		return;
	}

	[
		{ path: "/dev/rfid", label: "/dev/rfid (RFID)" },
		{ path: "/dev/scale", label: "/dev/scale (Bascula)" },
	].forEach((fallbackPort) => {
		const fallbackPath = String(fallbackPort.path || "").trim();
		const alreadyListed = entries.some((entry) => String(entry?.path || "").trim() === fallbackPath);
		if (!alreadyListed) {
			entries.unshift(fallbackPort);
		}
	});

	const createOption = (value, text) => {
		const option = document.createElement("option");
		option.value = value;
		option.textContent = text;
		return option;
	};

	const rebuildSelect = (selectEl, selectedValue, fallbackLabel) => {
		const normalizedSelected = String(selectedValue || "").trim();
		selectEl.innerHTML = "";
		selectEl.appendChild(createOption("", "Seleccionar puerto detectado..."));
		if (normalizedSelected) {
			selectEl.appendChild(createOption(normalizedSelected, fallbackLabel || normalizedSelected));
		}
		entries.forEach((entry) => {
			const value = String(entry?.path || "").trim();
			if (!value) {
				return;
			}
			if (normalizedSelected && value === normalizedSelected) {
				return;
			}
			const label = entry?.label ? String(entry.label) : value;
			selectEl.appendChild(createOption(value, label));
		});
		selectEl.value = normalizedSelected || "";
	};

	rebuildSelect(scalePortInput, configuredScalePort, `Actual: ${configuredScalePort}`);
	rebuildSelect(rfidPortInput, configuredRfidPort, `Actual: ${configuredRfidPort}`);
}

async function refreshStablePortSelectors({ silent = false } = {}) {
	if (!window.scaleApi?.listStablePorts) {
		renderStablePortSelectors([]);
		if (!silent) {
			setFeedback(scaleConfigFeedbackEl, "Detección de puertos no disponible", true);
		}
		return;
	}

	if (scalePortScanBtn) {
		scalePortScanBtn.disabled = true;
	}
	if (!silent) {
		setFeedback(scaleConfigFeedbackEl, "Detectando puertos USB estables...", false);
	}

	try {
		const result = await window.scaleApi.listStablePorts();
		if (!result?.ok) {
			renderStablePortSelectors([]);
			setFeedback(scaleConfigFeedbackEl, result?.error || "No se pudieron detectar puertos", true);
			return;
		}

		const ports = Array.isArray(result.ports) ? result.ports : [];
		renderStablePortSelectors(ports);
		if (!ports.length) {
			setFeedback(
				scaleConfigFeedbackEl,
				"No se encontraron puertos estables. Revisa conexion USB o reglas udev.",
				true
			);
			return;
		}

		if (!silent) {
			setFeedback(scaleConfigFeedbackEl, `${ports.length} puerto(s) detectado(s).`, "success");
		}
	} catch (error) {
		renderStablePortSelectors([]);
		setFeedback(scaleConfigFeedbackEl, error?.message || "No se pudieron detectar puertos", true);
	} finally {
		if (scalePortScanBtn) {
			scalePortScanBtn.disabled = false;
		}
	}
}

function updateScaleConfigStatus() {
	if (!scaleConfigStatusEl) return;

	if (scaleStatus.ready === true) {
		scaleConfigStatusEl.textContent = `Conectada (${scaleStatus.port || "?"})`;
		scaleConfigStatusEl.className = "scale-status-value scale-status-value--ok";
	} else if (scaleStatus.ready === false) {
		scaleConfigStatusEl.textContent = scaleStatus.error || "Desconectada";
		scaleConfigStatusEl.className = "scale-status-value scale-status-value--error";
	} else {
		scaleConfigStatusEl.textContent = "Conectando...";
		scaleConfigStatusEl.className = "scale-status-value scale-status-value--pending";
	}
}

async function saveScaleConfig() {
	if (!window.scaleApi?.setConfig) {
		setFeedback(scaleConfigFeedbackEl, "API no disponible", true);
		return;
	}

	const port = (scalePortInput?.value || "").trim();
	const rfidPort = (rfidPortInput?.value || "").trim();
	// Permitir guardar solo RFID (sin báscula) o solo báscula; al menos uno debe estar seleccionado
	if (!port && !rfidPort) {
		setFeedback(scaleConfigFeedbackEl, "Selecciona al menos el puerto RFID o el de báscula", true);
		return;
	}
	if (port && rfidPort && port === rfidPort) {
		setFeedback(scaleConfigFeedbackEl, "Báscula y RFID no pueden usar el mismo puerto", true);
		return;
	}

	const config = {
		port: port,
		rfidPort: rfidPort,
		baudRate: parseInt(scaleBaudSelect?.value || "9600", 10),
		slaveId: parseInt(scaleSlaveIdInput?.value || "1", 10),
		startAddr: parseInt(scaleStartAddrInput?.value || "0", 10),
		wordOrder: scaleWordOrderSelect?.value || "BA",
		registers: scaleRegistersSelect?.value || "holding",
		pollInterval: parseInt(scalePollIntervalInput?.value || "500", 10),
		divisor: parseFloat(scaleDivisorInput?.value || "1000"),
	};

	scaleConfigSaveBtn.disabled = true;
	setFeedback(scaleConfigFeedbackEl, "Guardando configuración...", false);

	try {
		const result = await window.scaleApi.setConfig(config);

		if (result?.ok) {
			configuredScalePort = port;
			configuredRfidPort = rfidPort;
			setFeedback(scaleConfigFeedbackEl, "Configuración guardada. Reconectando...", false);
			// Wait a bit for the scale to reconnect
			await new Promise((resolve) => setTimeout(resolve, 2000));
			setFeedback(scaleConfigFeedbackEl, "Configuración aplicada", false);
		} else {
			setFeedback(scaleConfigFeedbackEl, result?.error || "Error al guardar", true);
		}
	} catch (error) {
		setFeedback(scaleConfigFeedbackEl, error?.message || "Error al guardar", true);
	} finally {
		scaleConfigSaveBtn.disabled = false;
	}
}

async function showScaleConfigScreen() {
	setFeedback(scaleConfigFeedbackEl, "", false);
	updateScaleConfigStatus();
	hideScaleWeightKeypad();
	setScreen("scaleConfig");
	await loadScaleConfig();
	await refreshStablePortSelectors({ silent: true });
}

function formatRawAsKg(rawValue) {
	if (!Number.isFinite(rawValue)) {
		return "N/D";
	}
	return `${(rawValue / 100).toFixed(2)} kg`;
}

function setupLogLine(entry) {
	const timestamp = Number.isFinite(entry?.timestamp) ? new Date(entry.timestamp) : new Date();
	const timeText = timestamp.toLocaleTimeString();
	const message = entry?.message ? String(entry.message) : "(sin mensaje)";
	const meta = entry?.meta && typeof entry.meta === "object" ? ` ${JSON.stringify(entry.meta)}` : "";
	return `[Setup ${timeText}] ${message}${meta}`;
}

function appendSetupLog(entry) {
	const line = setupLogLine(entry);
	setupLogBuffer.push(line);
	if (setupLogBuffer.length > 300) {
		setupLogBuffer.shift();
	}
	console.log(line);
}

function ensureStepConfirmed(message) {
	const confirmed = confirm(message);
	if (!confirmed) {
		throw new Error("Setup cancelado por el usuario.");
	}
}

async function runScaleAutoSetupWG18(knownWeightKg) {
	if (!window.scaleApi?.calibrate || !window.scaleApi?.calibrateKnownWeight || !window.scaleApi?.clearTare) {
		throw new Error("Funciones de calibración WG18 no disponibles");
	}
	setupLogBuffer.length = 0;
	appendSetupLog({ message: `Inicio calibración WG18 con ${knownWeightKg.toFixed(1)} kg` });

	ensureStepConfirmed("Paso 1/3: Báscula vacía.\nPulsa Aceptar para resetear tara.");
	setFeedback(scaleConfigFeedbackEl, "Reseteando tara...", false);
	const clearResult = await window.scaleApi.clearTare();
	if (!clearResult?.ok) {
		throw new Error(clearResult?.error || "No se pudo resetear tara");
	}

	ensureStepConfirmed("Paso 2/3: Báscula vacía.\nPulsa Aceptar para hacer TARE (cero).");
	setFeedback(scaleConfigFeedbackEl, "Aplicando tare...", false);
	const tareResult = await window.scaleApi.calibrate();
	if (!tareResult?.ok) {
		throw new Error(tareResult?.error || "No se pudo hacer tare");
	}

	ensureStepConfirmed(
		`Paso 3/3: Coloca el peso patrón (${knownWeightKg.toFixed(1)} kg) y espera estable.\nPulsa Aceptar para calibrar.`
	);
	setFeedback(scaleConfigFeedbackEl, "Calibrando con peso patrón...", false);
	const calibrationResult = await window.scaleApi.calibrateKnownWeight(knownWeightKg);
	if (!calibrationResult?.ok) {
		throw new Error(calibrationResult?.error || "No se pudo calibrar con peso");
	}

	const sentValue = Number.isFinite(calibrationResult.calibrationValue)
		? calibrationResult.calibrationValue
		: "N/D";
	setFeedback(scaleConfigFeedbackEl, "Calibración WG18 completada.", "success");
	alert(
		[
			"Calibración completada.",
			`Peso patrón: ${knownWeightKg.toFixed(1)} kg`,
			`Valor enviado al registro 0xF0: ${sentValue}`,
		].join("\n")
	);
	appendSetupLog({ message: "Calibración WG18 completada OK", meta: { calibrationValue: sentValue } });
}

async function runScaleAutoSetupRS4(knownWeightKg) {
	if (!window.scaleApi?.setupInit) {
		throw new Error("Función de setup no disponible");
	}
	setupLogBuffer.length = 0;
	appendSetupLog({ message: `Inicio calibración WG18 con ${knownWeightKg.toFixed(1)} kg` });

	ensureStepConfirmed("Paso 1/3: Báscula vacía.\nPulsa Aceptar para resetear tara.");
	setFeedback(scaleConfigFeedbackEl, "Reseteando tara...", false);
	const clearResult = await window.scaleApi.clearTare();
	if (!clearResult?.ok) {
		throw new Error(clearResult?.error || "No se pudo resetear tara");
	}

	ensureStepConfirmed("Paso 2/3: Báscula vacía.\nPulsa Aceptar para hacer TARE (cero).");
	setFeedback(scaleConfigFeedbackEl, "Aplicando tare...", false);
	const tareResult = await window.scaleApi.calibrate();
	if (!tareResult?.ok) {
		throw new Error(tareResult?.error || "No se pudo hacer tare");
	}

	ensureStepConfirmed(`Paso 3/3: Coloca el peso patrón (${knownWeightKg.toFixed(1)} kg) y espera estable.\nPulsa Aceptar para calibrar.`);
	setFeedback(scaleConfigFeedbackEl, "Calibrando con peso patrón...", false);
	const calibrationResult = await window.scaleApi.calibrateKnownWeight(knownWeightKg);
	if (!calibrationResult?.ok) {
		throw new Error(calibrationResult?.error || "No se pudo calibrar con peso");
	}

	const warnings = [];
	if (Math.abs(finalEmptySample.averageRaw) > 20) {
		warnings.push(`Aviso: vacío final alejado de 0 (${formatRawAsKg(finalEmptySample.averageRaw)}).`);
	}
	if (
		Number.isFinite(zeroSample.dispersionRaw) &&
		Number.isFinite(loadSample.dispersionRaw) &&
		(zeroSample.dispersionRaw > 80 || loadSample.dispersionRaw > 80)
	) {
		warnings.push("Aviso: señal inestable (>0.8 kg de dispersión).");
	}

	const lines = [
		`OK. Ganancia: ${gainResult.currentGain} -> ${gainResult.nextGain}`,
		`Verificación: ${finalReadingKg.toFixed(2)} kg (error ${errorKg >= 0 ? "+" : ""}${errorKg.toFixed(
			2
		)} kg, ${errorPct >= 0 ? "+" : ""}${errorPct.toFixed(2)}%)`,
		`Estado final: proceso=${formatRawAsKg(finalState.processRaw)} | tara=${formatRawAsKg(
			finalState.tareRaw
		)} | peso muerto=${formatRawAsKg(finalState.deadWeightRaw)}`,
		`Cmds: 1200=${finalState.cmdTare ?? "N/D"} | 1300=${finalState.cmdDeadWeight ?? "N/D"}`,
	];

	if (warnings.length) {
		lines.push(...warnings);
	}

	// Keep message concise in UI and provide full detail in alert.
	setFeedback(scaleConfigFeedbackEl, "Setup completado correctamente.", "success");
	alert(lines.join("\n"));
	appendSetupLog({ message: "Setup completado OK" });
	console.log("[Setup RS4]", {
		knownWeightKg,
		zeroSample,
		loadSample,
		gainResult,
		deadWeightResult,
		finalEmptySample,
		finalLoadSample,
		finalState,
		warnings,
	});
}

function attachEvents() {
	openWifiBtn?.addEventListener("click", () => {
		selectedNetwork = null;
		renderNetworksLoading("Buscando redes...");
		loadNetworks();
	});

	// Scale config events
	openScaleConfigBtn?.addEventListener("click", () => {
		showScaleConfigScreen();
	});

	scaleConfigBackBtn?.addEventListener("click", () => {
		hideScaleWeightKeypad();
		setFeedback(scaleConfigFeedbackEl, "", false);
		renderMeasureView();
	});

	scaleConfigSaveBtn?.addEventListener("click", () => {
		saveScaleConfig();
	});

	scalePortScanBtn?.addEventListener("click", () => {
		refreshStablePortSelectors();
	});

	scaleOpenLogsBtn?.addEventListener("click", async () => {
		if (!window.scaleApi?.openSetupLog) {
			setFeedback(scaleConfigFeedbackEl, "Abrir logs no disponible", true);
			return;
		}
		try {
			const result = await window.scaleApi.openSetupLog();
			if (result?.ok) {
				setFeedback(scaleConfigFeedbackEl, `Log abierto: ${result.path}`, "success");
			} else {
				setFeedback(scaleConfigFeedbackEl, result?.error || "No se pudo abrir el log", true);
			}
		} catch (error) {
			setFeedback(scaleConfigFeedbackEl, error?.message || "No se pudo abrir el log", true);
		}
	});

	scaleAppRestartBtn?.addEventListener("click", async () => {
		if (confirm("¿Estás seguro de que deseas reiniciar la aplicación para buscar actualizaciones?")) {
			scaleAppRestartBtn.disabled = true;
			if (window.appApi?.restartWithUpdate) {
				await window.appApi.restartWithUpdate();
			}
		}
	});

	scaleAutoSetupBtn?.addEventListener("click", async () => {
		const knownWeight = parseWeightInput(scaleKnownWeightInput?.value);
		if (!Number.isFinite(knownWeight) || knownWeight <= 0) {
			setFeedback(scaleConfigFeedbackEl, "Introduce un peso patrón válido (kg)", true);
			return;
		}

		const confirmed = confirm(`Se ejecutará calibración WG18 con ${knownWeight.toFixed(1)} kg.\n¿Continuar?`);
		if (!confirmed) {
			return;
		}

		scaleAutoSetupBtn.disabled = true;
		setFeedback(scaleConfigFeedbackEl, "Iniciando calibración...", false);

		try {
			await runScaleAutoSetupWG18(knownWeight);
		} catch (error) {
			appendSetupLog({
				message: "Error en setup",
				meta: { error: error?.message || String(error) },
			});
			const recentLogs = setupLogBuffer.slice(-12);
			setFeedback(scaleConfigFeedbackEl, error?.message || "Error en calibración", true);
			if (recentLogs.length) {
				alert(
					[`Error: ${error?.message || "Error en calibración"}`, "", "Últimos logs:", ...recentLogs].join(
						"\n"
					)
				);
			}
		} finally {
			scaleAutoSetupBtn.disabled = false;
		}
	});

	scaleAutoSetupRs4Btn?.addEventListener("click", async () => {
		const knownWeight = parseWeightInput(scaleKnownWeightInput?.value);
		if (!Number.isFinite(knownWeight) || knownWeight <= 0) {
			setFeedback(scaleConfigFeedbackEl, "Introduce un peso patrón válido (kg)", true);
			return;
		}

		const confirmed = confirm(`Se ejecutará calibración RS4 con ${knownWeight.toFixed(1)} kg.\n¿Continuar?`);
		if (!confirmed) {
			return;
		}

		scaleAutoSetupRs4Btn.disabled = true;
		setFeedback(scaleConfigFeedbackEl, "Iniciando setup RS4...", false);

		try {
			await runScaleAutoSetupRS4(knownWeight);
		} catch (error) {
			appendSetupLog({
				message: "Error en setup RS4",
				meta: { error: error?.message || String(error) },
			});
			const recentLogs = setupLogBuffer.slice(-12);
			setFeedback(scaleConfigFeedbackEl, error?.message || "Error en setup RS4", true);
			if (recentLogs.length) {
				alert(
					[`Error: ${error?.message || "Error en setup RS4"}`, "", "Últimos logs:", ...recentLogs].join(
						"\n"
					)
				);
			}
		} finally {
			scaleAutoSetupRs4Btn.disabled = false;
		}
	});

	scaleKnownWeightInput?.addEventListener("focus", () => {
		showScaleWeightKeypad();
	});

	scaleKnownWeightInput?.addEventListener("click", () => {
		showScaleWeightKeypad();
	});

	document.addEventListener("pointerdown", (event) => {
		const target = event.target;
		if (!(target instanceof Node)) {
			return;
		}
		if (scaleKnownWeightInput?.contains(target) || scaleKnownWeightKeypad?.contains(target)) {
			return;
		}
		hideScaleWeightKeypad();
	});

	networksRefreshBtn?.addEventListener("click", () => {
		renderNetworksLoading("Actualizando redes...");
		loadNetworks();
	});

	networksBackBtn?.addEventListener("click", () => {
		setFeedback(networksFeedbackEl, "", false);
		renderMeasureView();
	});

	passwordConnectBtn?.addEventListener("click", () => {
		if (!selectedNetwork?.ssid) {
			return;
		}
		connectToNetwork(
			{ ssid: selectedNetwork.ssid, password: passwordField.value },
			{
				feedbackEl: passwordFeedbackEl,
				buttonToDisable: passwordConnectBtn,
				onSuccess: () => {
					passwordField.value = "";
				},
			}
		);
	});

	passwordBackBtn?.addEventListener("click", () => {
		selectedNetwork = null;
		passwordField.value = "";
		setFeedback(passwordFeedbackEl, "", false);
		shiftEnabled = false;
		updateShiftVisual();
		renderNetworksLoading("Selecciona red");
		renderNetworks(availableNetworks);
	});

	passwordField?.addEventListener("input", () => {
		setFeedback(passwordFeedbackEl, "", false);
	});

	passwordField?.addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
			event.preventDefault();
			passwordConnectBtn?.click();
		}
		if (event.key === "Escape") {
			event.preventDefault();
			passwordBackBtn?.click();
		}
	});
}

function showVersion() {
	try {
		const version = window.appApi?.getVersion?.();
		if (versionEl && version) {
			versionEl.textContent = `v${version}`;
		}
	} catch (error) {
		console.warn("No se pudo obtener la version de la app", error);
	}
}

function assertApiAvailable() {
	if (!window.wifiApi) {
		throw new Error("wifiApi no esta disponible en el contexto de la ventana.");
	}
	if (!window.appApi) {
		throw new Error("appApi no esta disponible en el contexto de la ventana.");
	}
}

async function init() {
	try {
		assertApiAvailable();
		showVersion();
		buildKeyboard();
		buildScaleWeightKeypad();
		attachEvents();
		renderRfidState();
		renderScaleState();
		await initializeRfid();
		await initializeScale();
		if (typeof window.scaleApi?.onSetupLog === "function") {
			if (typeof scaleSetupLogUnsubscribe === "function") {
				scaleSetupLogUnsubscribe();
			}
			scaleSetupLogUnsubscribe = window.scaleApi.onSetupLog((entry) => {
				appendSetupLog(entry);
				if (scaleConfigFeedbackEl && entry?.message) {
					setFeedback(scaleConfigFeedbackEl, String(entry.message), false);
				}
			});
		}
		await refreshStatus();
	} catch (error) {
		console.error(error);
		setFeedback(networksFeedbackEl, formatError(error?.message || String(error)), true);
		setScreen("networks");
	}
}

document.addEventListener("DOMContentLoaded", init);

window.addEventListener("beforeunload", () => {
	if (typeof rfidUnsubscribe === "function") {
		rfidUnsubscribe();
		rfidUnsubscribe = null;
	}
	if (typeof scaleUnsubscribe === "function") {
		scaleUnsubscribe();
		scaleUnsubscribe = null;
	}
	if (typeof scaleSetupLogUnsubscribe === "function") {
		scaleSetupLogUnsubscribe();
		scaleSetupLogUnsubscribe = null;
	}
});

function updateMeasurementResult(payload) {
	if (!payload || typeof payload !== "object") {
		resetMeasurementResult();
		return;
	}

	const measurementDate = payload.measurementDate || payload.createdAt;
	const containerNumber = payload.container?.number ?? "N/D";
	const zoneName = payload.zone?.name ?? "N/D";
	const contentWeight = typeof payload.contentWeight === "number" ? payload.contentWeight.toFixed(1) : payload.contentWeight || "N/D";

	if (resultDateEl) {
		if (measurementDate) {
			const date = new Date(measurementDate);
			const formatted = date.toLocaleTimeString(undefined, {
				hour: "2-digit",
				minute: "2-digit",
				second: "2-digit",
				hour12: false,
			});
			resultDateEl.textContent = formatted;
		} else {
			resultDateEl.textContent = "N/D";
		}
	}

	if (resultContainerEl) {
		resultContainerEl.textContent = containerNumber;
	}

	if (resultZoneEl) {
		resultZoneEl.textContent = zoneName;
	}

	if (resultWeightEl) {
		resultWeightEl.textContent = `${contentWeight} kg`;
	}

	measureDisplay?.classList.add("measure-display--compact");
	measureDetails?.classList.remove("hidden");
}

async function safeJson(response) {
	try {
		return await response.json();
	} catch (_) {
		return null;
	}
}

function resetMeasurementResult() {
	measureDisplay?.classList.remove("measure-display--compact");
	measureDetails?.classList.add("hidden");
	if (resultDateEl) {
		resultDateEl.textContent = "--";
	}
	if (resultContainerEl) {
		resultContainerEl.textContent = "--";
	}
	if (resultZoneEl) {
		resultZoneEl.textContent = "--";
	}
	if (resultWeightEl) {
		resultWeightEl.textContent = "--";
	}
}
