const CURRENT_YEAR = new Date().getFullYear();
const ORDER_STORAGE_KEY = "ordini.module.in-progress.v1";
const ORDER_LOT_SELECTION_STORAGE_KEY = "ordini.module.lot-selection.v1";
const ORDER_DRAFT_METADATA_STORAGE_KEY = "ordini.module.draft-metadata.v1";
const ORDER_HISTORY_EDIT_STORAGE_KEY = "ordini.module.history-edit.v1";
const PORTAL_SHARED_SESSION_STORAGE_KEY = "powerup.portal.session-token";
const PORTAL_SESSION_STORAGE_KEY = "ordini.module.session-token";
const UI_THEME_STORAGE_KEY = "powerup.ui.theme";
const DEFAULT_STAFF = "club";
const MODULE_BASE_URL = new URL("./", window.location.href);
const EURO_FORMATTER = new Intl.NumberFormat("it-IT", { style: "currency", currency: "EUR" });
const COMMON_LOT_OPTIONS = ["bt", "ct", "pz"];
const MULTIPACK_LOT_CODES = new Set(["ct", "cartone", "cartoni", "cassa", "casse"]);
const SINGLE_UNIT_LOT_CODES = new Set(["bt", "bottiglia", "bottiglie", "pz", "pezzo", "pezzi"]);
const SUPPLIER_CATALOG_FIELD_CONFIG = [
  { key: "source_name", label: "Prodotto", required: true, aliases: ["prodotto", "product_name", "nome", "descrizione", "articolo", "item", "prodotto_descrizione"] },
  { key: "source_lot_code", label: "Lotto", required: false, aliases: ["lotto", "lot_code", "formato", "confezione", "pack", "um"] },
  { key: "source_supplier_name", label: "Fornitore", required: false, aliases: ["fornitore", "supplier_name", "supplier", "ragione_sociale"] },
  { key: "product_code", label: "Codice prodotto", required: false, aliases: ["codice_prodotto", "product_code", "codice", "sku", "ean", "barcode", "codice_articolo"] },
  { key: "final_price_vat", label: "Prezzo finale ivato", required: false, aliases: ["prezzo_finale_ivato", "final_price_vat", "prezzo", "prezzo_ivato", "prezzo_vendita"] },
  { key: "vat_rate", label: "IVA %", required: false, aliases: ["iva", "vat_rate", "aliquota_iva"] },
  { key: "weight_kg", label: "Peso / UM (kg)", required: false, aliases: ["um", "weight_kg", "peso_kg", "peso", "peso_netto"] },
  { key: "unit_price_per_kg", label: "Prezzo al kg", required: false, aliases: ["prezzo_un_kg", "unit_price_per_kg", "prz_un_kg", "prezzo_kg"] },
  { key: "category", label: "Categoria", required: false, aliases: ["categoria", "category", "reparto"] },
  { key: "notes", label: "Note", required: false, aliases: ["note", "notes", "descrizione_aggiuntiva"] },
  { key: "units_per_pack", label: "Unità per pack", required: false, aliases: ["units_per_pack", "unit_per_pack", "pezzi_per_cartone", "bottiglie_per_cartone"] },
  { key: "liters_per_unit", label: "Litri per unità", required: false, aliases: ["liters_per_unit", "litri_per_unita", "lt_unita"] }
];

let authToken = "";
let currentStaff = DEFAULT_STAFF;
let products = [];
let catalogEntries = [];
let historyBatches = [];
let currentQuantities = loadStoredQuantities();
let selectedLotsByEntry = loadStoredJson(ORDER_LOT_SELECTION_STORAGE_KEY);
let draftMetadataByEntry = loadStoredJson(ORDER_DRAFT_METADATA_STORAGE_KEY);
let editingHistoryBatch = loadStoredHistoryEdit();
let availableYears = [CURRENT_YEAR];
let selectedYear = CURRENT_YEAR;
let goalYears = [CURRENT_YEAR];
let selectedGoalYear = CURRENT_YEAR;
let goalsCache = [];
let editingGoalId = null;
let sharedNotes = [];
let summaryReturnModalId = null;
let loginPending = false;
let sessionRecoveryPromise = null;
let expandedMobileProductKey = null;
let selectedEditableProductId = null;
let supplierCatalogState = createEmptySupplierCatalogState();

function isMobileCatalogLayout() {
  return window.matchMedia("(max-width: 900px)").matches;
}

function isMobileNavLayout() {
  return window.matchMedia("(max-width: 720px)").matches;
}

const loginPanel = document.getElementById("loginPanel");
const appPanel = document.getElementById("appPanel");
const identifierInput = document.getElementById("identifierInput");
const passwordInput = document.getElementById("passwordInput");
const loginButton = document.getElementById("loginButton");
const loginError = document.getElementById("loginError");
const topbarTitle = document.getElementById("topbarTitle");
const topbarSubtitle = document.getElementById("topbarSubtitle");
const tenantNameLabel = document.getElementById("tenantNameLabel");
const tenantMetaLabel = document.getElementById("tenantMetaLabel");
const themeToggleButton = document.getElementById("themeToggleButton");
const mobileNavToggle = document.getElementById("mobileNavToggle");
const mobileNavNotesBadge = document.getElementById("mobileNavNotesBadge");
const mobileNavBackdrop = document.getElementById("mobileNavBackdrop");
const mobileNavClose = document.getElementById("mobileNavClose");
const moduleNav = document.getElementById("moduleNav");
const searchInput = document.getElementById("searchInput");
const productsTableBody = document.getElementById("productsTableBody");
const csvFileInput = document.getElementById("csvFileInput");
const supplierCatalogFileInput = document.getElementById("supplierCatalogFileInput");
const activeYearLabel = document.getElementById("activeYearLabel");
const activeYearHeadline = document.getElementById("activeYearHeadline");
const productCountLabel = document.getElementById("productCountLabel");
const selectionCountLabel = document.getElementById("selectionCountLabel");
const orderEstimateTotal = document.getElementById("orderEstimateTotal");
const orderEstimateMeta = document.getElementById("orderEstimateMeta");
const historyEditBanner = document.getElementById("historyEditBanner");
const historyEditTitle = document.getElementById("historyEditTitle");
const historyEditMeta = document.getElementById("historyEditMeta");
const sharedNotesBadge = document.getElementById("sharedNotesBadge");
const historySearchInput = document.getElementById("historySearchInput");
const flashMessage = document.getElementById("flashMessage");
const noteInput = document.getElementById("noteInput");
const supplierCatalogFileLabel = document.getElementById("supplierCatalogFileLabel");
const supplierCatalogDefaultSupplierInput = document.getElementById("supplierCatalogDefaultSupplier");
const supplierCatalogDefaultLotInput = document.getElementById("supplierCatalogDefaultLot");
const supplierCatalogCreateMissingInput = document.getElementById("supplierCatalogCreateMissing");
const supplierCatalogMappingGrid = document.getElementById("supplierCatalogMappingGrid");
const supplierCatalogSample = document.getElementById("supplierCatalogSample");
const supplierCatalogSummary = document.getElementById("supplierCatalogSummary");
const supplierCatalogPreview = document.getElementById("supplierCatalogPreview");
const editProductSearchInput = document.getElementById("editProductSearchInput");
const editProductsList = document.getElementById("editProductsList");
const editProductHeading = document.getElementById("editProductHeading");
const editProductMeta = document.getElementById("editProductMeta");

function createEmptySupplierCatalogState() {
  return {
    fileName: "",
    rawRows: [],
    headers: [],
    mapping: {},
    preview: null
  };
}

bootstrap();

function bootstrap() {
  initializeUiTheme();
  updateCounters();
  renderHistoryEditBanner();
  bindEvents();
  const sessionToken = getPortalSessionToken();
  if (sessionToken) {
    authToken = sessionToken;
    void enterModuleFromPortalSession();
  }
}

function bindEvents() {
  themeToggleButton?.addEventListener("click", toggleUiTheme);
  mobileNavToggle?.addEventListener("click", () => {
    if (moduleNav?.classList.contains("is-open")) {
      closeMobileNav();
    } else {
      openMobileNav();
    }
  });
  mobileNavClose?.addEventListener("click", closeMobileNav);
  mobileNavBackdrop?.addEventListener("click", closeMobileNav);
  loginButton.addEventListener("click", () => void handleLogin());
  identifierInput.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      void handleLogin();
    }
  });
  passwordInput.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      void handleLogin();
    }
  });

  searchInput.addEventListener("input", renderProductsTable);
  historySearchInput.addEventListener("input", renderHistory);

  document.querySelectorAll("[data-toggle-menu]").forEach((button) => {
    button.addEventListener("click", (event) => {
      event.stopPropagation();
      toggleMenu(button.dataset.toggleMenu);
    });
  });

  document.addEventListener("click", (event) => {
    if (!event.target.closest(".action-group")) {
      closeMenus();
    }
  });
  moduleNav?.addEventListener("click", (event) => {
    const target = event.target;
    if (!(target instanceof Element)) {
      return;
    }
    if (target.closest("[data-toggle-menu]")) {
      return;
    }
    if (target.closest(".submenu button, .nav-link")) {
      closeMobileNav();
    }
  });
  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape") {
      closeMobileNav();
    }
  });
  window.addEventListener("resize", () => {
    if (!isMobileNavLayout()) {
      closeMobileNav();
    }
  });

  document.getElementById("importProductsButton").addEventListener("click", () => csvFileInput.click());
  csvFileInput.addEventListener("change", handleCsvImport);
  document.getElementById("importSupplierCatalogButton").addEventListener("click", () => supplierCatalogFileInput.click());
  supplierCatalogFileInput.addEventListener("change", handleSupplierCatalogFileImport);
  document.getElementById("supplierCatalogChangeFileButton").addEventListener("click", () => supplierCatalogFileInput.click());
  document.getElementById("previewSupplierCatalogButton").addEventListener("click", () => void previewSupplierCatalogImport());
  document.getElementById("confirmSupplierCatalogImportButton").addEventListener("click", () => void importSupplierCatalogPreview());
  document.getElementById("editProductsButton").addEventListener("click", openEditProductsModal);
  document.getElementById("addProductButton").addEventListener("click", () => showModal("addProductModal"));
  document.getElementById("saveProductButton").addEventListener("click", () => void saveNewProduct());
  document.getElementById("saveEditedProductButton").addEventListener("click", () => void saveEditedProduct());
  document.getElementById("deleteEditedProductButton").addEventListener("click", () => void deleteEditedProduct());
  editProductSearchInput.addEventListener("input", renderEditableProductsList);
  document.getElementById("newProductLot").addEventListener("change", () => {
    syncPriceForLotChange(
      document.getElementById("newProductLot"),
      document.getElementById("newProductPrice"),
      document.getElementById("newProductUnits")
    );
  });
  document.getElementById("newProductUnits").addEventListener("input", () => {
    setLotPriceSyncState(
      document.getElementById("newProductLot"),
      document.getElementById("newProductUnits"),
      document.getElementById("newProductLot").value,
      parseOptionalNumber(document.getElementById("newProductUnits").value)
    );
  });
  document.getElementById("editProductLot").addEventListener("change", () => {
    syncPriceForLotChange(
      document.getElementById("editProductLot"),
      document.getElementById("editProductPrice"),
      document.getElementById("editProductUnits")
    );
  });
  document.getElementById("editProductUnits").addEventListener("input", () => {
    setLotPriceSyncState(
      document.getElementById("editProductLot"),
      document.getElementById("editProductUnits"),
      document.getElementById("editProductLot").value,
      parseOptionalNumber(document.getElementById("editProductUnits").value)
    );
  });

  [supplierCatalogDefaultSupplierInput, supplierCatalogDefaultLotInput].forEach((input) => {
    input.addEventListener("input", () => {
      supplierCatalogState.preview = null;
      renderSupplierCatalogPreviewState();
    });
  });
  supplierCatalogCreateMissingInput.addEventListener("change", () => {
    supplierCatalogState.preview = null;
    renderSupplierCatalogPreviewState();
  });

  document.getElementById("confirmOrderButton").addEventListener("click", () => void confirmOrder());
  document.getElementById("suspendOrderButton").addEventListener("click", () => void suspendOrder());
  document.getElementById("loadSuspendedOrderButton").addEventListener("click", () => void loadSuspendedOrder());
  document.getElementById("deleteSuspendedOrderButton").addEventListener("click", () => void deleteSuspendedOrder());
  document.getElementById("clearOrderButton").addEventListener("click", clearQuantities);
  document.getElementById("supplierSummaryButton").addEventListener("click", openSupplierSummary);
  document.getElementById("cancelHistoryEditButton").addEventListener("click", cancelHistoryEdit);

  document.getElementById("compareOrdersButton").addEventListener("click", () => void openComparison());
  document.getElementById("latestOrderButton").addEventListener("click", () => void openLatestOrder());
  document.getElementById("historyArchiveButton").addEventListener("click", openHistoryArchive);
  document.getElementById("exportHistoryButton").addEventListener("click", exportHistory);
  document.getElementById("seasonGoalsButton").addEventListener("click", () => void openGoals());
  document.getElementById("sharedNotesButton").addEventListener("click", () => void openNotes());
  document.getElementById("addNoteButton").addEventListener("click", () => void createNote());

  document.querySelectorAll("[data-close-modal]").forEach((button) => {
    button.addEventListener("click", () => closeModal(button.dataset.closeModal));
  });

  document.querySelectorAll(".modal").forEach((modal) => {
    modal.addEventListener("click", (event) => {
      if (event.target === modal) {
        closeModal(modal.id);
      }
    });
  });

  document.querySelectorAll("[data-history-tab]").forEach((button) => {
    button.addEventListener("click", () => setHistoryTab(button.dataset.historyTab));
  });
}

function readStoredUiTheme() {
  try {
    return window.localStorage.getItem(UI_THEME_STORAGE_KEY) === "light" ? "light" : "dark";
  } catch {
    return "dark";
  }
}

function applyUiTheme(theme) {
  document.documentElement.setAttribute("data-theme", theme);
  document.documentElement.style.colorScheme = theme;
  if (themeToggleButton) {
    themeToggleButton.textContent = theme === "light" ? "Tema scuro" : "Tema chiaro";
  }
}

function initializeUiTheme() {
  applyUiTheme(readStoredUiTheme());
  window.addEventListener("storage", (event) => {
    if (event.key === UI_THEME_STORAGE_KEY) {
      applyUiTheme(readStoredUiTheme());
    }
  });
}

function toggleUiTheme() {
  const nextTheme = readStoredUiTheme() === "light" ? "dark" : "light";
  try {
    window.localStorage.setItem(UI_THEME_STORAGE_KEY, nextTheme);
  } catch {
    // Ignore storage failures and still apply the theme locally.
  }
  applyUiTheme(nextTheme);
}

function openMobileNav() {
  if (!isMobileNavLayout() || !moduleNav || !mobileNavBackdrop) {
    return;
  }
  moduleNav.classList.add("is-open");
  mobileNavBackdrop.hidden = false;
  document.body.classList.add("mobile-nav-open");
  if (mobileNavToggle) {
    mobileNavToggle.setAttribute("aria-expanded", "true");
  }
}

function closeMobileNav() {
  moduleNav?.classList.remove("is-open");
  if (mobileNavBackdrop) {
    mobileNavBackdrop.hidden = true;
  }
  document.body.classList.remove("mobile-nav-open");
  if (mobileNavToggle) {
    mobileNavToggle.setAttribute("aria-expanded", "false");
  }
}

function resolveModuleUrl(path) {
  if (/^[a-z]+:\/\//i.test(path)) {
    return path;
  }
  return new URL(path.replace(/^\//, ""), MODULE_BASE_URL).toString();
}

function loadStoredQuantities() {
  try {
    return JSON.parse(sessionStorage.getItem(ORDER_STORAGE_KEY) || "{}");
  } catch {
    return {};
  }
}

function loadStoredJson(storageKey) {
  try {
    return JSON.parse(sessionStorage.getItem(storageKey) || "{}");
  } catch {
    return {};
  }
}

function loadStoredHistoryEdit() {
  try {
    const payload = JSON.parse(sessionStorage.getItem(ORDER_HISTORY_EDIT_STORAGE_KEY) || "null");
    if (!payload || typeof payload !== "object") {
      return null;
    }
    const batchId = Number(payload.batchId);
    if (!Number.isFinite(batchId) || batchId <= 0) {
      return null;
    }
    return {
      batchId,
      confirmedAt: String(payload.confirmedAt || ""),
      staff: String(payload.staff || "")
    };
  } catch {
    return null;
  }
}

function persistQuantities() {
  sessionStorage.setItem(ORDER_STORAGE_KEY, JSON.stringify(currentQuantities));
  updateCounters();
}

function persistLotSelections() {
  sessionStorage.setItem(ORDER_LOT_SELECTION_STORAGE_KEY, JSON.stringify(selectedLotsByEntry));
}

function persistDraftMetadata() {
  sessionStorage.setItem(ORDER_DRAFT_METADATA_STORAGE_KEY, JSON.stringify(draftMetadataByEntry));
}

function persistHistoryEdit() {
  if (editingHistoryBatch && Number.isFinite(Number(editingHistoryBatch.batchId))) {
    sessionStorage.setItem(ORDER_HISTORY_EDIT_STORAGE_KEY, JSON.stringify(editingHistoryBatch));
    return;
  }
  sessionStorage.removeItem(ORDER_HISTORY_EDIT_STORAGE_KEY);
}

function normalizeCatalogText(value) {
  return String(value || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .trim();
}

function buildSearchTokens(value) {
  return normalizeCatalogText(value)
    .split(/\s+/)
    .filter(Boolean);
}

function matchesSearchText(haystack, searchValue) {
  const tokens = Array.isArray(searchValue) ? searchValue : buildSearchTokens(searchValue);
  if (!tokens.length) {
    return true;
  }
  const normalizedHaystack = normalizeCatalogText(haystack);
  return tokens.every((token) => normalizedHaystack.includes(token));
}

function normalizeLotCode(value) {
  return normalizeCatalogText(value).replace(/\s+/g, " ");
}

function isMultipackLotCode(lotCode) {
  return MULTIPACK_LOT_CODES.has(normalizeLotCode(lotCode));
}

function isBottleLotCode(lotCode) {
  const normalized = normalizeLotCode(lotCode);
  return normalized === "bt" || normalized.startsWith("bt ") || normalized.includes("bottiglia");
}

function isPieceLotCode(lotCode) {
  const normalized = normalizeLotCode(lotCode);
  return normalized === "pz" || normalized.startsWith("pz ") || normalized.includes("pezz");
}

function formatLotCodeLabel(lotCode) {
  const normalized = normalizeLotCode(lotCode);
  return normalized ? normalized.toUpperCase() : "ND";
}

function isSingleUnitLotCode(lotCode) {
  return isBottleLotCode(lotCode) || isPieceLotCode(lotCode);
}

function formatInputNumber(value, precision = 4) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) {
    return "";
  }
  const rounded = Number(numeric.toFixed(precision));
  return Number.isFinite(rounded) ? String(rounded) : "";
}

function inferSiblingMultipackUnits(product) {
  if (!product) {
    return null;
  }
  const sibling = products.find((candidate) => {
    if (Number(candidate.id) === Number(product.id)) {
      return false;
    }
    if (candidate.product_name !== product.product_name || candidate.supplier_name !== product.supplier_name) {
      return false;
    }
    if (!isMultipackLotCode(candidate.lot_code)) {
      return false;
    }
    const units = Number(candidate.units_per_pack);
    return Number.isFinite(units) && units > 0;
  });
  if (!sibling) {
    return null;
  }
  const units = Number(sibling.units_per_pack);
  return Number.isFinite(units) && units > 0 ? units : null;
}

function setLotPriceSyncState(lotInput, unitsInput, lotCode, unitsPerPack = null) {
  if (!lotInput) {
    return;
  }
  lotInput.dataset.previousLot = normalizeLotCode(lotCode || lotInput.value || "");
  const numericUnits = Number(unitsPerPack);
  if (Number.isFinite(numericUnits) && numericUnits > 0) {
    lotInput.dataset.lastMultipackUnits = String(numericUnits);
    if (unitsInput && isMultipackLotCode(lotCode) && !String(unitsInput.value || "").trim()) {
      unitsInput.value = formatInputNumber(numericUnits);
    }
    return;
  }
  delete lotInput.dataset.lastMultipackUnits;
}

function syncPriceForLotChange(lotInput, priceInput, unitsInput) {
  if (!lotInput || !priceInput || !unitsInput) {
    return;
  }

  const previousLot = normalizeLotCode(lotInput.dataset.previousLot || "");
  const nextLot = normalizeLotCode(lotInput.value || "");
  const currentPrice = parseOptionalNumber(priceInput.value);
  const currentUnits = parseOptionalNumber(unitsInput.value);

  if (currentUnits !== null && currentUnits > 0) {
    lotInput.dataset.lastMultipackUnits = String(currentUnits);
  }
  const storedUnits = parseOptionalNumber(lotInput.dataset.lastMultipackUnits || "");

  if (!previousLot || previousLot === nextLot) {
    lotInput.dataset.previousLot = nextLot;
    return;
  }

  if (isMultipackLotCode(previousLot) && isSingleUnitLotCode(nextLot)) {
    const packUnits = currentUnits ?? storedUnits;
    if (packUnits !== null && packUnits > 0 && currentPrice !== null) {
      priceInput.value = formatInputNumber(currentPrice / packUnits, 2);
    }
    if (packUnits !== null && packUnits > 0) {
      lotInput.dataset.lastMultipackUnits = String(packUnits);
    }
    unitsInput.value = "";
  } else if (isSingleUnitLotCode(previousLot) && isMultipackLotCode(nextLot)) {
    const packUnits = currentUnits ?? storedUnits;
    if (packUnits !== null && packUnits > 0) {
      unitsInput.value = formatInputNumber(packUnits);
      if (currentPrice !== null) {
        priceInput.value = formatInputNumber(currentPrice * packUnits, 2);
      }
    }
  }

  lotInput.dataset.previousLot = nextLot;
}

function buildCatalogEntryKey(productName, supplierName) {
  return `${normalizeCatalogText(productName)}::${normalizeCatalogText(supplierName)}`;
}

function buildDraftQuantityKey(entryKey, lotCode) {
  return `draft:${entryKey}:${normalizeLotCode(lotCode)}`;
}

function lotSortRank(lotCode) {
  if (isBottleLotCode(lotCode)) {
    return 0;
  }
  if (isMultipackLotCode(lotCode)) {
    return 1;
  }
  if (isPieceLotCode(lotCode)) {
    return 2;
  }
  return 9;
}

function buildCatalogEntries(items) {
  const grouped = new Map();
  items.forEach((product) => {
    const key = buildCatalogEntryKey(product.product_name, product.supplier_name);
    if (!grouped.has(key)) {
      grouped.set(key, {
        key,
        product_name: product.product_name,
        supplier_name: product.supplier_name,
        variants: []
      });
    }
    grouped.get(key).variants.push(product);
  });

  return [...grouped.values()]
    .map((entry) => {
      entry.variants.sort((left, right) => {
        return (
          lotSortRank(left.lot_code) - lotSortRank(right.lot_code) ||
          normalizeLotCode(left.lot_code).localeCompare(normalizeLotCode(right.lot_code)) ||
          (left.product_code || "").localeCompare(right.product_code || "") ||
          Number(left.id) - Number(right.id)
        );
      });
      const representative = entry.variants[0];
      const searchParts = new Set([
        entry.product_name,
        entry.supplier_name,
        ...entry.variants.flatMap((variant) => [
          variant.lot_code,
          variant.product_code || "",
          variant.category || "",
          variant.notes || ""
        ])
      ]);
      return {
        ...entry,
        representative,
        searchText: normalizeCatalogText([...searchParts].join(" "))
      };
    })
    .sort((left, right) => {
      return (
        left.supplier_name.localeCompare(right.supplier_name) ||
        left.product_name.localeCompare(right.product_name)
      );
    });
}

function getEntryLotOptions(entry) {
  const options = new Map();
  entry.variants.forEach((variant) => {
    const value = normalizeLotCode(variant.lot_code);
    if (value && !options.has(value)) {
      options.set(value, { value, label: formatLotCodeLabel(variant.lot_code), exists: true });
    }
  });
  COMMON_LOT_OPTIONS.forEach((lotCode) => {
    const value = normalizeLotCode(lotCode);
    if (!options.has(value)) {
      options.set(value, { value, label: formatLotCodeLabel(lotCode), exists: false });
    }
  });
  return [...options.values()];
}

function findVariantForLot(entry, lotCode) {
  const normalizedLot = normalizeLotCode(lotCode);
  return entry.variants.find((variant) => normalizeLotCode(variant.lot_code) === normalizedLot) || null;
}

function inferLotFromQuantities(entry) {
  const variantWithQuantity = entry.variants.find((variant) => {
    const quantity = Number.parseInt(String(currentQuantities[String(variant.id)] || ""), 10) || 0;
    return quantity > 0;
  });
  if (variantWithQuantity) {
    return normalizeLotCode(variantWithQuantity.lot_code);
  }

  const draftPrefix = `draft:${entry.key}:`;
  const matchingDraftKey = Object.keys(currentQuantities).find((key) => {
    if (!key.startsWith(draftPrefix)) {
      return false;
    }
    const quantity = Number.parseInt(String(currentQuantities[key] || ""), 10) || 0;
    return quantity > 0;
  });
  if (matchingDraftKey) {
    return normalizeLotCode(matchingDraftKey.slice(draftPrefix.length));
  }

  return null;
}

function getSelectedLotForEntry(entry) {
  const inferredLot = inferLotFromQuantities(entry);
  if (inferredLot) {
    return inferredLot;
  }

  const storedLot = normalizeLotCode(selectedLotsByEntry[entry.key] || "");
  if (storedLot && getEntryLotOptions(entry).some((option) => option.value === storedLot)) {
    return storedLot;
  }

  return normalizeLotCode(entry.variants[0]?.lot_code || COMMON_LOT_OPTIONS[0]);
}

function getDraftLotState(entryKey, lotCode) {
  const entryState = draftMetadataByEntry[entryKey];
  if (!entryState || typeof entryState !== "object") {
    return {};
  }
  return entryState[normalizeLotCode(lotCode)] || {};
}

function setDraftLotState(entryKey, lotCode, patch) {
  const normalizedLot = normalizeLotCode(lotCode);
  const entryState = {
    ...(draftMetadataByEntry[entryKey] && typeof draftMetadataByEntry[entryKey] === "object" ? draftMetadataByEntry[entryKey] : {})
  };
  const nextState = {
    ...(entryState[normalizedLot] && typeof entryState[normalizedLot] === "object" ? entryState[normalizedLot] : {}),
    ...patch
  };

  Object.keys(nextState).forEach((key) => {
    const value = nextState[key];
    if (value === null || value === undefined || String(value).trim() === "") {
      delete nextState[key];
    }
  });

  if (Object.keys(nextState).length) {
    entryState[normalizedLot] = nextState;
    draftMetadataByEntry[entryKey] = entryState;
  } else {
    delete entryState[normalizedLot];
    if (Object.keys(entryState).length) {
      draftMetadataByEntry[entryKey] = entryState;
    } else {
      delete draftMetadataByEntry[entryKey];
    }
  }

  persistDraftMetadata();
}

function getEntrySelection(entry, lotCode = null) {
  const selectedLot = normalizeLotCode(lotCode || getSelectedLotForEntry(entry));
  const variant = findVariantForLot(entry, selectedLot);
  const storageKey = variant ? String(variant.id) : buildDraftQuantityKey(entry.key, selectedLot);
  const quantity = Number.parseInt(String(currentQuantities[storageKey] || ""), 10) || 0;
  const draftState = getDraftLotState(entry.key, selectedLot);
  const draftUnitsPerPack = parseOptionalNumber(String(draftState.units_per_pack || ""));
  const existingUnitsPerPack = variant && Number.isFinite(Number(variant.units_per_pack)) ? Number(variant.units_per_pack) : null;
  const needsUnitsPerPack = isMultipackLotCode(selectedLot) && !Number.isFinite(existingUnitsPerPack) && !Number.isFinite(draftUnitsPerPack);
  return {
    lotCode: selectedLot,
    variant,
    storageKey,
    quantity,
    draftState,
    draftUnitsPerPack,
    existingUnitsPerPack,
    needsUnitsPerPack
  };
}

function findPricingSourceVariant(entry, preferredVariant = null) {
  const candidates = [preferredVariant, ...entry.variants].filter(Boolean);
  return (
    candidates.find((variant) => {
      const finalPrice = Number(variant.final_price_vat);
      const unitPricePerKg = Number(variant.unit_price_per_kg);
      const weightKg = Number(variant.weight_kg);
      return (
        (Number.isFinite(finalPrice) && finalPrice > 0) ||
        (Number.isFinite(unitPricePerKg) && unitPricePerKg > 0 && Number.isFinite(weightKg) && weightKg > 0)
      );
    }) ||
    preferredVariant ||
    entry.representative
  );
}

function deriveSelectedLotVariant(entry, selection) {
  const selectedLot = normalizeLotCode(selection?.lotCode || entry.representative?.lot_code || "");
  const selectedVariant = selection?.variant || findVariantForLot(entry, selectedLot);
  const pricingSource = findPricingSourceVariant(entry, selectedVariant);
  const baseVariant = selectedVariant || pricingSource || entry.representative;
  const baseLot = normalizeLotCode(pricingSource?.lot_code || baseVariant?.lot_code || selectedLot);

  const explicitDraftUnits = Number(selection?.draftUnitsPerPack);
  const explicitExistingUnits = Number(selection?.existingUnitsPerPack);
  const sourceUnits = Number(pricingSource?.units_per_pack);
  const selectedVariantUnits = Number(selectedVariant?.units_per_pack);
  const inferredMultipackUnits = inferSiblingMultipackUnits(selectedVariant || pricingSource || baseVariant);
  const packUnits =
    (Number.isFinite(explicitDraftUnits) && explicitDraftUnits > 0 ? explicitDraftUnits : null) ??
    (Number.isFinite(explicitExistingUnits) && explicitExistingUnits > 0 ? explicitExistingUnits : null) ??
    (Number.isFinite(selectedVariantUnits) && selectedVariantUnits > 0 ? selectedVariantUnits : null) ??
    (Number.isFinite(sourceUnits) && sourceUnits > 0 ? sourceUnits : null) ??
    inferredMultipackUnits;

  const finalPriceSource =
    _coerceFiniteNumber(selectedVariant?.final_price_vat) ?? _coerceFiniteNumber(pricingSource?.final_price_vat);
  const unitPricePerKgSource =
    _coerceFiniteNumber(selectedVariant?.unit_price_per_kg) ?? _coerceFiniteNumber(pricingSource?.unit_price_per_kg);
  const weightKgSource = _coerceFiniteNumber(selectedVariant?.weight_kg) ?? _coerceFiniteNumber(pricingSource?.weight_kg);
  const vatRateSource = _coerceFiniteNumber(selectedVariant?.vat_rate) ?? _coerceFiniteNumber(pricingSource?.vat_rate);

  let resolvedFinalPrice = finalPriceSource;
  if (
    resolvedFinalPrice !== null &&
    Number.isFinite(packUnits) &&
    packUnits > 0 &&
    baseLot &&
    selectedLot &&
    baseLot !== selectedLot
  ) {
    if (isMultipackLotCode(baseLot) && isSingleUnitLotCode(selectedLot)) {
      resolvedFinalPrice = Number((resolvedFinalPrice / packUnits).toFixed(2));
    } else if (isSingleUnitLotCode(baseLot) && isMultipackLotCode(selectedLot)) {
      resolvedFinalPrice = Number((resolvedFinalPrice * packUnits).toFixed(2));
    }
  }

  let resolvedUnitsPerPack = null;
  if (isMultipackLotCode(selectedLot)) {
    resolvedUnitsPerPack =
      (Number.isFinite(explicitDraftUnits) && explicitDraftUnits > 0 ? explicitDraftUnits : null) ??
      (Number.isFinite(explicitExistingUnits) && explicitExistingUnits > 0 ? explicitExistingUnits : null) ??
      (Number.isFinite(selectedVariantUnits) && selectedVariantUnits > 0 ? selectedVariantUnits : null) ??
      (Number.isFinite(sourceUnits) && sourceUnits > 0 ? sourceUnits : null) ??
      inferredMultipackUnits ??
      null;
  }

  return {
    ...(baseVariant || {}),
    lot_code: selectedLot,
    final_price_vat: resolvedFinalPrice,
    unit_price_per_kg: unitPricePerKgSource,
    weight_kg: weightKgSource,
    vat_rate: vatRateSource,
    units_per_pack: resolvedUnitsPerPack
  };
}

function setSelectedLotForEntry(entry, nextLotCode) {
  const previousSelection = getEntrySelection(entry);
  const normalizedNextLot = normalizeLotCode(nextLotCode);
  const nextVariant = findVariantForLot(entry, normalizedNextLot);
  const nextStorageKey = nextVariant ? String(nextVariant.id) : buildDraftQuantityKey(entry.key, normalizedNextLot);

  if (previousSelection.storageKey !== nextStorageKey) {
    const previousQuantity = previousSelection.quantity;
    const nextQuantity = Number.parseInt(String(currentQuantities[nextStorageKey] || ""), 10) || 0;
    if (previousQuantity > 0 && nextQuantity <= 0) {
      currentQuantities[nextStorageKey] = previousQuantity;
    }
    delete currentQuantities[previousSelection.storageKey];
    persistQuantities();
  }

  selectedLotsByEntry[entry.key] = normalizedNextLot;
  persistLotSelections();
}

function showFlash(message, timeout = 2800) {
  flashMessage.textContent = message;
  flashMessage.hidden = false;
  window.clearTimeout(showFlash.timeoutId);
  showFlash.timeoutId = window.setTimeout(() => {
    flashMessage.hidden = true;
  }, timeout);
}

function setLoginPending(isPending) {
  loginPending = isPending;
  loginButton.disabled = isPending;
  identifierInput.disabled = isPending;
  passwordInput.disabled = isPending;
}

function applyTenantBranding(payload) {
  const tenantName = payload.tenant_name || "PowerUp Ordini";
  const userName = payload.user_name || payload.staff || "admin";
  topbarTitle.textContent = `${tenantName} | Ordini`;
  topbarSubtitle.textContent = "Catalogo prodotti, storico ordini e richieste fornitori del tuo locale.";
  tenantNameLabel.textContent = tenantName;
  tenantMetaLabel.textContent = `${userName} - database locale attivo`;
}

function renderHistoryEditBanner() {
  if (!historyEditBanner || !historyEditTitle || !historyEditMeta) {
    return;
  }
  if (!editingHistoryBatch?.batchId) {
    historyEditBanner.hidden = true;
    return;
  }
  historyEditBanner.hidden = false;
  const formattedDate = editingHistoryBatch.confirmedAt ? formatDate(editingHistoryBatch.confirmedAt) : "ordine storico";
  historyEditTitle.textContent = `Stai modificando l'ordine del ${formattedDate}`;
  historyEditMeta.textContent = "Confermando aggiorni lo stesso ordine mantenendo la sua data originale.";
}

function setHistoryEditContext(context) {
  if (!context || !Number.isFinite(Number(context.batchId))) {
    editingHistoryBatch = null;
  } else {
    editingHistoryBatch = {
      batchId: Number(context.batchId),
      confirmedAt: String(context.confirmedAt || ""),
      staff: String(context.staff || "")
    };
  }
  persistHistoryEdit();
  renderHistoryEditBanner();
}

function resetOrderDraftState() {
  currentQuantities = {};
  selectedLotsByEntry = {};
  draftMetadataByEntry = {};
  persistQuantities();
  persistLotSelections();
  persistDraftMetadata();
}

function cancelHistoryEdit() {
  resetOrderDraftState();
  setHistoryEditContext(null);
  renderProductsTable();
  showFlash("Modifica storico annullata.");
}

function clearSessionQueryParam() {
  const nextUrl = new URL(window.location.href);
  nextUrl.searchParams.delete("session");
  window.history.replaceState({}, "", nextUrl);
}

function getStoredModuleSessionToken() {
  try {
    return window.sessionStorage.getItem(PORTAL_SESSION_STORAGE_KEY)?.trim() || null;
  } catch {
    return null;
  }
}

function storeModuleSessionToken(token) {
  try {
    if (token) {
      window.sessionStorage.setItem(PORTAL_SESSION_STORAGE_KEY, token);
    }
  } catch {
    // Ignore storage failures.
  }
}

function clearModuleSessionToken() {
  try {
    window.sessionStorage.removeItem(PORTAL_SESSION_STORAGE_KEY);
  } catch {
    // Ignore storage failures.
  }
}

function getPortalSessionToken() {
  const tokenFromUrl = new URL(window.location.href).searchParams.get("session")?.trim();
  if (tokenFromUrl) {
    storeModuleSessionToken(tokenFromUrl);
    return tokenFromUrl;
  }

  try {
    const sharedPortalToken = window.localStorage.getItem(PORTAL_SHARED_SESSION_STORAGE_KEY)?.trim();
    if (sharedPortalToken) {
      const storedToken = getStoredModuleSessionToken();
      if (storedToken !== sharedPortalToken) {
        storeModuleSessionToken(sharedPortalToken);
      }
      return sharedPortalToken;
    }
  } catch {
    // Ignore storage failures.
  }

  return getStoredModuleSessionToken();
}

async function recoverSession() {
  if (sessionRecoveryPromise) {
    return sessionRecoveryPromise;
  }

  sessionRecoveryPromise = (async () => {
    const sessionToken = getPortalSessionToken();
    if (!sessionToken) {
      authToken = "";
      clearModuleSessionToken();
      return false;
    }

    try {
      const response = await fetch(resolveModuleUrl("api/login"), {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ session_token: sessionToken })
      });
      const payload = await response.json();
      if (!payload?.success || !payload?.token) {
        authToken = "";
        clearModuleSessionToken();
        return false;
      }

      authToken = payload.token;
      storeModuleSessionToken(authToken);
      currentStaff = payload.staff || currentStaff || DEFAULT_STAFF;
      applyTenantBranding(payload);
      return true;
    } catch {
      authToken = "";
      clearModuleSessionToken();
      return false;
    }
  })();

  try {
    return await sessionRecoveryPromise;
  } finally {
    sessionRecoveryPromise = null;
  }
}

function revealAppPanel() {
  identifierInput.value = "";
  passwordInput.value = "";
  loginPanel.hidden = true;
  loginPanel.style.display = "none";
  appPanel.hidden = false;
  appPanel.style.display = "";
}

async function performLogin(payload, invalidMessage) {
  if (loginPending) {
    return;
  }

  loginError.hidden = true;
  setLoginPending(true);
  try {
    const response = await fetch(resolveModuleUrl("api/login"), {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });
    const payload = await response.json();
    if (!payload.success) {
      loginError.textContent = invalidMessage;
      loginError.hidden = false;
      return;
    }

    authToken = payload.token;
    storeModuleSessionToken(authToken);
    await hydrateAuthenticatedModule(payload, { clearPortalSession: false });
  } catch {
    loginError.textContent = "Impossibile contattare il backend del modulo.";
    loginError.hidden = false;
  } finally {
    if (!authToken) {
      setLoginPending(false);
    }
  }
}

async function hydrateAuthenticatedModule(payload, { clearPortalSession = true } = {}) {
  currentStaff = payload.staff || DEFAULT_STAFF;
  if (payload.token) {
    authToken = payload.token;
    storeModuleSessionToken(authToken);
  }
  applyTenantBranding(payload);
  if (clearPortalSession) {
    clearSessionQueryParam();
  }
  revealAppPanel();
  await loadProducts();
  await loadAvailableYears();
  await loadHistory(selectedYear);
  await Promise.allSettled([checkSuspendedOrder(), loadNotes()]);
  showFlash("Modulo ordini pronto.");
}

async function enterModuleFromPortalSession() {
  loginError.hidden = true;
  setLoginPending(true);
  try {
    const payload = await apiFetch("/api/session");
    await hydrateAuthenticatedModule(payload);
  } catch {
    const restored = await recoverSession();
    if (restored) {
      try {
        const payload = await apiFetch("/api/session");
        await hydrateAuthenticatedModule(payload);
        return;
      } catch {
        // Fall through to login panel.
      }
    }
    authToken = "";
    clearModuleSessionToken();
    loginError.textContent = "Sessione del gestionale non valida. Rientra dal portale.";
    loginError.hidden = false;
  } finally {
    if (!authToken) {
      setLoginPending(false);
    }
  }
}

async function handleLogin() {
  const identifier = identifierInput.value.trim();
  const password = passwordInput.value.trim();

  if (!identifier) {
    loginError.textContent = "Inserisci username o email dell'amministratore.";
    loginError.hidden = false;
    return;
  }

  if (!password) {
    loginError.textContent = "Inserisci la password amministratore.";
    loginError.hidden = false;
    return;
  }

  await performLogin({ identifier, password }, "Credenziali amministratore non valide.");
}

async function apiFetch(path, options = {}) {
  if (!authToken) {
    authToken = getPortalSessionToken() || "";
  }

  const headers = new Headers(options.headers || {});
  if (authToken) {
    headers.set("Authorization", `Bearer ${authToken}`);
  }

  let response = await fetch(resolveModuleUrl(path), {
    ...options,
    headers
  });

  if (response.status === 401) {
    const restored = await recoverSession();
    if (restored) {
      const retryHeaders = new Headers(options.headers || {});
      if (authToken) {
        retryHeaders.set("Authorization", `Bearer ${authToken}`);
      }
      response = await fetch(resolveModuleUrl(path), {
        ...options,
        headers: retryHeaders
      });
    }
  }

  if (!response.ok) {
    let detail = `Errore ${response.status}`;
    try {
      const payload = await response.json();
      detail = payload.detail || detail;
    } catch {
      // Ignore body parsing failures.
    }
    throw new Error(detail);
  }

  if (response.status === 204) {
    return null;
  }

  return response.json();
}

async function loadProducts() {
  const payload = await apiFetch("/api/prodotti");
  products = Array.isArray(payload) ? payload : [];
  products.sort((left, right) => {
    return (
      left.supplier_name.localeCompare(right.supplier_name) ||
      (left.category || "").localeCompare(right.category || "") ||
      left.product_name.localeCompare(right.product_name) ||
      left.lot_code.localeCompare(right.lot_code)
    );
  });
  catalogEntries = buildCatalogEntries(products);
  renderProductsTable();
  if (!document.getElementById("editProductsModal").hidden) {
    syncEditableProductSelection();
    renderEditableProductsList();
  }
  updateCounters();
}

async function loadAvailableYears() {
  const payload = await apiFetch("/api/ordini/years");
  availableYears = Array.isArray(payload?.years) && payload.years.length ? payload.years : [CURRENT_YEAR];
  if (!availableYears.includes(selectedYear)) {
    selectedYear = availableYears[0];
  }
  updateSelectedYearUi();
  renderHistoryYearSwitchers();
}

async function loadHistory(year) {
  selectedYear = year;
  updateSelectedYearUi();
  historyBatches = await apiFetch(`/api/ordini?year=${encodeURIComponent(year)}`);
  renderHistory();
  renderHistoryYearSwitchers();
}

function renderProductsTable() {
  const mobileCatalogLayout = isMobileCatalogLayout();
  const searchValue = searchInput.value.trim().toLowerCase();
  const filteredEntries = catalogEntries.filter((entry) => {
    if (!searchValue) {
      return true;
    }
    return entry.searchText.includes(normalizeCatalogText(searchValue));
  });

  if (!filteredEntries.length) {
    productsTableBody.innerHTML = `<tr><td colspan="4" class="empty-state">Nessun prodotto disponibile.</td></tr>`;
    return;
  }

  const filteredKeys = new Set(filteredEntries.map((entry) => entry.key));
  if (expandedMobileProductKey && !filteredKeys.has(expandedMobileProductKey)) {
    expandedMobileProductKey = null;
  }
  if (!mobileCatalogLayout) {
    expandedMobileProductKey = null;
  }

  productsTableBody.innerHTML = filteredEntries
    .map((entry) => {
      const key = entry.key;
      const selection = getEntrySelection(entry);
      const quantity = selection.quantity;
      const selectedVariant = deriveSelectedLotVariant(entry, selection);
      const lotOptions = getEntryLotOptions(entry);
      const isExpanded = mobileCatalogLayout && expandedMobileProductKey === key;
      const needsUnitsPerPack = selection.needsUnitsPerPack;
      const shouldShowDetail = isExpanded || needsUnitsPerPack;
      const unitsPerPackValue = selection.draftState.units_per_pack || "";
      return `
        <tr class="product-row ${shouldShowDetail ? "is-expanded" : ""}">
          <td class="product-primary-cell">
            <button
              type="button"
              class="product-row-toggle"
              data-expand-key="${escapeAttribute(key)}"
              aria-expanded="${shouldShowDetail ? "true" : "false"}"
            >
              <span class="product-row-copy">
                <span class="product-title">${escapeHtml(entry.product_name)}</span>
                ${renderProductMeta(selectedVariant)}
              </span>
              <span class="product-row-chevron" aria-hidden="true"></span>
            </button>
          </td>
          <td class="product-desktop-cell product-lot-cell">
            <select data-lot-entry-key="${escapeAttribute(key)}">
              ${lotOptions
                .map(
                  (option) => `
                    <option value="${escapeAttribute(option.value)}" ${option.value === selection.lotCode ? "selected" : ""}>
                      ${escapeHtml(option.label)}
                    </option>
                  `
                )
                .join("")}
            </select>
          </td>
          <td class="product-desktop-cell">${escapeHtml(entry.supplier_name)}</td>
          <td class="product-desktop-cell">
            <input
              type="number"
              min="0"
              step="1"
              inputmode="numeric"
              pattern="[0-9]*"
              value="${quantity}"
              data-quantity-key="${escapeAttribute(selection.storageKey)}"
            />
          </td>
        </tr>
        <tr class="product-detail-row ${shouldShowDetail ? "is-expanded" : ""}">
          <td colspan="4">
            <div class="product-detail-panel">
              <div class="product-detail-grid">
                <label class="product-detail-item">
                  <span class="product-detail-label">Lotto</span>
                  <select data-lot-entry-key="${escapeAttribute(key)}">
                    ${lotOptions
                      .map(
                        (option) => `
                          <option value="${escapeAttribute(option.value)}" ${option.value === selection.lotCode ? "selected" : ""}>
                            ${escapeHtml(option.label)}
                          </option>
                        `
                      )
                      .join("")}
                  </select>
                </label>
                <div class="product-detail-item">
                  <span class="product-detail-label">Fornitore</span>
                  <strong>${escapeHtml(entry.supplier_name)}</strong>
                </div>
                <label class="product-detail-quantity">
                  <span class="product-detail-label">Quantità</span>
                  <input
                    type="number"
                    min="0"
                    step="1"
                    inputmode="numeric"
                    pattern="[0-9]*"
                    value="${quantity}"
                    data-quantity-key="${escapeAttribute(selection.storageKey)}"
                  />
                </label>
              </div>
              ${
                needsUnitsPerPack
                  ? `
                    <label class="product-detail-callout">
                      <span class="product-detail-callout-title">Serve un dato per il lotto ${escapeHtml(formatLotCodeLabel(selection.lotCode))}</span>
                      <span class="product-detail-callout-copy">Quante bottiglie contiene un cartone? Serve per conteggi, litri e obiettivi.</span>
                      <input
                        type="number"
                        min="1"
                        step="1"
                        inputmode="numeric"
                        pattern="[0-9]*"
                        placeholder="Es. 6"
                        value="${escapeAttribute(unitsPerPackValue)}"
                        data-units-pack-entry-key="${escapeAttribute(key)}"
                        data-units-pack-lot="${escapeAttribute(selection.lotCode)}"
                      />
                    </label>
                  `
                  : ""
              }
            </div>
          </td>
        </tr>
      `;
    })
    .join("");

  if (mobileCatalogLayout) {
    productsTableBody.querySelectorAll("[data-expand-key]").forEach((button) => {
      button.addEventListener("click", () => {
        const key = button.dataset.expandKey;
        expandedMobileProductKey = expandedMobileProductKey === key ? null : key;
        renderProductsTable();
      });
    });
  }

  productsTableBody.querySelectorAll("[data-lot-entry-key]").forEach((select) => {
    select.addEventListener("change", () => {
      const entry = catalogEntries.find((item) => item.key === select.dataset.lotEntryKey);
      if (!entry) {
        return;
      }
      setSelectedLotForEntry(entry, select.value || "");
      renderProductsTable();
    });
  });

  productsTableBody.querySelectorAll("[data-quantity-key]").forEach((input) => {
    input.addEventListener("focus", () => {
      if (input.value === "0") {
        input.value = "";
      }
    });

    input.addEventListener("input", () => {
      const key = input.dataset.quantityKey;
      const quantity = Number.parseInt(input.value, 10) || 0;
      if (quantity > 0) {
        currentQuantities[key] = quantity;
      } else {
        delete currentQuantities[key];
      }
      syncQuantityInputs(key, quantity > 0 ? String(quantity) : "");
      persistQuantities();
    });

    input.addEventListener("blur", () => {
      const key = input.dataset.quantityKey;
      const quantity = Number.parseInt(input.value, 10) || 0;
      if (quantity > 0) {
        currentQuantities[key] = quantity;
        syncQuantityInputs(key, String(quantity));
      } else {
        delete currentQuantities[key];
        syncQuantityInputs(key, "0");
      }
      persistQuantities();
    });
  });

  productsTableBody.querySelectorAll("[data-units-pack-entry-key]").forEach((input) => {
    const persistUnitsValue = () => {
      const entryKey = input.dataset.unitsPackEntryKey;
      const lotCode = input.dataset.unitsPackLot;
      if (!entryKey || !lotCode) {
        return;
      }
      setDraftLotState(entryKey, lotCode, { units_per_pack: input.value.trim() });
    };

    input.addEventListener("input", persistUnitsValue);
    input.addEventListener("blur", persistUnitsValue);
  });
}

function syncQuantityInputs(key, value) {
  productsTableBody.querySelectorAll(`[data-quantity-key="${CSS.escape(String(key))}"]`).forEach((input) => {
    if (document.activeElement !== input) {
      input.value = value;
    }
  });
}

async function handleCsvImport(event) {
  const file = event.target.files?.[0];
  if (!file) {
    return;
  }

  try {
    const text = await file.text();
    const rows = parseCsvProducts(text);
    if (!rows.length) {
      throw new Error("Il CSV non contiene righe valide.");
    }
    const result = await apiFetch("/api/prodotti", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(rows)
    });
    await loadProducts();
    const duplicateRows = Number(result?.duplicate_rows) || 0;
    const createdRows = Number(result?.created) || 0;
    const updatedRows = Number(result?.updated) || 0;
    if (duplicateRows > 0) {
      showFlash(
        `Import CSV completato. Creati ${createdRows}, aggiornati ${updatedRows}, ignorate ${duplicateRows} righe duplicate.`
      );
      return;
    }
    showFlash(`Import CSV completato. Creati ${createdRows}, aggiornati ${updatedRows}.`);
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Import CSV non riuscito.");
  } finally {
    event.target.value = "";
  }
}

function guessSupplierCatalogMapping(headers) {
  const mapping = {};
  SUPPLIER_CATALOG_FIELD_CONFIG.forEach((field) => {
    const headerIndex = headers.findIndex((header) => field.aliases.includes(normalizeCsvHeader(header)));
    if (headerIndex >= 0) {
      mapping[field.key] = String(headerIndex);
    }
  });
  return mapping;
}

function renderSupplierCatalogMappingControls() {
  if (!supplierCatalogState.headers.length) {
    supplierCatalogMappingGrid.innerHTML = '<p class="muted-copy">Seleziona un file per configurare il mapping.</p>';
    return;
  }

  const options = ['<option value="">Nessuna colonna</option>']
    .concat(
      supplierCatalogState.headers.map(
        (header, index) => `<option value="${index}">${escapeHtml(header || `Colonna ${index + 1}`)}</option>`
      )
    )
    .join("");

  supplierCatalogMappingGrid.innerHTML = SUPPLIER_CATALOG_FIELD_CONFIG.map((field) => {
    const value = supplierCatalogState.mapping[field.key] || "";
    const requiredBadge = field.required ? '<small class="supplier-catalog-badge">obbligatorio</small>' : "";
    return `
      <label class="field">
        <span>${escapeHtml(field.label)} ${requiredBadge}</span>
        <select data-supplier-catalog-field="${escapeHtml(field.key)}">${options}</select>
      </label>
    `;
  }).join("");

  supplierCatalogMappingGrid.querySelectorAll("[data-supplier-catalog-field]").forEach((select) => {
    const fieldKey = select.dataset.supplierCatalogField;
    if (!fieldKey) {
      return;
    }
    select.value = supplierCatalogState.mapping[fieldKey] || "";
    select.addEventListener("change", () => {
      supplierCatalogState.mapping[fieldKey] = select.value;
      supplierCatalogState.preview = null;
      renderSupplierCatalogPreviewState();
    });
  });
}

function renderSupplierCatalogSampleRows() {
  if (supplierCatalogState.rawRows.length <= 1) {
    supplierCatalogSample.innerHTML = '<p class="muted-copy">Nessuna anteprima disponibile.</p>';
    return;
  }

  const sampleHeaders = supplierCatalogState.headers;
  const sampleRows = supplierCatalogState.rawRows.slice(1, 5);
  supplierCatalogSample.innerHTML = `
    <div class="supplier-catalog-sample-head">
      <p class="eyebrow">Estratto file</p>
      <p class="muted-copy">Prime ${sampleRows.length} righe del catalogo caricato.</p>
    </div>
    <div class="table-wrap supplier-catalog-table-wrap">
      <table class="supplier-catalog-table">
        <thead>
          <tr>${sampleHeaders.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr>
        </thead>
        <tbody>
          ${sampleRows
            .map(
              (cells) => `<tr>${sampleHeaders
                .map((_, index) => `<td>${escapeHtml(cells[index] || "")}</td>`)
                .join("")}</tr>`
            )
            .join("")}
        </tbody>
      </table>
    </div>
  `;
}

function renderSupplierCatalogPreviewState() {
  supplierCatalogFileLabel.textContent = supplierCatalogState.fileName || "Nessun file selezionato.";
  const confirmButton = document.getElementById("confirmSupplierCatalogImportButton");
  const preview = supplierCatalogState.preview;

  if (!preview) {
    confirmButton.disabled = true;
    supplierCatalogSummary.innerHTML = '<p class="muted-copy">Genera l&apos;anteprima per vedere match, nuovi prodotti e righe da rivedere.</p>';
    supplierCatalogPreview.innerHTML = "";
    return;
  }

  const summary = preview.summary || {};
  confirmButton.disabled = !Number(summary.importable_rows || 0);
  supplierCatalogSummary.innerHTML = `
    <div class="comparison-grid supplier-catalog-summary-grid">
      <article class="snapshot-tile">
        <span>Righe lette</span>
        <strong>${summary.total_rows || 0}</strong>
      </article>
      <article class="snapshot-tile">
        <span>Importabili</span>
        <strong>${summary.importable_rows || 0}</strong>
      </article>
      <article class="snapshot-tile">
        <span>Match sicuri</span>
        <strong>${summary.matched_rows || 0}</strong>
      </article>
      <article class="snapshot-tile">
        <span>Nuovi</span>
        <strong>${summary.new_rows || 0}</strong>
      </article>
      <article class="snapshot-tile">
        <span>Ambigui</span>
        <strong>${summary.ambiguous_rows || 0}</strong>
      </article>
      <article class="snapshot-tile">
        <span>Saltati</span>
        <strong>${summary.skipped_rows || 0}</strong>
      </article>
    </div>
    <p class="muted-copy supplier-catalog-copy">
      I match per codice o identita aggiorneranno il catalogo locale. Le righe ambigue restano fuori dall&apos;import automatico.
    </p>
  `;

  const rows = Array.isArray(preview.rows) ? preview.rows : [];
  supplierCatalogPreview.innerHTML = `
    <div class="table-wrap supplier-catalog-table-wrap">
      <table class="supplier-catalog-table">
        <thead>
          <tr>
            <th>Riga</th>
            <th>Sorgente</th>
            <th>Esito</th>
            <th>Destinazione</th>
            <th>Note</th>
          </tr>
        </thead>
        <tbody>
          ${rows
            .map((row) => {
              const warnings = Array.isArray(row.warnings) && row.warnings.length ? row.warnings.join(" ") : "";
              const sourceBits = [row.source_name, row.source_supplier_name, row.source_lot_code].filter(Boolean).join(" · ");
              const proposed = row.proposed_product || null;
              const destination = proposed
                ? [proposed.product_name, proposed.supplier_name, proposed.lot_code].filter(Boolean).join(" · ")
                : "Non importata";
              const confidence = Number(row.confidence || 0);
              const confidenceLabel = confidence > 0 ? ` (${Math.round(confidence * 100)}%)` : "";
              return `
                <tr>
                  <td>${row.row_index || ""}</td>
                  <td>${escapeHtml(sourceBits)}</td>
                  <td>
                    <span class="supplier-catalog-result supplier-catalog-result-${escapeHtml(row.match_type || "skipped")}">
                      ${escapeHtml(row.match_label || row.match_type || "saltato")}${confidenceLabel}
                    </span>
                  </td>
                  <td>${escapeHtml(destination)}</td>
                  <td>${escapeHtml(warnings)}</td>
                </tr>
              `;
            })
            .join("")}
        </tbody>
      </table>
    </div>
  `;
}

async function handleSupplierCatalogFileImport(event) {
  const file = event.target.files?.[0];
  if (!file) {
    return;
  }

  try {
    const text = await file.text();
    const rows = parseCsvRows(text);
    if (rows.length <= 1) {
      throw new Error("Il file non contiene righe catalogo valide.");
    }

    const headers = rows[0].map((header, index) => {
      const value = String(header || "").trim();
      return value || `Colonna ${index + 1}`;
    });
    supplierCatalogState = {
      fileName: file.name,
      rawRows: rows,
      headers,
      mapping: guessSupplierCatalogMapping(headers),
      preview: null
    };

    renderSupplierCatalogMappingControls();
    renderSupplierCatalogSampleRows();
    renderSupplierCatalogPreviewState();
    showModal("supplierCatalogModal");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Import catalogo fornitore non riuscito.");
  } finally {
    event.target.value = "";
  }
}

function readSupplierCatalogMappedCell(cells, fieldKey) {
  const rawIndex = supplierCatalogState.mapping[fieldKey];
  const index = Number.parseInt(String(rawIndex || ""), 10);
  if (!Number.isInteger(index) || index < 0) {
    return "";
  }
  return String(cells[index] || "").trim();
}

function buildSupplierCatalogMappedRows() {
  if (!supplierCatalogState.rawRows.length) {
    throw new Error("Seleziona prima un file catalogo.");
  }

  const requiredField = supplierCatalogState.mapping.source_name;
  if (requiredField === undefined || requiredField === null || requiredField === "") {
    throw new Error("Associa almeno la colonna Prodotto prima di generare l'anteprima.");
  }

  return supplierCatalogState.rawRows
    .slice(1)
    .map((cells) => {
      const row = {
        source_name: readSupplierCatalogMappedCell(cells, "source_name")
      };

      [
        "source_lot_code",
        "source_supplier_name",
        "product_code",
        "category",
        "notes"
      ].forEach((fieldKey) => {
        const value = readSupplierCatalogMappedCell(cells, fieldKey);
        if (value) {
          row[fieldKey] = value;
        }
      });

      [
        "final_price_vat",
        "vat_rate",
        "weight_kg",
        "unit_price_per_kg",
        "units_per_pack",
        "liters_per_unit"
      ].forEach((fieldKey) => {
        const parsed = parseOptionalNumber(readSupplierCatalogMappedCell(cells, fieldKey));
        if (parsed !== null) {
          row[fieldKey] = parsed;
        }
      });

      return row;
    })
    .filter((row) => row.source_name);
}

async function previewSupplierCatalogImport() {
  try {
    const rows = buildSupplierCatalogMappedRows();
    if (!rows.length) {
      throw new Error("Il file non contiene righe mappate importabili.");
    }

    supplierCatalogState.preview = await apiFetch("/api/prodotti/catalogo/preview", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        supplier_name: supplierCatalogDefaultSupplierInput.value.trim() || null,
        default_lot_code: supplierCatalogDefaultLotInput.value.trim() || null,
        create_missing: Boolean(supplierCatalogCreateMissingInput.checked),
        rows
      })
    });
    renderSupplierCatalogPreviewState();
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Anteprima catalogo fornitore non riuscita.");
  }
}

async function importSupplierCatalogPreview() {
  try {
    const previewRows = Array.isArray(supplierCatalogState.preview?.rows) ? supplierCatalogState.preview.rows : [];
    const payload = previewRows
      .filter((row) => row.importable && row.proposed_product)
      .map((row) => row.proposed_product);

    if (!payload.length) {
      throw new Error("Non ci sono righe valide da importare.");
    }

    const result = await apiFetch("/api/prodotti", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });
    await loadProducts();
    closeModal("supplierCatalogModal");
    supplierCatalogState.preview = null;
    renderSupplierCatalogPreviewState();
    const duplicateRows = Number(result?.duplicate_rows) || 0;
    const createdRows = Number(result?.created) || 0;
    const updatedRows = Number(result?.updated) || 0;
    showFlash(
      duplicateRows > 0
        ? `Catalogo fornitore importato. Creati ${createdRows}, aggiornati ${updatedRows}, ignorate ${duplicateRows} righe duplicate.`
        : `Catalogo fornitore importato. Creati ${createdRows}, aggiornati ${updatedRows}.`
    );
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Import catalogo fornitore non riuscito.");
  }
}

function parseCsvProducts(csvText) {
  const rows = parseCsvRows(csvText);
  if (rows.length <= 1) {
    return [];
  }

  const headers = rows[0].map((header) => normalizeCsvHeader(header));
  const getIndex = (aliases, fallback = -1) => {
    const index = headers.findIndex((header) => aliases.includes(header));
    return index >= 0 ? index : fallback;
  };
  const readCell = (cells, index) => (index >= 0 ? (cells[index] || "").trim() : "");
  const assignText = (target, key, value) => {
    const text = value.trim();
    if (text) {
      target[key] = text;
    }
  };
  const assignNumber = (target, key, value) => {
    const parsed = parseOptionalNumber(value);
    if (parsed !== null) {
      target[key] = parsed;
    }
  };

  const productIndex = getIndex(["prodotto", "product_name", "nome"], 0);
  const lotIndex = getIndex(["lotto", "lot_code"], 1);
  const supplierIndex = getIndex(["fornitore", "supplier_name"], 2);
  const codeIndex = getIndex(["codice_prodotto", "product_code", "codice"]);
  const finalPriceIndex = getIndex(["prezzo_finale_ivato", "final_price_vat", "prezzo"]);
  const vatIndex = getIndex(["iva", "vat_rate"]);
  const weightIndex = getIndex(["um", "weight_kg", "peso_kg", "peso"]);
  const pricePerKgIndex = getIndex(["prezzo_un_kg", "unit_price_per_kg", "prz_un_kg"]);
  const categoryIndex = getIndex(["categoria", "category"]);
  const notesIndex = getIndex(["note", "notes"]);
  const unitsPerPackIndex = getIndex(["units_per_pack", "unit_per_pack"]);
  const litersPerUnitIndex = getIndex(["liters_per_unit"]);

  return rows
    .slice(1)
    .map((cells) => {
      const item = {
        prodotto: readCell(cells, productIndex),
        lotto: readCell(cells, lotIndex),
        fornitore: readCell(cells, supplierIndex)
      };
      assignText(item, "product_code", readCell(cells, codeIndex));
      assignNumber(item, "final_price_vat", readCell(cells, finalPriceIndex));
      assignNumber(item, "vat_rate", readCell(cells, vatIndex));
      assignNumber(item, "weight_kg", readCell(cells, weightIndex));
      assignNumber(item, "unit_price_per_kg", readCell(cells, pricePerKgIndex));
      assignText(item, "category", readCell(cells, categoryIndex));
      assignText(item, "notes", readCell(cells, notesIndex));
      assignNumber(item, "units_per_pack", readCell(cells, unitsPerPackIndex));
      assignNumber(item, "liters_per_unit", readCell(cells, litersPerUnitIndex));
      return item;
    })
    .filter((item) => item.prodotto && item.lotto && item.fornitore);
}

async function saveNewProduct() {
  try {
    const payload = {
      prodotto: document.getElementById("newProductName").value.trim(),
      lotto: document.getElementById("newProductLot").value.trim(),
      fornitore: document.getElementById("newProductSupplier").value.trim()
    };

    if (!payload.prodotto || !payload.lotto || !payload.fornitore) {
      window.alert("Compila prodotto, lotto e fornitore.");
      return;
    }

    const optionalNumberFields = [
      ["units_per_pack", "newProductUnits"],
      ["liters_per_unit", "newProductLiters"],
      ["final_price_vat", "newProductPrice"],
      ["vat_rate", "newProductVat"],
      ["weight_kg", "newProductWeight"],
      ["unit_price_per_kg", "newProductPriceKg"]
    ];
    optionalNumberFields.forEach(([fieldName, inputId]) => {
      const parsed = parseOptionalNumber(document.getElementById(inputId).value);
      if (parsed !== null) {
        payload[fieldName] = parsed;
      }
    });

    [
      ["product_code", "newProductCode"],
      ["category", "newProductCategory"],
      ["notes", "newProductNote"]
    ].forEach(([fieldName, inputId]) => {
      const value = document.getElementById(inputId).value.trim();
      if (value) {
        payload[fieldName] = value;
      }
    });

    await apiFetch("/api/prodotti", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify([payload])
    });

    clearAddProductForm();
    closeModal("addProductModal");
    await loadProducts();
    showFlash("Prodotto salvato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Salvataggio prodotto non riuscito.");
  }
}

function editableProductSearchText(product) {
  return normalizeCatalogText(
    [
      product.product_name,
      product.lot_code,
      product.supplier_name,
      product.product_code || "",
      product.category || "",
      product.notes || ""
    ].join(" ")
  );
}

function getFilteredEditableProducts() {
  const searchValue = normalizeCatalogText(editProductSearchInput.value || "");
  if (!searchValue) {
    return products;
  }
  return products.filter((product) => editableProductSearchText(product).includes(searchValue));
}

function setEditProductFormDisabled(disabled) {
  [
    "editProductName",
    "editProductLot",
    "editProductSupplier",
    "editProductCode",
    "editProductPrice",
    "editProductVat",
    "editProductWeight",
    "editProductPriceKg",
    "editProductCategory",
    "editProductUnits",
    "editProductLiters",
    "editProductNote",
    "saveEditedProductButton",
    "deleteEditedProductButton"
  ].forEach((id) => {
    const element = document.getElementById(id);
    if (element) {
      element.disabled = disabled;
    }
  });
}

function renderEditableProductForm(product) {
  if (!product) {
    editProductHeading.textContent = "Nessun prodotto selezionato";
    editProductMeta.textContent = "Seleziona un prodotto dalla lista per modificarlo.";
    document.getElementById("editProductName").value = "";
    document.getElementById("editProductLot").value = "";
    document.getElementById("editProductSupplier").value = "";
    document.getElementById("editProductCode").value = "";
    document.getElementById("editProductPrice").value = "";
    document.getElementById("editProductVat").value = "";
    document.getElementById("editProductWeight").value = "";
    document.getElementById("editProductPriceKg").value = "";
    document.getElementById("editProductCategory").value = "";
    document.getElementById("editProductUnits").value = "";
    document.getElementById("editProductLiters").value = "";
    document.getElementById("editProductNote").value = "";
    setEditProductFormDisabled(true);
    return;
  }

  editProductHeading.textContent = product.product_name;
  editProductMeta.textContent = [product.supplier_name, product.lot_code, product.product_code || "codice non indicato"]
    .filter(Boolean)
    .join(" · ");

  document.getElementById("editProductName").value = product.product_name || "";
  document.getElementById("editProductLot").value = product.lot_code || "";
  document.getElementById("editProductSupplier").value = product.supplier_name || "";
  document.getElementById("editProductCode").value = product.product_code || "";
  document.getElementById("editProductPrice").value =
    product.final_price_vat === null || product.final_price_vat === undefined ? "" : String(product.final_price_vat);
  document.getElementById("editProductVat").value =
    product.vat_rate === null || product.vat_rate === undefined ? "" : String(product.vat_rate);
  document.getElementById("editProductWeight").value =
    product.weight_kg === null || product.weight_kg === undefined ? "" : String(product.weight_kg);
  document.getElementById("editProductPriceKg").value =
    product.unit_price_per_kg === null || product.unit_price_per_kg === undefined ? "" : String(product.unit_price_per_kg);
  document.getElementById("editProductCategory").value = product.category || "";
  document.getElementById("editProductUnits").value =
    product.units_per_pack === null || product.units_per_pack === undefined ? "" : String(product.units_per_pack);
  document.getElementById("editProductLiters").value =
    product.liters_per_unit === null || product.liters_per_unit === undefined ? "" : String(product.liters_per_unit);
  document.getElementById("editProductNote").value = product.notes || "";
  setLotPriceSyncState(
    document.getElementById("editProductLot"),
    document.getElementById("editProductUnits"),
    product.lot_code || "",
    product.units_per_pack ?? inferSiblingMultipackUnits(product)
  );
  setEditProductFormDisabled(false);
}

function syncEditableProductSelection() {
  const filteredProducts = getFilteredEditableProducts();
  if (!filteredProducts.length) {
    selectedEditableProductId = null;
    renderEditableProductForm(null);
    return;
  }

  if (!filteredProducts.some((product) => Number(product.id) === Number(selectedEditableProductId))) {
    selectedEditableProductId = filteredProducts[0].id;
  }

  const selectedProduct = filteredProducts.find((product) => Number(product.id) === Number(selectedEditableProductId)) || null;
  renderEditableProductForm(selectedProduct);
}

function renderEditableProductsList() {
  const filteredProducts = getFilteredEditableProducts();
  if (!filteredProducts.length) {
    editProductsList.innerHTML = '<p class="muted-copy">Nessun prodotto trovato.</p>';
    selectedEditableProductId = null;
    renderEditableProductForm(null);
    return;
  }

  if (!filteredProducts.some((product) => Number(product.id) === Number(selectedEditableProductId))) {
    selectedEditableProductId = filteredProducts[0].id;
  }

  editProductsList.innerHTML = filteredProducts
    .map((product) => {
      const isActive = Number(product.id) === Number(selectedEditableProductId);
      return `
        <button
          type="button"
          class="product-editor-item ${isActive ? "is-active" : ""}"
          data-edit-product-id="${escapeAttribute(product.id)}"
        >
          <strong>${escapeHtml(product.product_name)}</strong>
          <span>${escapeHtml([product.supplier_name, product.lot_code].filter(Boolean).join(" · "))}</span>
          <small>${escapeHtml(product.product_code || product.category || "Scheda catalogo")}</small>
        </button>
      `;
    })
    .join("");

  editProductsList.querySelectorAll("[data-edit-product-id]").forEach((button) => {
    button.addEventListener("click", () => {
      selectedEditableProductId = Number(button.dataset.editProductId);
      renderEditableProductsList();
    });
  });

  const selectedProduct = filteredProducts.find((product) => Number(product.id) === Number(selectedEditableProductId)) || null;
  renderEditableProductForm(selectedProduct);
}

function openEditProductsModal() {
  editProductSearchInput.value = "";
  if (products.length && (selectedEditableProductId === null || !products.some((product) => Number(product.id) === Number(selectedEditableProductId)))) {
    selectedEditableProductId = products[0].id;
  }
  renderEditableProductsList();
  showModal("editProductsModal");
}

async function saveEditedProduct() {
  if (selectedEditableProductId === null) {
    window.alert("Seleziona prima un prodotto da modificare.");
    return;
  }

  try {
    const payload = {
      prodotto: document.getElementById("editProductName").value.trim(),
      lotto: document.getElementById("editProductLot").value.trim(),
      fornitore: document.getElementById("editProductSupplier").value.trim(),
      product_code: document.getElementById("editProductCode").value.trim() || null,
      final_price_vat: parseOptionalNumber(document.getElementById("editProductPrice").value),
      vat_rate: parseOptionalNumber(document.getElementById("editProductVat").value),
      weight_kg: parseOptionalNumber(document.getElementById("editProductWeight").value),
      unit_price_per_kg: parseOptionalNumber(document.getElementById("editProductPriceKg").value),
      category: document.getElementById("editProductCategory").value.trim() || null,
      units_per_pack: parseOptionalNumber(document.getElementById("editProductUnits").value),
      liters_per_unit: parseOptionalNumber(document.getElementById("editProductLiters").value),
      notes: document.getElementById("editProductNote").value.trim() || null
    };

    if (!payload.prodotto || !payload.lotto || !payload.fornitore) {
      window.alert("Prodotto, lotto e fornitore sono obbligatori.");
      return;
    }

    await apiFetch(`/api/prodotti/${encodeURIComponent(selectedEditableProductId)}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });

    await loadProducts();
    renderEditableProductsList();
    showFlash("Prodotto aggiornato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Aggiornamento prodotto non riuscito.");
  }
}

async function deleteEditedProduct() {
  if (selectedEditableProductId === null) {
    window.alert("Seleziona prima un prodotto da eliminare.");
    return;
  }

  const selectedProduct = products.find((product) => Number(product.id) === Number(selectedEditableProductId)) || null;
  const productLabel = selectedProduct
    ? `${selectedProduct.product_name} · ${selectedProduct.supplier_name} · ${selectedProduct.lot_code}`
    : `#${selectedEditableProductId}`;
  const confirmed = window.confirm(`Vuoi eliminare il prodotto ${productLabel}?`);
  if (!confirmed) {
    return;
  }

  try {
    await apiFetch(`/api/prodotti/${encodeURIComponent(selectedEditableProductId)}`, {
      method: "DELETE"
    });

    await loadProducts();
    const remainingProducts = getFilteredEditableProducts();
    if (remainingProducts.length) {
      selectedEditableProductId = remainingProducts[0].id;
    } else {
      selectedEditableProductId = null;
    }
    renderEditableProductsList();
    showFlash("Prodotto eliminato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Eliminazione prodotto non riuscita.");
  }
}

function clearAddProductForm() {
  document.getElementById("newProductName").value = "";
  document.getElementById("newProductLot").value = "";
  document.getElementById("newProductSupplier").value = "";
  document.getElementById("newProductCode").value = "";
  document.getElementById("newProductPrice").value = "";
  document.getElementById("newProductVat").value = "";
  document.getElementById("newProductWeight").value = "";
  document.getElementById("newProductPriceKg").value = "";
  document.getElementById("newProductCategory").value = "";
  document.getElementById("newProductNote").value = "";
  document.getElementById("newProductUnits").value = "";
  document.getElementById("newProductLiters").value = "";
  setLotPriceSyncState(document.getElementById("newProductLot"), document.getElementById("newProductUnits"), "", null);
}

function parseOptionalNumber(value) {
  const trimmed = value.trim();
  if (!trimmed) {
    return null;
  }
  if (trimmed.toUpperCase() === "PZ" || trimmed === "#DIV/0!") {
    return null;
  }
  let normalized = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
  if (normalized.includes(",") && normalized.includes(".")) {
    normalized = normalized.replaceAll(".", "").replace(",", ".");
  } else if (normalized.includes(",")) {
    normalized = normalized.replace(",", ".");
  }
  const parsed = Number.parseFloat(normalized);
  return Number.isFinite(parsed) ? parsed : null;
}

function buildProductUpsertPayload(entry, lotCode, draftState = {}) {
  const normalizedLot = normalizeLotCode(lotCode);
  const variant = findVariantForLot(entry, normalizedLot);
  const derivedVariant = deriveSelectedLotVariant(entry, {
    lotCode: normalizedLot,
    variant,
    draftState,
    draftUnitsPerPack: parseOptionalNumber(String(draftState.units_per_pack || "")),
    existingUnitsPerPack: variant && Number.isFinite(Number(variant.units_per_pack)) ? Number(variant.units_per_pack) : null
  });
  const payload = {
    prodotto: entry.product_name,
    lotto: normalizedLot,
    fornitore: entry.supplier_name
  };

  [
    ["product_code", derivedVariant?.product_code],
    ["final_price_vat", derivedVariant?.final_price_vat],
    ["vat_rate", derivedVariant?.vat_rate],
    ["weight_kg", derivedVariant?.weight_kg],
    ["unit_price_per_kg", derivedVariant?.unit_price_per_kg],
    ["category", derivedVariant?.category],
    ["notes", derivedVariant?.notes],
    ["liters_per_unit", derivedVariant?.liters_per_unit]
  ].forEach(([fieldName, value]) => {
    if (value !== null && value !== undefined && value !== "") {
      payload[fieldName] = value;
    }
  });

  if (derivedVariant?.units_per_pack !== null && derivedVariant?.units_per_pack !== undefined && derivedVariant?.units_per_pack !== "") {
    payload.units_per_pack = derivedVariant.units_per_pack;
  }

  return payload;
}

async function ensureSelectedProductsPersisted(selectedItems) {
  const upserts = [];

  selectedItems.forEach((item) => {
    const draftUnitsPerPack = parseOptionalNumber(String(item.draftState?.units_per_pack || ""));
    if (item.requiresUnitsPerPack && draftUnitsPerPack === null) {
      throw new Error(
        `Per ${item.product_name} con lotto ${formatLotCodeLabel(item.lot_code)} indicami quante bottiglie contiene un cartone.`
      );
    }

    if (!item.id) {
      upserts.push(buildProductUpsertPayload(item.entry, item.lot_code, item.draftState));
      return;
    }

    const existingUnitsPerPack = Number.isFinite(Number(item.variant?.units_per_pack))
      ? Number(item.variant.units_per_pack)
      : null;
    if (isMultipackLotCode(item.lot_code) && existingUnitsPerPack === null && draftUnitsPerPack !== null) {
      upserts.push(buildProductUpsertPayload(item.entry, item.lot_code, item.draftState));
    }
  });

  if (!upserts.length) {
    return selectedItems;
  }

  await apiFetch("/api/prodotti", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(upserts)
  });

  const draftSelections = selectedItems
    .filter((item) => !item.id)
    .map((item) => ({
      entryKey: item.entry.key,
      lotCode: item.lot_code,
      quantity: item.quantity
    }));

  await loadProducts();

  draftSelections.forEach((selection) => {
    const draftKey = buildDraftQuantityKey(selection.entryKey, selection.lotCode);
    const refreshedEntry = catalogEntries.find((entry) => entry.key === selection.entryKey);
    const refreshedVariant = refreshedEntry ? findVariantForLot(refreshedEntry, selection.lotCode) : null;
    const draftQuantity = Number.parseInt(String(currentQuantities[draftKey] || selection.quantity), 10) || 0;
    delete currentQuantities[draftKey];
    if (refreshedVariant && draftQuantity > 0) {
      currentQuantities[String(refreshedVariant.id)] = draftQuantity;
    }
    setDraftLotState(selection.entryKey, selection.lotCode, {});
  });

  persistQuantities();
  renderProductsTable();
  return buildSelectedItems();
}

function buildSelectedItems() {
  return catalogEntries
    .map((entry) => {
      const selection = getEntrySelection(entry);
      if (selection.quantity <= 0) {
        return null;
      }

      const resolvedVariant = deriveSelectedLotVariant(entry, selection);
      return {
        id: selection.variant ? selection.variant.id : null,
        entry,
        variant: selection.variant,
        quantity: selection.quantity,
        product_name: entry.product_name,
        lot_code: selection.lotCode,
        supplier_name: entry.supplier_name,
        product_code: resolvedVariant?.product_code || null,
        final_price_vat: resolvedVariant?.final_price_vat ?? null,
        vat_rate: resolvedVariant?.vat_rate ?? null,
        weight_kg: resolvedVariant?.weight_kg ?? null,
        unit_price_per_kg: resolvedVariant?.unit_price_per_kg ?? null,
        category: resolvedVariant?.category || null,
        notes: resolvedVariant?.notes || null,
        units_per_pack: resolvedVariant?.units_per_pack ?? null,
        liters_per_unit: resolvedVariant?.liters_per_unit ?? null,
        estimated_line_total: _estimateItemLineTotal({
          quantity: selection.quantity,
          final_price_vat: resolvedVariant?.final_price_vat ?? null,
          final_price_vat_snapshot: null,
          estimated_line_total: null
        }),
        draftState: selection.draftState,
        requiresUnitsPerPack: selection.needsUnitsPerPack
      };
    })
    .filter(Boolean);
}

function _coerceFiniteNumber(value) {
  const numeric = Number(value);
  return Number.isFinite(numeric) ? numeric : null;
}

function _estimateUnitPriceVat(item) {
  const explicitPrice =
    _coerceFiniteNumber(item?.final_price_vat_snapshot) ??
    _coerceFiniteNumber(item?.final_price_vat);
  if (explicitPrice !== null && explicitPrice > 0) {
    return explicitPrice;
  }

  const unitPricePerKg = _coerceFiniteNumber(item?.unit_price_per_kg);
  const weightKg = _coerceFiniteNumber(item?.weight_kg);
  if (unitPricePerKg === null || unitPricePerKg <= 0 || weightKg === null || weightKg <= 0) {
    return null;
  }

  let estimatedUnitTotal = unitPricePerKg * weightKg;
  const vatRate = _coerceFiniteNumber(item?.vat_rate);
  if (vatRate !== null && vatRate > 0) {
    estimatedUnitTotal *= 1 + vatRate / 100;
  }
  return Number(estimatedUnitTotal.toFixed(2));
}

function _estimateItemLineTotal(item) {
  const snapshotLineTotal = _coerceFiniteNumber(item?.estimated_line_total);
  const unitPrice = _estimateUnitPriceVat(item);
  if (snapshotLineTotal !== null && (snapshotLineTotal > 0 || unitPrice === null || unitPrice <= 0)) {
    return Number(snapshotLineTotal.toFixed(2));
  }

  const quantity = Number(item?.quantity || 0);
  if (!Number.isFinite(quantity) || quantity <= 0) {
    return null;
  }

  if (unitPrice === null || unitPrice < 0) {
    return null;
  }

  return Number((quantity * unitPrice).toFixed(2));
}

function calculateOrderEconomics(items) {
  return items.reduce(
    (summary, item) => {
      const quantity = Number(item?.quantity || 0);
      if (!Number.isFinite(quantity) || quantity <= 0) {
        return summary;
      }

      summary.totalLines += 1;
      summary.totalQuantity += quantity;
      const estimatedLineTotal = _estimateItemLineTotal(item);
      if (estimatedLineTotal === null) {
        summary.missingPriceLines += 1;
        summary.missingPriceQuantity += quantity;
        return summary;
      }

      summary.pricedLines += 1;
      summary.totalAmount += estimatedLineTotal;
      return summary;
    },
    {
      totalLines: 0,
      totalQuantity: 0,
      pricedLines: 0,
      missingPriceLines: 0,
      missingPriceQuantity: 0,
      totalAmount: 0
    }
  );
}

function renderOrderEstimate(items = buildSelectedItems()) {
  if (!orderEstimateTotal || !orderEstimateMeta) {
    return;
  }

  const economics = calculateOrderEconomics(items);
  if (!economics.totalLines) {
    orderEstimateTotal.textContent = EURO_FORMATTER.format(0);
    orderEstimateMeta.textContent = "Aggiungi quantità per vedere il totale stimato.";
    return;
  }

  if (!economics.pricedLines) {
    orderEstimateTotal.textContent = "n/d";
    orderEstimateMeta.textContent =
      "Totale non disponibile: nei prodotti selezionati mancano ancora i prezzi di catalogo.";
    return;
  }

  orderEstimateTotal.textContent = EURO_FORMATTER.format(economics.totalAmount);
  const parts = [
    `${economics.totalLines} righe`,
    `${economics.totalQuantity} pezzi`,
    "stima sui prezzi ivati del catalogo"
  ];
  if (economics.missingPriceLines) {
    parts.push(`${economics.missingPriceLines} righe senza prezzo escluse`);
  }
  orderEstimateMeta.textContent = parts.join(" · ");
}

async function suspendOrder() {
  try {
    let selectedItems = buildSelectedItems();
    if (!selectedItems.length) {
      window.alert("Seleziona almeno un prodotto.");
      return;
    }

    selectedItems = await ensureSelectedProductsPersisted(selectedItems);
    if (selectedItems.some((item) => !item.id)) {
      throw new Error("Non riesco ancora a risolvere tutti i prodotti selezionati. Riprova tra un attimo.");
    }
    const payload = Object.fromEntries(
      selectedItems
        .filter((item) => item.id)
        .map((item) => [String(item.id), item.quantity])
    );

    await apiFetch(`/api/ordine_sospeso?staff=${encodeURIComponent(currentStaff)}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });
    closeMenus();
    showFlash("Ordine sospeso salvato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Salvataggio ordine sospeso non riuscito.");
  }
}

async function fetchSuspendedOrder() {
  const payload = await apiFetch(`/api/ordine_sospeso?staff=${encodeURIComponent(currentStaff)}`);
  return payload && Object.keys(payload).length ? payload : null;
}

function applySuspendedOrder(payload, flashMessageText) {
  if (editingHistoryBatch?.batchId) {
    setHistoryEditContext(null);
  }
  currentQuantities = payload;
  persistQuantities();
  renderProductsTable();
  if (flashMessageText) {
    showFlash(flashMessageText);
  }
}

async function loadSuspendedOrder({ notify = true } = {}) {
  try {
    const payload = await fetchSuspendedOrder();
    if (!payload) {
      if (notify) {
        showFlash("Nessun ordine sospeso da caricare.");
      }
      return false;
    }

    closeMenus();
    applySuspendedOrder(payload, notify ? "Ordine sospeso caricato." : null);
    return true;
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Caricamento ordine sospeso non riuscito.");
    return false;
  }
}

async function deleteSuspendedOrder({ confirmDelete = true, notify = true, notifyWhenMissing = true } = {}) {
  try {
    const payload = await fetchSuspendedOrder();
    if (!payload) {
      if (notifyWhenMissing) {
        showFlash("Nessun ordine sospeso da cancellare.");
      }
      return false;
    }

    if (confirmDelete && !window.confirm("Vuoi cancellare l'ordine sospeso salvato?")) {
      return false;
    }

    await apiFetch(`/api/ordine_sospeso?staff=${encodeURIComponent(currentStaff)}`, { method: "DELETE" });
    closeMenus();
    if (notify) {
      showFlash("Ordine sospeso cancellato.");
    }
    return true;
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Cancellazione ordine sospeso non riuscita.");
    return false;
  }
}

async function deleteHistoryBatch(batchId) {
  if (!Number.isFinite(Number(batchId)) || Number(batchId) <= 0) {
    throw new Error("Ordine storico non valido.");
  }

  await apiFetch("/api/ordini", {
    method: "DELETE",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      batch_id: Number(batchId)
    })
  });
}

async function checkSuspendedOrder() {
  const payload = await fetchSuspendedOrder();
  if (!payload || !Object.keys(payload).length) {
    return;
  }
  if (window.confirm("È presente un ordine sospeso. Vuoi caricarlo?")) {
    applySuspendedOrder(payload, "Ordine sospeso caricato.");
  }
}

async function confirmOrder() {
  try {
    const editingBatchId = editingHistoryBatch?.batchId || null;
    let selectedItems = buildSelectedItems();
    if (!selectedItems.length) {
      if (editingBatchId) {
        const confirmedDate = editingHistoryBatch?.confirmedAt ? formatDate(editingHistoryBatch.confirmedAt) : "questo ordine";
        if (!window.confirm(`Non ci sono piu righe selezionate. Vuoi eliminare definitivamente l'ordine del ${confirmedDate}?`)) {
          return;
        }
        await deleteHistoryBatch(editingBatchId);
        resetOrderDraftState();
        setHistoryEditContext(null);
        renderProductsTable();
        await deleteSuspendedOrder({ confirmDelete: false, notify: false, notifyWhenMissing: false });
        await loadAvailableYears();
        await loadHistory(selectedYear);
        showFlash("Ordine storico eliminato.");
        return;
      }
      window.alert("Nessun prodotto selezionato.");
      return;
    }

    selectedItems = await ensureSelectedProductsPersisted(selectedItems);
    if (selectedItems.some((item) => !item.id)) {
      throw new Error("Non riesco ancora a risolvere tutti i prodotti selezionati. Riprova tra un attimo.");
    }

    const payload = {
      staff: editingHistoryBatch?.staff || currentStaff,
      items: selectedItems.map((item) => ({
        product_id: item.id,
        quantity: item.quantity
      }))
    };

    if (editingBatchId) {
      payload.batch_id = editingBatchId;
    }

    await apiFetch("/api/ordini", {
      method: editingBatchId ? "PUT" : "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });

    resetOrderDraftState();
    setHistoryEditContext(null);
    renderProductsTable();
    await deleteSuspendedOrder({ confirmDelete: false, notify: false, notifyWhenMissing: false });
    await loadAvailableYears();
    await loadHistory(selectedYear);
    summaryReturnModalId = null;
    renderGroupedSummary(
      selectedItems,
      "Ordine raggruppato per fornitore",
      "summaryContent",
      true
    );
    showModal("summaryModal");
    showFlash(editingBatchId ? "Ordine storico aggiornato." : "Ordine confermato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Conferma ordine non riuscita.");
  }
}

function clearQuantities() {
  currentQuantities = {};
  persistQuantities();
  renderProductsTable();
}

function updateCounters() {
  if (productCountLabel) {
    productCountLabel.textContent = String(catalogEntries.length || products.length);
  }
  const selectionCount = Object.values(currentQuantities).reduce((sum, value) => sum + (Number(value) || 0), 0);
  if (selectionCountLabel) {
    selectionCountLabel.textContent = String(selectionCount);
  }
  renderOrderEstimate();
}

function renderProductMeta(product) {
  const metaParts = [];
  if (product.product_code) {
    metaParts.push(`Cod. ${product.product_code}`);
  }
  if (product.final_price_vat !== null && product.final_price_vat !== undefined) {
    metaParts.push(formatCurrency(product.final_price_vat));
  }
  if (product.unit_price_per_kg !== null && product.unit_price_per_kg !== undefined) {
    metaParts.push(`${formatCurrency(product.unit_price_per_kg)}/kg`);
  }
  if (product.category) {
    metaParts.push(product.category);
  }

  const metaLine = metaParts.length ? `<div class="product-meta">${escapeHtml(metaParts.join(" · "))}</div>` : "";
  const notesLine = product.notes ? `<div class="product-note">${escapeHtml(product.notes)}</div>` : "";
  return `${metaLine}${notesLine}`;
}

function renderGroupedSummary(items, title, targetId, includeActions = false) {
  const grouped = groupItemsBySupplier(items);
  const overallEconomics = calculateOrderEconomics(items);
  const target = document.getElementById(targetId);
  target.innerHTML = `
    <div class="modal-head">
      <div>
        <p class="eyebrow">Riepilogo</p>
        <h3>${escapeHtml(title)}</h3>
      </div>
    </div>
    <div class="summary-kpis">
      <article class="summary-kpi">
        <span>Righe</span>
        <strong>${overallEconomics.totalLines}</strong>
      </article>
      <article class="summary-kpi">
        <span>Pezzi</span>
        <strong>${overallEconomics.totalQuantity}</strong>
      </article>
      <article class="summary-kpi">
        <span>Totale stimato</span>
        <strong>${overallEconomics.pricedLines ? formatCurrency(overallEconomics.totalAmount) : "n/d"}</strong>
      </article>
    </div>
    ${
      overallEconomics.missingPriceLines
        ? `<p class="summary-note">Prezzi mancanti esclusi dalla stima: ${overallEconomics.missingPriceLines} righe.</p>`
        : ""
    }
    ${grouped
      .map((group) => {
        const groupEconomics = calculateOrderEconomics(group.items);
        const message = buildSupplierSummaryText(group.items, groupEconomics);
        return `
          <section class="summary-group">
            <div class="summary-group-head">
              <h4>${escapeHtml(group.supplier)}</h4>
              <span>${groupEconomics.pricedLines ? formatCurrency(groupEconomics.totalAmount) : "n/d"}</span>
            </div>
            <pre>${escapeHtml(message)}</pre>
            ${
              includeActions
                ? `
                  <div class="summary-actions">
                    <button type="button" class="copy-button" data-copy-text="${escapeAttribute(message)}">Copia</button>
                    <a class="whatsapp-button" target="_blank" rel="noreferrer" href="https://wa.me/?text=${encodeURIComponent(message)}">WhatsApp</a>
                  </div>
                `
                : ""
            }
          </section>
        `;
      })
      .join("")}
  `;

  target.querySelectorAll("[data-copy-text]").forEach((button) => {
    button.addEventListener("click", () => void copyToClipboard(button.dataset.copyText || ""));
  });
}

function groupItemsBySupplier(items) {
  const map = new Map();
  items.forEach((item) => {
    const supplier = item.supplier_name || "Fornitore non indicato";
    if (!map.has(supplier)) {
      map.set(supplier, []);
    }
    map.get(supplier).push(item);
  });

  return [...map.entries()].map(([supplier, groupedItems]) => ({
    supplier,
    items: groupedItems
  }));
}

function buildSupplierSummaryText(items, economics = calculateOrderEconomics(items)) {
  const lines = items.map((item) => buildSupplierSummaryLine(item));
  if (economics.pricedLines) {
    lines.push(`Totale stimato fornitore: ${formatCurrency(economics.totalAmount)}`);
  }
  if (economics.missingPriceLines) {
    lines.push(`Prezzi mancanti: ${economics.missingPriceLines} righe escluse dalla stima`);
  }
  return lines.join("\n");
}

function buildSupplierSummaryLine(item) {
  const parts = [];
  if (item.product_code) {
    parts.push(`[${item.product_code}]`);
  }
  parts.push(item.product_name || "");
  parts.push(`x ${item.quantity}`);
  if (item.lot_code) {
    parts.push(`(${item.lot_code})`);
  }
  const unitPrice = _coerceFiniteNumber(item.final_price_vat_snapshot) ?? _coerceFiniteNumber(item.final_price_vat);
  if (unitPrice !== null) {
    parts.push(`- ${formatCurrency(unitPrice)}`);
  }
  const lineTotal = _estimateItemLineTotal(item);
  if (lineTotal !== null) {
    parts.push(`= ${formatCurrency(lineTotal)}`);
  }
  return parts.filter(Boolean).join(" ");
}

function openSupplierSummary() {
  const selectedItems = buildSelectedItems();
  if (!selectedItems.length) {
    window.alert("Seleziona almeno un prodotto.");
    return;
  }

  renderGroupedSummary(selectedItems, "Riepilogo fornitori", "suppliersContent", true);
  showModal("suppliersModal");
}

function openHistoryArchive() {
  historySearchInput.value = "";
  setHistoryTab("daily");
  updateHistoryYearLabel();
  renderHistory();
  showModal("historyModal");
}

function setHistoryTab(targetTab) {
  document.querySelectorAll("[data-history-tab]").forEach((button) => {
    button.classList.toggle("is-active", button.dataset.historyTab === targetTab);
  });
  document.getElementById("historyDailyContent").classList.toggle("is-active", targetTab === "daily");
  document.getElementById("historyTotalsContent").classList.toggle("is-active", targetTab === "totals");
}

function renderHistory() {
  const searchTokens = buildSearchTokens(historySearchInput.value || "");
  const dailyTarget = document.getElementById("historyDailyContent");
  const totalsTarget = document.getElementById("historyTotalsContent");

  const filteredBatches = historyBatches.filter((batch) => {
    if (!searchTokens.length) {
      return true;
    }
    return matchesSearchText(buildHistoryBatchSearchText(batch), searchTokens);
  });

  if (!filteredBatches.length) {
    dailyTarget.innerHTML = `<div class="empty-state">Nessun ordine trovato per l'anno selezionato.</div>`;
    totalsTarget.innerHTML = `<div class="empty-state">Nessun totale disponibile.</div>`;
    return;
  }

  dailyTarget.innerHTML = filteredBatches
    .map((batch) => {
      return `
        <section class="history-group">
          <div class="modal-head">
            <div>
              <p class="eyebrow">Ordine</p>
              <h4>${escapeHtml(formatDate(batch.confirmed_at))}</h4>
              ${
                batch.total_estimated_amount !== null && batch.total_estimated_amount !== undefined
                  ? `<p class="muted-copy">Totale salvato: ${escapeHtml(formatCurrency(batch.total_estimated_amount))}</p>`
                  : ""
              }
            </div>
            <div class="history-actions">
              <button type="button" class="history-action" data-show-batch="${batch.id}">Recupera</button>
              <button type="button" class="history-action is-danger" data-edit-batch="${batch.id}">Modifica / Storna</button>
            </div>
          </div>
          <ul class="summary-list">
            ${batch.items
              .map(
                (item) =>
                  `<li>${escapeHtml(item.product_name)} x ${item.quantity} (${escapeHtml(item.lot_code)}) [${escapeHtml(item.supplier_name)}]${
                    item.estimated_line_total !== null && item.estimated_line_total !== undefined
                      ? ` · ${escapeHtml(formatCurrency(item.estimated_line_total))}`
                      : ""
                  }</li>`
              )
              .join("")}
          </ul>
        </section>
      `;
    })
    .join("");

  dailyTarget.querySelectorAll("[data-show-batch]").forEach((button) => {
    button.addEventListener("click", () => openBatchSummary(Number(button.dataset.showBatch)));
  });
  dailyTarget.querySelectorAll("[data-edit-batch]").forEach((button) => {
    button.addEventListener("click", () => void restoreBatchToDraft(Number(button.dataset.editBatch)));
  });

  const totalsMap = new Map();
  filteredBatches.forEach((batch) => {
    batch.items.forEach((item) => {
      if (searchTokens.length && !matchesSearchText(buildHistoryBatchItemSearchText(batch, item), searchTokens)) {
        return;
      }
      const key = `${item.product_name}__${item.lot_code}__${item.supplier_name}`;
      const existing = totalsMap.get(key) || { ...item, quantity: 0, estimated_line_total: 0 };
      existing.quantity += item.quantity;
      if (item.estimated_line_total !== null && item.estimated_line_total !== undefined) {
        existing.estimated_line_total = Number(existing.estimated_line_total || 0) + Number(item.estimated_line_total || 0);
      } else if (existing.estimated_line_total === 0) {
        existing.estimated_line_total = null;
      }
      totalsMap.set(key, existing);
    });
  });

  if (!totalsMap.size) {
    totalsTarget.innerHTML = `<div class="empty-state">Nessun totale disponibile.</div>`;
    return;
  }

  totalsTarget.innerHTML = `
    <section class="history-group">
      <ul class="summary-list">
        ${[...totalsMap.values()]
          .sort((left, right) => right.quantity - left.quantity || left.product_name.localeCompare(right.product_name))
          .map(
            (item) =>
              `<li>${escapeHtml(item.product_name)} x ${item.quantity} (${escapeHtml(item.lot_code)}) [${escapeHtml(item.supplier_name)}]${
                item.estimated_line_total !== null && item.estimated_line_total !== undefined
                  ? ` · ${escapeHtml(formatCurrency(item.estimated_line_total))}`
                  : ""
              }</li>`
          )
          .join("")}
      </ul>
    </section>
  `;
}

function buildHistoryItemSearchText(item) {
  return [
    item.product_name,
    item.supplier_name,
    item.lot_code,
    item.product_code || "",
    item.category || "",
    item.notes || ""
  ].join(" ");
}

function buildHistoryBatchSearchText(batch) {
  return [formatDate(batch.confirmed_at), ...batch.items.map((item) => buildHistoryItemSearchText(item))].join(" ");
}

function buildHistoryBatchItemSearchText(batch, item) {
  return [formatDate(batch.confirmed_at), buildHistoryItemSearchText(item)].join(" ");
}

function restoreRemovedItemsToDraft(removedItems = []) {
  resetOrderDraftState();

  let restoredLines = 0;
  removedItems.forEach((item) => {
    const quantity = Number.parseInt(String(item?.quantity || ""), 10) || 0;
    if (quantity <= 0) {
      return;
    }

    const normalizedLot = normalizeLotCode(item?.lot_code || "");
    const productId = Number(item?.product_id);
    const directProduct = Number.isFinite(productId) ? products.find((product) => Number(product.id) === productId) : null;

    if (directProduct) {
      const existingQuantity = Number.parseInt(String(currentQuantities[String(directProduct.id)] || ""), 10) || 0;
      currentQuantities[String(directProduct.id)] = existingQuantity + quantity;
      const entryKey = buildCatalogEntryKey(directProduct.product_name, directProduct.supplier_name);
      if (normalizedLot) {
        selectedLotsByEntry[entryKey] = normalizedLot;
      }
      restoredLines += 1;
      return;
    }

    const entryKey = buildCatalogEntryKey(item?.product_name || "", item?.supplier_name || "");
    const entry = catalogEntries.find((candidate) => candidate.key === entryKey);
    if (!entry) {
      return;
    }

    const fallbackLot = normalizedLot || normalizeLotCode(entry.representative?.lot_code || "");
    const variant = findVariantForLot(entry, fallbackLot);
    const storageKey = variant ? String(variant.id) : buildDraftQuantityKey(entry.key, fallbackLot);
    const existingQuantity = Number.parseInt(String(currentQuantities[storageKey] || ""), 10) || 0;
    currentQuantities[storageKey] = existingQuantity + quantity;

    if (fallbackLot) {
      selectedLotsByEntry[entry.key] = fallbackLot;
      const unitsPerPack = parseOptionalNumber(String(item?.units_per_pack ?? ""));
      if (isMultipackLotCode(fallbackLot) && unitsPerPack !== null && unitsPerPack > 0) {
        setDraftLotState(entry.key, fallbackLot, { units_per_pack: unitsPerPack });
      }
    }

    restoredLines += 1;
  });
  persistQuantities();
  persistLotSelections();
  persistDraftMetadata();
  return restoredLines;
}

function openBatchSummary(batchId) {
  const batch = historyBatches.find((entry) => entry.id === batchId);
  if (!batch) {
    window.alert("Ordine non trovato.");
    return;
  }
  renderGroupedSummary(batch.items, `Ordine del ${formatDate(batch.confirmed_at)}`, "summaryContent", true);
  summaryReturnModalId = "historyModal";
  showModal("summaryModal");
}

async function restoreBatchToDraft(batchId) {
  const batch = historyBatches.find((entry) => entry.id === batchId);
  if (!batch) {
    window.alert("Ordine non trovato.");
    return;
  }

  if (!window.confirm(`Vuoi riportare in modifica l'ordine del ${formatDate(batch.confirmed_at)}?`)) {
    return;
  }

  try {
    const restoredLines = restoreRemovedItemsToDraft(batch.items || []);
    setHistoryEditContext({
      batchId: batch.id,
      confirmedAt: batch.confirmed_at,
      staff: batch.staff || currentStaff
    });
    renderProductsTable();
    closeModal("historyModal");
    window.scrollTo({ top: 0, behavior: "smooth" });
    if (!restoredLines) {
      showFlash("Ordine storico aperto in modifica, ma nessuna riga e' stata caricata nella bozza.");
      return;
    }
    showFlash("Ordine storico caricato in modifica. Confermando aggiorni la data originale.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Recupero ordine non riuscito.");
  }
}

async function openComparison() {
  try {
    const payload = await apiFetch(`/api/ordini/confronto?year=${encodeURIComponent(selectedYear)}`);
    document.getElementById("comparisonDescription").textContent = `Confronto tra ${payload.previous_year} e ${payload.year}.`;
    document.getElementById("comparisonContent").innerHTML = renderComparisonHtml(payload);
    showModal("comparisonModal");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Confronto ordini non disponibile.");
  }
}

function renderComparisonHtml(payload) {
  const delta = payload.current_total - payload.previous_total;
  const deltaPercent = payload.previous_total
    ? `${delta >= 0 ? "+" : ""}${((delta / payload.previous_total) * 100).toFixed(1)}%`
    : payload.current_total
      ? "n/d"
      : "0%";

  return `
    <section class="comparison-grid">
      <article class="comparison-tile">
        <span>${payload.previous_year}</span>
        <strong>${payload.previous_total}</strong>
        <em>Totale articoli</em>
      </article>
      <article class="comparison-tile">
        <span>${payload.year}</span>
        <strong>${payload.current_total}</strong>
        <em>Totale articoli</em>
      </article>
      <article class="comparison-tile">
        <span>Delta</span>
        <strong class="${delta >= 0 ? "delta-positive" : "delta-negative"}">${delta >= 0 ? "+" : ""}${delta}</strong>
        <em class="${delta >= 0 ? "delta-positive" : "delta-negative"}">${deltaPercent}</em>
      </article>
    </section>

    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th>Prodotto</th>
            <th>${payload.previous_year}</th>
            <th>${payload.year}</th>
            <th>Delta</th>
            <th>%</th>
          </tr>
        </thead>
        <tbody>
          ${
            payload.items.length
              ? payload.items
                  .map((item) => {
                    const percent = item.delta_percent === null ? (item.current_quantity ? "n/d" : "0%") : `${item.delta_percent >= 0 ? "+" : ""}${item.delta_percent.toFixed(1)}%`;
                    return `
                      <tr>
                        <td>${escapeHtml(item.product_name)}</td>
                        <td>${item.previous_quantity}</td>
                        <td>${item.current_quantity}</td>
                        <td class="${item.delta >= 0 ? "delta-positive" : "delta-negative"}">${item.delta >= 0 ? "+" : ""}${item.delta}</td>
                        <td>${percent}</td>
                      </tr>
                    `;
                  })
                  .join("")
              : `<tr><td colspan="5" class="empty-state">Nessun confronto disponibile.</td></tr>`
          }
        </tbody>
      </table>
    </div>
  `;
}

async function openLatestOrder() {
  try {
    const payload = await apiFetch("/api/ordini/ultimo");
    if (!payload?.batch) {
      window.alert("Nessun ordine recente disponibile.");
      return;
    }
    summaryReturnModalId = null;
    renderGroupedSummary(payload.batch.items, `Ultimo ordine del ${formatDate(payload.batch.confirmed_at)}`, "summaryContent", true);
    showModal("summaryModal");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Recupero ultimo ordine non riuscito.");
  }
}

function exportHistory() {
  if (!authToken) {
    return;
  }
  window.open(
    resolveModuleUrl(`storico_ordini.csv?year=${encodeURIComponent(selectedYear)}&token=${encodeURIComponent(authToken)}`),
    "_blank",
    "noopener"
  );
}

async function openGoals() {
  try {
    const payload = await apiFetch(`/api/obiettivi?year=${encodeURIComponent(selectedGoalYear)}`);
    goalYears = Array.isArray(payload.available) && payload.available.length ? payload.available : [selectedGoalYear];
    selectedGoalYear = payload.year || selectedGoalYear;
    goalsCache = Array.isArray(payload.goals) ? payload.goals : [];
    updateGoalYearLabel();
    renderYearSwitcher("goalYears", goalYears, selectedGoalYear, (year) => {
      selectedGoalYear = year;
      editingGoalId = null;
      updateGoalYearLabel();
      void openGoals();
    });
    renderGoals(goalsCache);
    showModal("goalsModal");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Obiettivi non disponibili.");
  }
}

function renderGoals(goals) {
  const target = document.getElementById("goalsContent");
  if (!goals.length) {
    target.innerHTML = `<div class="empty-state">Nessun obiettivo configurato per l'anno selezionato.</div>`;
    return;
  }

  const sortedGoals = goals
    .slice()
    .sort((left, right) => {
      const leftRank = left?.type === "note" ? 1 : 0;
      const rightRank = right?.type === "note" ? 1 : 0;
      return leftRank - rightRank;
    });

  target.innerHTML = sortedGoals
    .map((goal) => {
      const isEditing = Number(goal.id) === Number(editingGoalId);
      if (goal.type === "note") {
        return `
          <section class="goal-card ${isEditing ? "is-editing" : ""}">
            <p class="eyebrow">Nota</p>
            <h4>${escapeHtml(goal.name)}</h4>
            <p class="muted">${escapeHtml(goal.description || "")}</p>
            <div class="goal-card-actions">
              <button type="button" class="button-secondary" data-edit-goal="${goal.id}">Modifica</button>
            </div>
            ${renderGoalEditForm(goal, isEditing)}
          </section>
        `;
      }

      if (goal.type === "liters_dual") {
        const primaryPercent = goal.target_grey ? Math.min(100, (goal.progress_grey / goal.target_grey) * 100) : 0;
        const secondaryPercent = goal.target_patron ? Math.min(100, (goal.progress_patron / goal.target_patron) * 100) : 0;
        return `
          <section class="goal-card ${isEditing ? "is-editing" : ""}">
            <p class="eyebrow">Doppio target</p>
            <h4>${escapeHtml(goal.name)}</h4>
            <p class="muted">${escapeHtml(goal.description || "")}</p>
            <p>Grey: ${goal.progress_grey} / ${goal.target_grey} L</p>
            <div class="goal-progress"><span style="width:${primaryPercent}%"></span></div>
            <p>Patron: ${goal.progress_patron} / ${goal.target_patron} L</p>
            <div class="goal-progress"><span style="width:${secondaryPercent}%"></span></div>
            ${goal.bonus ? `<p class="muted">Bonus: ${escapeHtml(goal.bonus)}</p>` : ""}
            <div class="goal-card-actions">
              <button type="button" class="button-secondary" data-edit-goal="${goal.id}">Modifica</button>
            </div>
            ${renderGoalEditForm(goal, isEditing)}
          </section>
        `;
      }

      const targetValue = Number(goal.target || 0);
      const progressValue = Number(goal.progress || 0);
      const percent = targetValue ? Math.min(100, (progressValue / targetValue) * 100) : 0;
      return `
        <section class="goal-card ${isEditing ? "is-editing" : ""}">
          <p class="eyebrow">${goal.type === "liters" ? "Litri" : "Quantità"}</p>
          <h4>${escapeHtml(goal.name)}</h4>
          <p class="muted">${escapeHtml(goal.description || "")}</p>
          <p>${progressValue} / ${targetValue} ${escapeHtml(goal.unit || "")}</p>
          <div class="goal-progress"><span style="width:${percent}%"></span></div>
          ${goal.bonus ? `<p class="muted">Bonus: ${escapeHtml(goal.bonus)}</p>` : ""}
          <div class="goal-card-actions">
            <button type="button" class="button-secondary" data-edit-goal="${goal.id}">Modifica</button>
          </div>
          ${renderGoalEditForm(goal, isEditing)}
        </section>
      `;
    })
    .join("");

  target.querySelectorAll("[data-edit-goal]").forEach((button) => {
    button.addEventListener("click", () => {
      editingGoalId = Number(button.dataset.editGoal);
      renderGoals(goalsCache);
    });
  });
  target.querySelectorAll("[data-cancel-goal-edit]").forEach((button) => {
    button.addEventListener("click", () => {
      editingGoalId = null;
      renderGoals(goalsCache);
    });
  });
  target.querySelectorAll("[data-goal-form]").forEach((form) => {
    form.addEventListener("submit", (event) => {
      event.preventDefault();
      void saveGoalEdit(Number(form.dataset.goalForm), form);
    });
    const typeSelect = form.querySelector("[name='goal_type']");
    if (typeSelect) {
      typeSelect.addEventListener("change", () => updateGoalEditFormVisibility(form));
    }
    updateGoalEditFormVisibility(form);
  });
}

function renderGoalEditForm(goal, isEditing) {
  return `
    <form class="goal-edit-form" data-goal-form="${goal.id}" ${isEditing ? "" : "hidden"}>
      <div class="goal-edit-grid">
        <label class="field">
          <span>Nome</span>
          <input type="text" name="name" value="${escapeAttribute(goal.name || "")}" />
        </label>
        <label class="field">
          <span>Tipo</span>
          <select name="goal_type">
            ${renderGoalTypeOptions(goal.type)}
          </select>
        </label>
        <label class="field" data-goal-scope-field>
          <span>Prodotto</span>
          <input type="text" name="product_match" value="${escapeAttribute(goal.product_match || "")}" />
        </label>
        <label class="field" data-goal-scope-field>
          <span>Fornitore</span>
          <input type="text" name="supplier_match" value="${escapeAttribute(goal.supplier_match || "")}" />
        </label>
        <label class="field" data-goal-numeric-field>
          <span>Target</span>
          <input type="number" step="0.01" name="target" value="${escapeAttribute(goal.target ?? "")}" />
        </label>
        <label class="field" data-goal-numeric-field>
          <span>Unità</span>
          <input type="text" name="unit_label" value="${escapeAttribute(goal.unit || "")}" />
        </label>
        <label class="field" data-goal-dual-field>
          <span>Secondo prodotto</span>
          <input type="text" name="secondary_product_match" value="${escapeAttribute(goal.secondary_product_match || "")}" />
        </label>
        <label class="field" data-goal-dual-field>
          <span>Secondo target</span>
          <input type="number" step="0.01" name="secondary_target" value="${escapeAttribute(goal.secondary_target ?? "")}" />
        </label>
        <label class="field" data-goal-bonus-field>
          <span>Bonus</span>
          <input type="text" name="bonus_label" value="${escapeAttribute(goal.bonus || "")}" />
        </label>
        <label class="field field-wide">
          <span>Descrizione</span>
          <textarea name="description" rows="3">${escapeHtml(goal.description || "")}</textarea>
        </label>
      </div>
      <div class="goal-edit-actions">
        <button type="submit" class="button-primary">Salva</button>
        <button type="button" class="button-secondary" data-cancel-goal-edit>Annulla</button>
      </div>
    </form>
  `;
}

function renderGoalTypeOptions(selectedType) {
  return [
    ["quantity", "Quantità"],
    ["liters", "Litri"],
    ["liters_dual", "Doppio target"],
    ["note", "Nota"]
  ]
    .map(([value, label]) => `<option value="${value}" ${value === selectedType ? "selected" : ""}>${label}</option>`)
    .join("");
}

function updateGoalEditFormVisibility(form) {
  const type = form.querySelector("[name='goal_type']")?.value || "quantity";
  const isNote = type === "note";
  const isDual = type === "liters_dual";

  form.querySelectorAll("[data-goal-scope-field]").forEach((field) => {
    field.hidden = isNote;
  });
  form.querySelectorAll("[data-goal-numeric-field]").forEach((field) => {
    field.hidden = isNote;
  });
  form.querySelectorAll("[data-goal-dual-field]").forEach((field) => {
    field.hidden = !isDual;
  });
  form.querySelectorAll("[data-goal-bonus-field]").forEach((field) => {
    field.hidden = isNote;
  });

  const unitInput = form.querySelector("[name='unit_label']");
  if (unitInput && !String(unitInput.value || "").trim()) {
    unitInput.value = type === "liters" || type === "liters_dual" ? "L" : "articoli";
  }
}

async function saveGoalEdit(goalId, form) {
  const formData = new FormData(form);
  const payload = {
    year: selectedGoalYear,
    name: String(formData.get("name") || "").trim(),
    goal_type: String(formData.get("goal_type") || "").trim(),
    description: normalizeOptionalText(formData.get("description")),
    product_match: normalizeOptionalText(formData.get("product_match")),
    secondary_product_match: normalizeOptionalText(formData.get("secondary_product_match")),
    supplier_match: normalizeOptionalText(formData.get("supplier_match")),
    target: parseOptionalDecimal(formData.get("target")),
    secondary_target: parseOptionalDecimal(formData.get("secondary_target")),
    unit_label: normalizeOptionalText(formData.get("unit_label")),
    bonus_label: normalizeOptionalText(formData.get("bonus_label"))
  };

  try {
    await apiFetch(`/api/obiettivi/${goalId}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(payload)
    });
    editingGoalId = null;
    await openGoals();
    showFlash("Obiettivo aggiornato.");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Aggiornamento obiettivo non riuscito.");
  }
}

async function loadNotes() {
  sharedNotes = await apiFetch("/api/note");
  renderNotes();
}

async function openNotes() {
  try {
    await loadNotes();
    showModal("notesModal");
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Note non disponibili.");
  }
}

function renderNotes() {
  renderSharedNotesBadge();
  const target = document.getElementById("notesList");
  if (!sharedNotes.length) {
    target.innerHTML = `<div class="note-empty">Nessuna nota presente.</div>`;
    return;
  }

  target.innerHTML = sharedNotes
    .slice()
    .reverse()
    .map((note) => {
      return `
        <section class="note-card">
          <div class="note-meta">
            <span>${escapeHtml((note.author || DEFAULT_STAFF).toUpperCase())}</span>
            <span>${escapeHtml(formatDate(note.timestamp))}</span>
          </div>
          <p>${escapeHtml(note.text)}</p>
          <button type="button" class="note-delete" data-delete-note="${note.id}">Elimina</button>
        </section>
      `;
    })
    .join("");

  target.querySelectorAll("[data-delete-note]").forEach((button) => {
    button.addEventListener("click", () => void deleteNote(Number(button.dataset.deleteNote)));
  });
}

function renderSharedNotesBadge() {
  if (!sharedNotesBadge) {
    return;
  }
  const count = Array.isArray(sharedNotes) ? sharedNotes.length : 0;
  sharedNotesBadge.hidden = count <= 0;
  sharedNotesBadge.textContent = count > 99 ? "99+" : String(count);
  if (mobileNavNotesBadge) {
    mobileNavNotesBadge.hidden = count <= 0;
    mobileNavNotesBadge.textContent = count > 99 ? "99+" : String(count);
  }
  const button = document.getElementById("sharedNotesButton");
  if (button) {
    button.setAttribute(
      "aria-label",
      count > 0 ? `Note condivise, ${count} note presenti` : "Note condivise"
    );
  }
  if (mobileNavToggle) {
    mobileNavToggle.setAttribute(
      "aria-label",
      count > 0 ? `Apri menu, ${count} note condivise presenti` : "Apri menu"
    );
  }
}

async function createNote() {
  try {
    const text = noteInput.value.trim();
    if (!text) {
      window.alert("Scrivi una nota prima di inviarla.");
      return;
    }

    const payload = await apiFetch("/api/note", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ text })
    });
    noteInput.value = "";
    sharedNotes.push(payload.note);
    renderNotes();
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Salvataggio nota non riuscito.");
  }
}

async function deleteNote(noteId) {
  if (!window.confirm("Eliminare questa nota?")) {
    return;
  }
  try {
    await apiFetch(`/api/note/${noteId}`, { method: "DELETE" });
    sharedNotes = sharedNotes.filter((note) => note.id !== noteId);
    renderNotes();
  } catch (error) {
    window.alert(error instanceof Error ? error.message : "Eliminazione nota non riuscita.");
  }
}

function toggleMenu(menuId) {
  const targetMenu = document.getElementById(menuId);
  const shouldOpen = Boolean(targetMenu && !targetMenu.classList.contains("is-open"));
  document.querySelectorAll(".submenu").forEach((menu) => {
    menu.classList.toggle("is-open", shouldOpen && menu.id === menuId);
  });
  document.querySelectorAll("[data-toggle-menu]").forEach((button) => {
    button.setAttribute("aria-expanded", shouldOpen && button.dataset.toggleMenu === menuId ? "true" : "false");
  });
}

function closeMenus() {
  document.querySelectorAll(".submenu").forEach((menu) => {
    menu.classList.remove("is-open");
  });
  document.querySelectorAll("[data-toggle-menu]").forEach((button) => {
    button.setAttribute("aria-expanded", "false");
  });
}

function updateHistoryYearLabel() {
  const target = document.getElementById("historyYearLabel");
  if (target) {
    target.textContent = String(selectedYear);
  }
}

function updateSelectedYearUi() {
  const yearText = String(selectedYear);
  if (activeYearLabel) {
    activeYearLabel.textContent = yearText;
  }
  if (activeYearHeadline) {
    activeYearHeadline.textContent = yearText;
  }
  updateHistoryYearLabel();
}

function renderHistoryYearSwitchers() {
  const handleSelect = (year) => {
    selectedYear = year;
    updateSelectedYearUi();
    void loadHistory(selectedYear);
  };

  renderYearSwitcher("overviewYears", availableYears, selectedYear, handleSelect);
  renderYearSwitcher("historyYears", availableYears, selectedYear, handleSelect);
}

function updateGoalYearLabel() {
  const target = document.getElementById("goalYearLabel");
  if (target) {
    target.textContent = String(selectedGoalYear);
  }
}

function renderYearSwitcher(targetId, years, selected, onSelect) {
  const target = document.getElementById(targetId);
  if (!target) {
    return;
  }
  target.innerHTML = years
    .map((year) => `<button type="button" class="${year === selected ? "is-active" : ""}" data-year="${year}">${year}</button>`)
    .join("");
  target.querySelectorAll("[data-year]").forEach((button) => {
    button.addEventListener("click", () => onSelect(Number(button.dataset.year)));
  });
}

function formatDate(value) {
  if (!value) {
    return "";
  }
  try {
    return new Date(value).toLocaleString("it-IT", { dateStyle: "short", timeStyle: "short" });
  } catch {
    return value;
  }
}

function formatCurrency(value) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) {
    return "";
  }
  return EURO_FORMATTER.format(numeric);
}

function normalizeOptionalText(value) {
  const text = String(value ?? "").trim();
  return text || null;
}

function parseOptionalDecimal(value) {
  const text = String(value ?? "").trim();
  if (!text) {
    return null;
  }
  const normalized = text.includes(",") && text.includes(".")
    ? text.replaceAll(".", "").replace(",", ".")
    : text.replace(",", ".");
  const numeric = Number(normalized);
  return Number.isFinite(numeric) ? numeric : null;
}

function detectCsvDelimiter(csvText) {
  const firstMeaningfulLine = String(csvText || "")
    .split(/\r?\n/)
    .find((line) => line.trim());
  if (!firstMeaningfulLine) {
    return ",";
  }

  const candidates = [",", ";", "\t"];
  let selected = ",";
  let selectedCount = -1;
  candidates.forEach((candidate) => {
    const count = firstMeaningfulLine.split(candidate).length - 1;
    if (count > selectedCount) {
      selected = candidate;
      selectedCount = count;
    }
  });
  return selected;
}

function parseCsvRows(csvText) {
  const delimiter = detectCsvDelimiter(csvText);
  const rows = [];
  let row = [];
  let cell = "";
  let inQuotes = false;

  for (let index = 0; index < csvText.length; index += 1) {
    const char = csvText[index];

    if (inQuotes) {
      if (char === '"') {
        if (csvText[index + 1] === '"') {
          cell += '"';
          index += 1;
        } else {
          inQuotes = false;
        }
      } else {
        cell += char;
      }
      continue;
    }

    if (char === '"') {
      inQuotes = true;
      continue;
    }
    if (char === delimiter) {
      row.push(cell);
      cell = "";
      continue;
    }
    if (char === "\r") {
      continue;
    }
    if (char === "\n") {
      row.push(cell);
      rows.push(row);
      row = [];
      cell = "";
      continue;
    }
    cell += char;
  }

  if (cell.length || row.length) {
    row.push(cell);
    rows.push(row);
  }

  return rows.filter((cells) => cells.some((value) => String(value || "").trim()));
}

function normalizeCsvHeader(value) {
  return String(value || "")
    .trim()
    .toLowerCase()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "");
}

async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    showFlash("Testo copiato.");
  } catch {
    window.alert("Copia non riuscita.");
  }
}

function showModal(id) {
  closeMenus();
  if (id !== "summaryModal") {
    summaryReturnModalId = document.querySelector(".modal:not([hidden])")?.id || null;
  }
  document.querySelectorAll(".modal").forEach((modal) => {
    modal.hidden = modal.id !== id;
  });
  document.body.style.overflow = "hidden";
}

function closeModal(id) {
  const modal = document.getElementById(id);
  if (!modal) {
    return;
  }
  modal.hidden = true;

  if (id === "summaryModal" && summaryReturnModalId) {
    const returnModal = summaryReturnModalId;
    summaryReturnModalId = null;
    showModal(returnModal);
    return;
  }

  if (id === "summaryModal") {
    summaryReturnModalId = null;
  }

  if (![...document.querySelectorAll(".modal")].some((item) => !item.hidden)) {
    document.body.style.overflow = "";
  }
}

function escapeHtml(value) {
  return String(value || "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

function escapeAttribute(value) {
  return escapeHtml(value).replaceAll("\n", "&#10;");
}
