// @ts-nocheck
const fieldGrid = document.querySelector("#fieldGrid");
const variantHeader = document.querySelector("#variantHeader");
const manufacturerSelect = document.querySelector("#manufacturerSelect");
const manufacturerTrigger = document.querySelector("#manufacturerTrigger");
const manufacturerMenu = document.querySelector("#manufacturerMenu");
const manufacturerSearch = document.querySelector("#manufacturerSearch");
const manufacturerOptions = document.querySelector("#manufacturerOptions");
const manufacturerValue = document.querySelector("#manufacturerValue");
const languageSelect = document.querySelector("#languageSelect");
const languageValue = document.querySelector("#languageValue");
const recordIdElement = document.querySelector("#recordId");
const loadProductButton = document.querySelector("#loadProductButton");
const previousProductButton = document.querySelector("#previousProductButton");
const nextProductButton = document.querySelector("#nextProductButton");
const tableBody = document.querySelector("#variantRows");
const variantTable = document.querySelector(".variant-grid");
const pictureSlot = document.querySelector("#pictureSlot");
const scanInput = document.querySelector("#scanInput");
const suggestedModal = document.querySelector("#suggestedModal");
const suggestedTitle = document.querySelector("#suggestedTitle");
const suggestedList = document.querySelector("#suggestedList");
const suggestedCloseButton = document.querySelector("#suggestedCloseButton");
const mappingState = document.querySelector("#mappingState");
const mappingStatusText = document.querySelector("#mappingStatusText");
const mappingList = document.querySelector("#mappingList");
const mappingDetails = document.querySelector("#mappingDetails");
const findSourcesButton = document.querySelector("#findSourcesButton");
const getPicturesButton = document.querySelector("#getPicturesButton");
const sidebarDrawer = document.querySelector("#sidebarDrawer");
const sidebarOpenButton = document.querySelector("#sidebarOpenButton");
const sidebarCloseButton = document.querySelector("#sidebarCloseButton");
const sidebarOverlay = document.querySelector("#sidebarOverlay");
const sourceModal = document.querySelector("#sourceModal");
const sourceTitle = document.querySelector("#sourceTitle");
const sourceList = document.querySelector("#sourceList");
const sourceBatchActionButton = document.querySelector("#sourceBatchActionButton");
const sourceReloadButton = document.querySelector("#sourceReloadButton");
const sourceCloseButton = document.querySelector("#sourceCloseButton");
const settingsButton = document.querySelector("#settingsButton");
const themeToggle = document.querySelector("#themeToggle");
const settingsModal = document.querySelector("#settingsModal");
const settingsCloseButton = document.querySelector("#settingsCloseButton");
const databaseModeNote = document.querySelector("#databaseModeNote");
const databaseModeInputs = document.querySelectorAll('input[name="databaseMode"]');
const permissionInputs = document.querySelectorAll("#allowInsertToggle, #allowUpdateToggle, #allowDeleteToggle");
const transactionModeInputs = document.querySelectorAll('input[name="transactionMode"]');
const databaseStatus = document.querySelector("#databaseStatus");
const databaseStateDot = document.querySelector("#databaseStateDot");
const localDbFields = document.querySelector("#localDbFields");
const localDbInputs = document.querySelectorAll("#localDbFields input");
const testLocalDbButton = document.querySelector("#testLocalDbButton");
const localDbTestResult = document.querySelector("#localDbTestResult");
const openAdminerButton = document.querySelector("#openAdminerButton");
const openDbGateButton = document.querySelector("#openDbGateButton");
const adminerServerValue = document.querySelector("#adminerServerValue");
const adminerUserValue = document.querySelector("#adminerUserValue");
const adminerDatabaseValue = document.querySelector("#adminerDatabaseValue");
const lightContrastSelect = document.querySelector("#lightContrastSelect");
const darkContrastSelect = document.querySelector("#darkContrastSelect");
const catalogScreen = document.querySelector(".catalog-screen");
const secondaryPanelToggle = document.querySelector("#secondaryPanelToggle");
const mainWorkspaceToggle = document.querySelector("#mainWorkspaceToggle");
const MIN_PRODUCT_PICTURE_SIZE = 800;
let sidebarUserClosed = false;
let mappingLoadToken = 0;
const mappingCacheKey = "catalog-maker:mapping-cache";
const legacyPermissionStorageKey = "catalog-maker:db-permissions";
const permissionStorageKeys = {
  local: "catalog-maker:db-permissions:local-9bplus",
  live: "catalog-maker:db-permissions:live-9bplus",
  scrape: "catalog-maker:db-permissions:local-scrape",
};

const nativeFetch = window.fetch.bind(window);
window.fetch = (input, init = {}) => {
  const url = typeof input === "string" ? input : input.url;
  if (!url.startsWith("/api/")) return nativeFetch(input, init);

  const headers = new Headers(init.headers || (typeof input !== "string" ? input.headers : undefined));
  const selectedMode = localStorage.getItem("catalog-maker:database-mode") || "local";
  const permissions = readDatabasePermissions(selectedMode);
  const localWritesEnabled = ["local", "scrape"].includes(selectedMode) && [
    permissions.allowInsertToggle,
    permissions.allowUpdateToggle,
    permissions.allowDeleteToggle,
  ].some(Boolean);
  // Scrape storage is separate; existing catalog endpoints continue reading Local 9bplus.
  headers.set("X-Database-Mode", selectedMode === "live" ? "live" : "local");
  headers.set("X-Scrape-Database", selectedMode === "scrape" ? "1" : "0");
  headers.set("X-Local-Write-Access", localWritesEnabled ? "1" : "0");
  return nativeFetch(input, { ...init, headers });
};

let currentPosition = 1;
let totalPositions = 0;
let currentProduct = null;
let currentCombinations = [];
let loadRequestId = 0;
let applicationMode = "dry-run";
let databaseConnectionState = "unknown";
const localDraftKey = "catalog-maker:current-product-draft:v3";
const selectedManufacturerKey = "catalog-maker:selected-manufacturer";
let localDraftProductKey = "";

const fieldNames = [
  "id_manufacturer",
  "code_manufacture",
  "voc_discount",
  "max_moc_discount",
  "supplier_reference",
  "catalog_valid_until",
  "name",
  "price - moc",
  "combinations",
  "id_attribute_group",
  "id_supplier",
  "id_category_default",
  "9b_category_name",
  "manufacturer_name",
  "catalog category",
  "gender",
  "position/total",
  "Photos loaded",
  "Reference separator",
];

const variantColumns = [
  { key: "status", label: "status", width: 92 },
  { key: "color", label: "color", width: 156 },
  { key: "combi", label: "combi", width: 60, align: "center" },
  { key: "refer", label: "refer", width: 64 },
  { key: "ean13", label: "ean13", width: 148, align: "center", problem: true },
  { key: "nineB", label: "9b", width: 58, align: "center" },
  { key: "supplierStock", label: "sup", width: 58, align: "center" },
  { key: "id", label: "id", width: 74, align: "center" },
  { key: "voc", label: "voc", width: 74, align: "center", amount: true },
  { key: "moc", label: "moc", width: 74, align: "center", amount: true },
  { key: "productAttribute", label: "pr.at", width: 70, align: "center" },
  { key: "catalogColor", label: "color", width: 110 },
  { key: "catalogCombi", label: "combi", width: 70, align: "center" },
  { key: "nineBVoc", label: "9b.voc", width: 74, align: "center", amount: true },
  { key: "nineBMoc", label: "9b.moc", width: 74, align: "center", amount: true },
  { key: "eanStatus", label: "", width: 84, align: "center" },
];

const productFieldMap = {
  id_manufacturer: "idManufacturer",
  code_manufacture: "codeManufacture",
  voc_discount: "vocDiscount",
  max_moc_discount: "maxMocDiscount",
  supplier_reference: "supplierReference",
  catalog_valid_until: "catalogValidUntil",
  name: "name",
  "price - moc": "priceMoc",
  combinations: "combinations",
  id_attribute_group: "idAttributeGroup",
  id_supplier: "idSupplier",
  id_category_default: "idCategoryDefault",
  "9b_category_name": "category9bName",
  manufacturer_name: "manufacturerName",
  "catalog category": "catalogCategory",
  gender: "gender",
  "position/total": "positionTotal",
  "Photos loaded": "photosLoaded",
  "Reference separator": "referenceSeparator",
};

await loadStatus();
loadTheme();
loadAppearance();
renderFields();
renderTableHeader();
setTableWidth();
await loadManufacturers();
await loadLanguages();
languageSelect.addEventListener("change", syncLanguagePickerLabel);

manufacturerSelect.addEventListener("change", () => {
  if (manufacturerSelect.value) {
    localStorage.setItem(selectedManufacturerKey, manufacturerSelect.value);
  } else {
    localStorage.removeItem(selectedManufacturerKey);
  }
  syncManufacturerPickerLabel();
  closeManufacturerPicker();
  if (manufacturerSelect.value) {
    loadMappingSources({ checkUrls: false, preferCache: false });
    loadProductForSelectedManufacturer(1);
  } else {
    renderMappingSources({ mapped: false, items: [], message: "Select manufacturer." });
    clearProductState("Select manufacturer to load data.");
  }
});

manufacturerTrigger.addEventListener("click", toggleManufacturerPicker);
manufacturerSearch.addEventListener("input", renderManufacturerOptions);
manufacturerSearch.addEventListener("keydown", (event) => {
  if (event.key === "Escape") closeManufacturerPicker();
});
document.addEventListener("click", (event) => {
  if (!event.target.closest("#manufacturerPicker")) closeManufacturerPicker();
});

loadProductButton.disabled = true;
loadProductButton.title = "Load existing product attribute info.";
loadProductButton.addEventListener("click", loadProductInfoForCurrentProduct);
findSourcesButton.addEventListener("click", findSourcesForCurrentProduct);
sourceReloadButton.addEventListener("click", () => findSourcesForCurrentProduct(true));
sourceBatchActionButton.addEventListener("click", handleSourceBatchAction);
getPicturesButton.addEventListener("click", getPicturesForCurrentProduct);
secondaryPanelToggle.addEventListener("click", toggleSecondaryPanel);
mainWorkspaceToggle.addEventListener("click", toggleMainWorkspace);
sidebarOpenButton.addEventListener("click", openSidebar);
sidebarCloseButton.addEventListener("click", closeSidebar);
sidebarOverlay.addEventListener("click", closeSidebar);
document.addEventListener("keydown", (event) => {
  if (event.key === "Escape") closeSidebar();
});

previousProductButton.addEventListener("click", () => {
  if (!manufacturerSelect.value || currentPosition <= 1) return;
  loadProductForSelectedManufacturer(currentPosition - 1);
});

nextProductButton.addEventListener("click", () => {
  if (!manufacturerSelect.value || (totalPositions && currentPosition >= totalPositions)) return;
  loadProductForSelectedManufacturer(currentPosition + 1);
});

scanInput.addEventListener("keydown", (event) => {
  if (event.key !== "Enter") return;
  event.preventDefault();
  loadProductByScannedEan();
});

suggestedCloseButton.addEventListener("click", closeSuggestedProducts);
suggestedModal.addEventListener("click", (event) => {
  if (event.target === suggestedModal) closeSuggestedProducts();
});
sourceCloseButton.addEventListener("click", closeSourceCandidates);
sourceModal.addEventListener("click", (event) => {
  if (event.target === sourceModal) closeSourceCandidates();
});
settingsButton.addEventListener("click", openSettings);
themeToggle.addEventListener("click", toggleTheme);
settingsCloseButton.addEventListener("click", closeSettings);
settingsModal.addEventListener("click", (event) => {
  if (event.target === settingsModal) closeSettings();
});
for (const input of databaseModeInputs) input.addEventListener("change", saveDatabaseMode);
for (const input of permissionInputs) input.addEventListener("change", saveSettings);
for (const input of transactionModeInputs) input.addEventListener("change", saveSettings);
for (const input of localDbInputs) input.addEventListener("change", saveLocalDbSettings);
testLocalDbButton.addEventListener("click", testLocalDbConnection);
openAdminerButton.addEventListener("click", openAdminer);
openDbGateButton.addEventListener("click", openDbGate);
lightContrastSelect.addEventListener("change", saveAppearance);
darkContrastSelect.addEventListener("change", saveAppearance);

loadDatabaseMode();
syncSidebarForViewport();

setTimeout(() => {
  if (manufacturerSelect.value) {
    loadMappingSources({ checkUrls: false, preferCache: true });
    loadProductForSelectedManufacturer(1);
  } else {
    renderMappingSources({ mapped: false, items: [], message: "Select manufacturer." });
    updateNavigationState();
  }
}, 0);

async function loadStatus() {
  const response = await fetch("/api/status");
  const status = await response.json();
  applicationMode = status.mode || "dry-run";
  if (status.database) {
    databaseStatus.dataset.driver = status.database.driver;
    const selectedMode = localStorage.getItem("catalog-maker:database-mode") || "local";
    databaseConnectionState = selectedMode === "local" && status.database.driver !== "mariadb"
      ? "error"
      : "ready";
  }
}

function renderFields() {
  fieldGrid.replaceChildren(
    ...fieldNames.flatMap((name) => {
      const label = document.createElement("div");
      label.className = "field-label";
      label.textContent = name;

      const value = document.createElement("div");
      value.className = "field-value";
      value.dataset.field = name;
      value.textContent = "";

      return [label, value];
    }),
  );
}

function renderTableHeader() {
  variantHeader.replaceChildren(
    ...variantColumns.map((column) => {
      const cell = document.createElement("div");
      cell.className = "variant-header-cell";
      cell.setAttribute("role", "columnheader");
      cell.textContent = column.label;
      cell.dataset.column = column.key;
      cell.style.textAlign = column.align || "";
      return cell;
    }),
  );
}

function setTableWidth() {
  const totalWidth = variantColumns.reduce((sum, column) => sum + column.width, 0);
  variantTable.style.setProperty("--variant-min-width", `${Math.max(totalWidth, 820)}px`);
  variantTable.style.setProperty(
    "--variant-columns",
    variantColumns.map((column) => `${column.width}px`).join(" "),
  );
}

async function loadManufacturers() {
  manufacturerSelect.replaceChildren(createOption("", "Select manufacturer"));
  try {
    const response = await fetch("/api/manufacturers");
    const data = await response.json();
    if (!response.ok) throw new Error(data.message || "Local DB is not configured.");

    for (const manufacturer of data.items || []) {
      manufacturerSelect.append(createOption(String(manufacturer.id), manufacturer.name));
    }
    if (data.message) manufacturerSelect.append(createOption("", data.message));
    restoreSelectedManufacturer();
    syncManufacturerPickerLabel();
    renderManufacturerOptions();
  } catch (error) {
    manufacturerSelect.append(createOption("", error.message || "Local DB is not configured."));
    restoreSelectedManufacturer();
    syncManufacturerPickerLabel();
    renderManufacturerOptions();
  }
}

function restoreSelectedManufacturer() {
  const savedManufacturerId = localStorage.getItem(selectedManufacturerKey);
  if (!savedManufacturerId) return;

  const savedOption = [...manufacturerSelect.options].find(
    (option) => option.value === savedManufacturerId,
  );
  if (savedOption) {
    manufacturerSelect.value = savedManufacturerId;
  } else {
    localStorage.removeItem(selectedManufacturerKey);
  }
}

function toggleManufacturerPicker() {
  if (manufacturerMenu.hidden) {
    manufacturerMenu.hidden = false;
    manufacturerTrigger.setAttribute("aria-expanded", "true");
    manufacturerSearch.value = "";
    renderManufacturerOptions();
    if (manufacturerSelect.options.length > 11) manufacturerSearch.focus();
  } else {
    closeManufacturerPicker();
  }
}

function closeManufacturerPicker() {
  manufacturerMenu.hidden = true;
  manufacturerTrigger.setAttribute("aria-expanded", "false");
}

function syncManufacturerPickerLabel() {
  const option = manufacturerSelect.selectedOptions[0];
  manufacturerValue.textContent = option?.textContent || "Select manufacturer";
}

function renderManufacturerOptions() {
  const options = [...manufacturerSelect.options].filter((option) => option.value);
  const query = manufacturerSearch.value.trim().toLocaleLowerCase();
  const filtered = options.filter((option) => option.textContent.toLocaleLowerCase().includes(query));
  const visible = filtered.slice(0, 10);

  manufacturerOptions.replaceChildren(
    ...visible.map((option) => {
      const item = document.createElement("button");
      item.className = "manufacturer-option";
      item.type = "button";
      item.setAttribute("role", "option");
      item.textContent = option.textContent;
      item.setAttribute("aria-selected", String(option.value === manufacturerSelect.value));
      item.addEventListener("click", () => {
        manufacturerSelect.value = option.value;
        manufacturerSelect.dispatchEvent(new Event("change", { bubbles: true }));
      });
      return item;
    }),
  );

  if (!visible.length) {
    const empty = document.createElement("div");
    empty.className = "manufacturer-empty";
    empty.textContent = "No manufacturer found.";
    manufacturerOptions.append(empty);
  }
}

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

function setButtonLabel(button, label) {
  const labelElement = button.querySelector(":scope > span");
  if (labelElement) {
    labelElement.textContent = label;
  } else {
    button.textContent = label;
  }
}

function setButtonIcon(button, iconName) {
  const useElement = button.querySelector(":scope > svg use");
  if (useElement) useElement.setAttribute("href", `/icons.svg#${iconName}`);
}

function setSourceBatchAction(mode) {
  sourceBatchActionButton.dataset.mode = mode;
  sourceBatchActionButton.hidden = false;
  sourceBatchActionButton.disabled = false;
  if (mode === "get-pictures") {
    setButtonIcon(sourceBatchActionButton, "image");
    setButtonLabel(sourceBatchActionButton, "Get all pictures");
    return;
  }

  setButtonIcon(sourceBatchActionButton, "save");
  setButtonLabel(sourceBatchActionButton, "Save all");
}

async function loadLanguages() {
  languageSelect.replaceChildren(createOption("", "Select language"));
  try {
    const response = await fetch("/api/languages");
    const data = await response.json();
    if (!response.ok) throw new Error(data.message || "Local DB is not configured.");

    for (const language of data.items || []) {
      const label = language.isoCode ? `${language.name} (${language.isoCode})` : language.name;
      languageSelect.append(createOption(String(language.id), label));
    }
    if (data.message) languageSelect.append(createOption("", data.message));
  } catch (error) {
    languageSelect.append(createOption("", error.message || "Local DB is not configured."));
  }
  syncLanguagePickerLabel();
}

function syncLanguagePickerLabel() {
  const option = languageSelect.selectedOptions[0];
  languageValue.textContent = option?.textContent || "Select language";
}

async function loadMappingSources({ checkUrls = false, preferCache = false } = {}) {
  const manufacturerId = manufacturerSelect.value;
  const loadToken = ++mappingLoadToken;
  if (!manufacturerId) {
    renderMappingSources({ mapped: false, items: [], message: "Select manufacturer." });
    return;
  }

  const cached = preferCache ? readMappingCache(manufacturerId) : null;
  if (cached) {
    renderMappingSources(cached);
    return;
  }

  setMappingStatus("Loading mapping...", "is-loading");

  try {
    const params = new URLSearchParams({ manufacturerId });
    if (checkUrls) params.set("check", "1");
    const response = await fetch(`/api/catalog-maker/mapping-sources?${params.toString()}`);
    const data = await response.json();
    if (loadToken !== mappingLoadToken || manufacturerSelect.value !== manufacturerId) return;

    if (!response.ok) {
      throw new Error(data.message || "Mapping load failed.");
    }

    renderMappingSources(data);
    if (data.mapped && (data.items || []).some((item) => item.enabled)) {
      saveMappingCache(manufacturerId, data);
    }
  } catch (error) {
    if (loadToken !== mappingLoadToken || manufacturerSelect.value !== manufacturerId) return;
    renderMappingSources({
      mapped: false,
      items: [],
      message: error.message || "Mapping load failed.",
    });
  }
}

function readMappingCache(manufacturerId) {
  try {
    const cache = JSON.parse(localStorage.getItem(mappingCacheKey) || "{}");
    return cache[String(manufacturerId)] || null;
  } catch {
    return null;
  }
}

function saveMappingCache(manufacturerId, data) {
  try {
    const cache = JSON.parse(localStorage.getItem(mappingCacheKey) || "{}");
    cache[String(manufacturerId)] = {
      ...data,
      cachedAt: new Date().toISOString(),
    };
    localStorage.setItem(mappingCacheKey, JSON.stringify(cache));
  } catch {
    // Mapping cache is only a UI convenience. If storage fails, normal loading still works.
  }
}

function renderMappingSources(data) {
  const items = data.items || [];

  if (!data.mapped || !items.length) {
    setMappingStatus(data.message || "Mapping missing", "is-missing");
    mappingList.replaceChildren();
    mappingDetails.open = false;
    return;
  }

  const enabledCount = items.filter((item) => item.enabled).length;
  const failedCount = items.filter((item) => item.enabled && item.health && item.health !== "ok").length;
  const testedCount = items.filter((item) => item.enabled && item.health === "ok").length;
  const checkedCount = items.filter((item) => item.enabled && item.health).length;
  const ready = enabledCount > 0 && failedCount === 0;
  const statusText = ready
    ? `Sources OK ✓ (${testedCount}/${enabledCount})`
    : data.message || `Sources problem (${testedCount}/${enabledCount})`;
  const displayStatusText = ready && !checkedCount ? `Mapping OK (${enabledCount})` : statusText;
  setMappingStatus(displayStatusText, ready ? "is-ready" : testedCount > 0 ? "is-warning" : "is-missing");
  mappingDetails.open = false;

  mappingList.replaceChildren(
    ...items.map((item) => {
      const row = document.createElement("div");
      row.className = `mapping-source-row${item.enabled ? "" : " is-disabled"}`;

      const name = document.createElement("span");
      name.className = "mapping-source-name";
      name.textContent = formatValue(item.name || item.key || item.url || "Source");

      const status = document.createElement("span");
      status.className = `mapping-source-status ${getMappingHealthClass(item)}`;
      status.textContent = getMappingHealthText(item);

      row.append(name, status);
      return row;
    }),
  );
}

function setMappingStatus(text, stateClass) {
  mappingState.textContent = "";
  mappingState.className = `mapping-state-dot ${stateClass}`;
  mappingState.setAttribute("aria-label", text);
  mappingStatusText.textContent = text;
}

function getMappingHealthText(item) {
  if (!item.enabled) return "off";
  if (!item.health) return "ready";
  if (item.health === "ok") return `${item.httpStatus || 200} OK`;
  if (item.health === "missing-url") return "no URL";
  if (item.httpStatus) return `${item.httpStatus}`;
  if (item.health) return "error";
  return "netest";
}

function getMappingHealthClass(item) {
  if (!item.enabled) return "is-muted";
  if (!item.health) return "is-ok";
  if (item.health === "ok") return "is-ok";
  return "is-bad";
}

async function loadProductForSelectedManufacturer(position = currentPosition) {
  const requestId = ++loadRequestId;
  const manufacturerId = manufacturerSelect.value;
  if (!manufacturerId) {
    clearProductState("Select manufacturer to load data.");
    return;
  }

  setNavigationEnabled(false);
  setTableMessage("Loading product data...");

  try {
    const response = await fetch(
      `/api/catalog-maker/product?manufacturerId=${encodeURIComponent(manufacturerId)}&position=${encodeURIComponent(position)}`,
    );
    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.message || "Product load failed.");
    }

    if (!data.product) {
      if (requestId !== loadRequestId) return;
      clearProductState(data.message || "No product to do for selected manufacturer.");
      return;
    }

    if (requestId !== loadRequestId) return;
    renderProduct(data.product);
    currentCombinations = data.combinations;
    renderCombinations(currentCombinations);
  } catch (error) {
    setTableMessage(error.message);
  } finally {
    updateNavigationState();
  }
}

async function loadProductByScannedEan() {
  const requestId = ++loadRequestId;
  const manufacturerId = manufacturerSelect.value;
  const ean = normalizeScannedEan(scanInput.value);

  if (!manufacturerId) {
    setTableMessage("Select manufacturer before scanning EAN.");
    return;
  }

  if (!ean) {
    setTableMessage("Scan EAN first.");
    return;
  }

  setNavigationEnabled(false);
  scanInput.disabled = true;
  setTableMessage(`Searching EAN ${ean}...`);

  try {
    const params = new URLSearchParams({ manufacturerId, ean });
    const response = await fetch(`/api/catalog-maker/product-by-ean?${params.toString()}`);
    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.message || "EAN search failed.");
    }

    if (!data.product) {
      if (requestId !== loadRequestId) return;
      await showSuggestedProducts({ ean });
      return;
    }

    if (requestId !== loadRequestId) return;
    renderProduct(data.product);
    currentCombinations = data.combinations;
    renderCombinations(currentCombinations);
    scanInput.value = "";

    if (!data.product.idProduct) {
      await showSuggestedProducts({
        ean,
        supplierReference: data.product.supplierReference,
        productName: data.product.name,
      });
    }
  } catch (error) {
    setTableMessage(error.message);
  } finally {
    scanInput.disabled = false;
    scanInput.focus();
    updateNavigationState();
  }
}

async function showSuggestedProducts({ ean, supplierReference = "", productName = "" }) {
  const params = new URLSearchParams({
    manufacturerId: manufacturerSelect.value,
    supplierReference: supplierReference || currentProduct?.supplierReference || scanInput.value || ean,
    productName: productName || currentProduct?.name || "",
  });
  const response = await fetch(`/api/catalog-maker/suggested-products?${params.toString()}`);
  const data = await response.json();

  if (!response.ok) {
    throw new Error(data.message || "Suggested product lookup failed.");
  }

  suggestedTitle.textContent = `Sugested product for ${ean}`;
  renderSuggestedProducts(data.items);
  suggestedModal.hidden = false;
}

function renderSuggestedProducts(items) {
  if (!items.length) {
    const empty = document.createElement("div");
    empty.className = "suggested-row";
    empty.textContent = "No suggested products found.";
    suggestedList.replaceChildren(empty);
    return;
  }

  suggestedList.replaceChildren(
    ...items.map((item) => {
      const row = document.createElement("div");
      row.className = "suggested-row";

      const image = document.createElement("img");
      image.alt = item.name || "Product";
      if (item.imageUrl) image.src = item.imageUrl;

      const id = document.createElement("div");
      id.className = "suggested-id";
      id.textContent = formatValue(item.idProduct);

      const name = document.createElement("div");
      name.className = "suggested-name";
      name.textContent = formatValue(item.name);
      name.title = name.textContent;

      const reference = document.createElement("div");
      reference.className = "suggested-reference";
      reference.textContent = formatValue(item.supplierReference);
      reference.title = reference.textContent;

      const active = document.createElement("div");
      active.className = "suggested-active";
      active.textContent = formatValue(item.active);

      const pick = document.createElement("button");
      pick.className = "suggested-pick";
      pick.type = "button";
      pick.textContent = "Use";
      pick.addEventListener("click", () => selectSuggestedProduct(item));

      row.append(image, id, name, reference, active, pick);
      return row;
    }),
  );
}

function selectSuggestedProduct(item) {
  if (!currentProduct) return;

  currentProduct = {
    ...currentProduct,
    idProduct: item.idProduct,
    imageUrl: item.imageUrl || currentProduct.imageUrl,
  };
  recordIdElement.textContent = formatRecordId(item.idProduct);
  renderPicture(currentProduct.imageUrl);
  closeSuggestedProducts();
  updateNavigationState();
}

function closeSuggestedProducts() {
  suggestedModal.hidden = true;
  suggestedList.replaceChildren();
}

async function findSourcesForCurrentProduct(forceRefresh = false) {
  if (!manufacturerSelect.value || !currentProduct) {
    setTableMessage("Select manufacturer and product first.");
    return;
  }

  const savedDraft = readLocalDraft();
  if (savedDraft?.sources && !forceRefresh) {
    renderSourceCandidates(savedDraft.sources.items || []);
    updateFindSourcesButtonState();
    sourceTitle.textContent = "Source candidates (cached)";
    sourceModal.hidden = false;
    return;
  }

  const supplierColorGroup = getFirstSupplierColorGroup();
  findSourcesButton.disabled = true;
  sourceReloadButton.disabled = true;
    setButtonLabel(findSourcesButton, "Finding sources...");

  try {
    const params = new URLSearchParams({
      manufacturerId: manufacturerSelect.value,
      productName: currentProduct.name || "",
      supplierReference: currentProduct.supplierReference || "",
      ean: supplierColorGroup?.supplierEan || currentCombinations[0]?.ean13 || "",
      eanGroup: (supplierColorGroup?.supplierRows || []).map((row) => row.ean13).filter(Boolean).join(","),
      color: supplierColorGroup?.color || "",
    });
    const response = await fetch(`/api/catalog-maker/find-sources?${params.toString()}`);
    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.message || "Source search failed.");
    }

    saveLocalDraft({ sources: data });
    updateFindSourcesButtonState();
    renderSourceCandidates(data.items || []);
    const targetLabel = supplierColorGroup
      ? `${supplierColorGroup.color} / EAN ${supplierColorGroup.supplierEan}`
      : currentProduct.name || currentProduct.supplierReference;
    sourceTitle.textContent = `Source candidates for ${targetLabel}`;
    sourceModal.hidden = false;
  } catch (error) {
    setTableMessage(error.message || "Source search failed.");
  } finally {
    sourceReloadButton.disabled = false;
    updateFindSourcesButtonState();
  }
}

async function getPicturesForCurrentProduct(sourceKey = "") {
  if (!manufacturerSelect.value || !currentProduct) {
    setTableMessage("Select manufacturer and product first.");
    return;
  }

  const savedDraft = readLocalDraft();
  const cachedPictures = savedDraft?.picturesBySource?.[sourceKey || "all"];
  if (cachedPictures) {
    renderPictureCandidates(cachedPictures.items || []);
    sourceTitle.textContent = `Picture candidates (cached${sourceKey ? `: ${sourceKey}` : ""})`;
    sourceModal.hidden = false;
    return;
  }

  const supplierColorGroup = getFirstSupplierColorGroup();
  getPicturesButton.disabled = true;
    setButtonLabel(getPicturesButton, "Loading pictures...");

  try {
    const params = new URLSearchParams({
      manufacturerId: manufacturerSelect.value,
      productName: currentProduct.name || "",
      supplierReference: currentProduct.supplierReference || "",
      ean: supplierColorGroup?.supplierEan || currentCombinations[0]?.ean13 || "",
      eanGroup: (supplierColorGroup?.supplierRows || []).map((row) => row.ean13).filter(Boolean).join(","),
      color: supplierColorGroup?.color || "",
    });
    if (sourceKey) params.set("sourceKey", sourceKey);
    const response = await fetch(`/api/catalog-maker/get-pictures?${params.toString()}`);
    const data = await response.json();
    if (!response.ok) throw new Error(data.message || "Picture lookup failed.");

    const picturesBySource = readLocalDraft()?.picturesBySource || {};
    picturesBySource[sourceKey || "all"] = data;
    saveLocalDraft({ picturesBySource });

    renderPictureCandidates(data.items || []);
    sourceTitle.textContent = `Picture candidates${sourceKey ? `: ${sourceKey}` : ""}`;
    sourceModal.hidden = false;
  } catch (error) {
    setTableMessage(error.message || "Picture lookup failed.");
  } finally {
    getPicturesButton.disabled = false;
        setButtonLabel(getPicturesButton, "Get pictures");
    updateNavigationState();
  }
}

function getFirstSupplierColorGroup() {
  const groups = new Map();

  for (const combination of currentCombinations) {
    const color = formatValue(combination.color) || "Unknown color";
    const group = groups.get(color) || {
      color,
      rows: [],
      supplierRows: [],
      supplierEan: "",
    };

    group.rows.push(combination);
    if (Number(combination.supplierStock) === 1) {
      group.supplierRows.push(combination);
      if (!group.supplierEan) group.supplierEan = combination.ean13;
    }
    groups.set(color, group);
  }

  return [...groups.values()].find((group) => group.supplierRows.length > 0) || null;
}

function renderSourceCandidates(items) {
  sourceBatchActionButton.hidden = true;
  if (!items.length) {
    const empty = document.createElement("div");
    empty.className = "source-row";
    empty.textContent = "No source mapping found.";
    sourceList.replaceChildren(empty);
    return;
  }

  setSourceBatchAction("get-pictures");
  sourceList.replaceChildren(
    ...items.map((item) => {
      const section = document.createElement("section");
      section.className = "source-section";

      const header = document.createElement("div");
      header.className = "source-section-header";

      const name = document.createElement("strong");
      name.textContent = formatValue(item.source?.name || item.source?.key || "Source");

      const status = document.createElement("span");
      status.className = `source-status ${item.status === "found" ? "is-ok" : "is-muted"}`;
      status.textContent = item.status === "found" ? `${item.candidates.length} found` : item.status;

      const actions = document.createElement("div");
      actions.className = "source-section-actions";
      const pictures = document.createElement("button");
      pictures.type = "button";
      pictures.className = "source-picture-action";
      pictures.textContent = "Get pictures";
      pictures.addEventListener("click", () =>
        getPicturesForCurrentProduct(item.source?.key || ""),
      );
      actions.append(status, pictures);

      header.append(name, actions);

      const rows = item.candidates.length
        ? item.candidates.map((candidate) => renderSourceCandidateRow(candidate, item.source))
        : [renderManualSearchRow(item.searchUrl, item.error || "Open search", item.source)];

      section.append(header, ...rows);
      return section;
    }),
  );
}

function renderPictureCandidates(items) {
  sourceBatchActionButton.hidden = true;
  const rows = [];
  let position = 0;
  for (const item of items) {
    for (const candidate of item.candidates || []) {
      for (const picture of candidate.pictures || []) {
        position += 1;
        const sourcePosition = position;
        const target = buildPictureTargetInfo();
        const row = document.createElement("div");
        row.className = "picture-candidate";

        const preview = document.createElement("a");
        preview.href = picture;
        preview.target = "_blank";
        preview.rel = "noreferrer";
        preview.className = "picture-candidate-preview";

        const image = document.createElement("img");
        image.src = picture;
        image.alt = candidate.rawTitle || item.source?.name || "Product picture";
        image.loading = "lazy";
        preview.append(image);

        const details = document.createElement("div");
        details.className = "picture-candidate-meta";

        const label = document.createElement("span");
        label.className = "picture-candidate-title";
        label.textContent = `${item.source?.name || item.source?.key || "Source"} picture #${sourcePosition}`;

        const dimensions = document.createElement("span");
        dimensions.className = "picture-candidate-dimensions";
        dimensions.textContent = "Size: loading...";
        image.addEventListener("load", () => {
          if (!image.naturalWidth || !image.naturalHeight) {
            dimensions.textContent = "Size: unknown";
            row.classList.remove("is-too-small");
            return;
          }

          const shortestSide = Math.min(image.naturalWidth, image.naturalHeight);
          const longestSide = Math.max(image.naturalWidth, image.naturalHeight);
          const isTooSmall = longestSide < MIN_PRODUCT_PICTURE_SIZE;
          row.classList.toggle("is-too-small", isTooSmall);
          dimensions.textContent = isTooSmall
            ? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - too small, min long side ${MIN_PRODUCT_PICTURE_SIZE}px`
            : shortestSide < MIN_PRODUCT_PICTURE_SIZE
              ? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - OK for 1:1 crop`
              : `Size: ${image.naturalWidth} x ${image.naturalHeight}px`;
        });
        image.addEventListener("error", () => {
          dimensions.textContent = "Size: image preview failed";
          row.classList.add("is-too-small");
        });

        const assignment = document.createElement("span");
        assignment.className = "picture-candidate-target";
        assignment.textContent = target.label;

        const duplicateHint = document.createElement("span");
        duplicateHint.className = "picture-candidate-duplicate";
        duplicateHint.textContent = "DB: not saved";

        details.append(label, dimensions, assignment, duplicateHint);

        const action = document.createElement("button");
        action.type = "button";
        action.className = "source-picture-action picture-candidate-save";
        action.textContent = "Save to DB";
        action.savePictureCandidate = () =>
          savePictureCandidateToDb({
            picture,
            item,
            candidate,
            image,
            position: sourcePosition,
            target,
            action,
            duplicateHint,
          });
        action.addEventListener("click", () => action.savePictureCandidate());

        row.append(preview, details, action);
        rows.push(row);
      }
    }
  }

  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "source-row";
    empty.textContent = "No pictures found. Open the source manually to inspect it.";
    sourceList.replaceChildren(empty);
    return;
  }
  setSourceBatchAction("save-pictures");
  sourceList.replaceChildren(...rows);
}

function buildPictureTargetInfo() {
  const supplierColorGroup = getFirstSupplierColorGroup();
  const firstSupplierRow = supplierColorGroup?.supplierRows?.[0] || currentCombinations[0] || {};
  const color = supplierColorGroup?.color || formatValue(firstSupplierRow.color);
  const ean13 = supplierColorGroup?.supplierEan || firstSupplierRow.ean13 || "";

  if (color) {
    return {
      strategy: "color",
      color,
      sizeValue: "",
      combi: "",
      ean13,
      idProductCatalog: firstSupplierRow.idProductCatalog || currentProduct?.idProductCatalog || "",
      label: `Target: color ${color}${ean13 ? ` / EAN ${ean13}` : ""}`,
    };
  }

  return {
    strategy: "unknown",
    color: "",
    sizeValue: "",
    combi: "",
    ean13,
    idProductCatalog: firstSupplierRow.idProductCatalog || currentProduct?.idProductCatalog || "",
    label: ean13 ? `Target: unknown / EAN ${ean13}` : "Target: needs manual assignment",
  };
}

async function savePictureCandidateToDb({
  picture,
  item,
  candidate,
  image,
  position,
  target,
  action,
  duplicateHint,
}) {
  action.disabled = true;
  action.textContent = "Saving...";
  duplicateHint.textContent = "DB: saving...";

  try {
    const payload = {
      sourceKey: item.source?.key || "",
      sourceName: item.source?.name || "",
      sourceType: item.source?.type || "",
      imageUrl: picture,
      pageUrl: candidate.url || item.searchUrl || "",
      sourcePosition: position,
      manufacturerId: manufacturerSelect.value,
      idProductCatalog: currentProduct?.idProductCatalog || target.idProductCatalog || "",
      idProduct: currentProduct?.idProduct || "",
      supplierReference: currentProduct?.supplierReference || "",
      ean13: target.ean13 || "",
      color: target.color || "",
      sizeValue: target.sizeValue || "",
      combi: target.combi || "",
      targetStrategy: target.strategy,
      targetColor: target.color || "",
      targetSize: target.sizeValue || "",
      targetCombi: target.combi || "",
      targetEan13: target.ean13 || "",
      targetIdProductCatalog: target.idProductCatalog || currentProduct?.idProductCatalog || "",
      matchConfidence: target.strategy === "color" ? 70 : 0,
      width: image.naturalWidth || "",
      height: image.naturalHeight || "",
    };
    const response = await fetch("/api/catalog-maker/save-picture", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
    });
    const data = await response.json();
    if (!response.ok) throw new Error(data.message || "Picture save failed.");

    action.textContent = "Saved";
    action.dataset.saved = "1";
    duplicateHint.textContent = data.duplicateStatus === "exact_duplicate"
      ? `DB: duplicate of #${data.duplicateOfCandidateId || "known"}`
      : `DB: saved #${data.candidateId}`;
    return true;
  } catch (error) {
    action.disabled = false;
    action.textContent = "Save to DB";
    duplicateHint.textContent = `DB: ${error.message || "save failed"}`;
    return false;
  }
}

async function saveAllVisiblePictureCandidates() {
  const buttons = [...sourceList.querySelectorAll(".picture-candidate-save")]
    .filter((button) => button.savePictureCandidate && button.dataset.saved !== "1");

  if (!buttons.length) return;

  sourceBatchActionButton.disabled = true;
  setButtonLabel(sourceBatchActionButton, `Saving 0/${buttons.length}`);
  let savedCount = 0;
  for (const button of buttons) {
    const saved = await button.savePictureCandidate();
    if (saved) savedCount += 1;
    setButtonLabel(sourceBatchActionButton, `Saving ${savedCount}/${buttons.length}`);
  }

  sourceBatchActionButton.disabled = false;
  setButtonLabel(
    sourceBatchActionButton,
    savedCount === buttons.length ? "Saved all" : `Saved ${savedCount}/${buttons.length}`,
  );
}

async function handleSourceBatchAction() {
  if (sourceBatchActionButton.dataset.mode === "get-pictures") {
    sourceBatchActionButton.disabled = true;
    setButtonLabel(sourceBatchActionButton, "Loading pictures...");
    try {
      await getPicturesForCurrentProduct("");
    } finally {
      sourceBatchActionButton.disabled = false;
      if (sourceBatchActionButton.dataset.mode === "get-pictures") {
        setButtonLabel(sourceBatchActionButton, "Get all pictures");
      }
    }
    return;
  }

  await saveAllVisiblePictureCandidates();
}

function collectPictureUrls(items) {
  return items.flatMap((item) =>
    (item.candidates || []).flatMap((candidate) => candidate.pictures || []),
  ).filter((url, index, all) => all.indexOf(url) === index);
}

function saveLocalDraft(patch) {
  if (!localDraftProductKey) return;
  let draft = {};
  try {
    draft = JSON.parse(localStorage.getItem(localDraftKey) || "{}");
  } catch {
    draft = {};
  }
  localStorage.setItem(localDraftKey, JSON.stringify({
    ...draft,
    productKey: localDraftProductKey,
    ...patch,
  }));
}

function readLocalDraft() {
  if (!localDraftProductKey) return null;
  try {
    const draft = JSON.parse(localStorage.getItem(localDraftKey) || "null");
    return draft?.productKey === localDraftProductKey ? draft : null;
  } catch {
    return null;
  }
}

function clearLocalDraft() {
  localStorage.removeItem(localDraftKey);
  localDraftProductKey = "";
  updateFindSourcesButtonState();
}

function updateFindSourcesButtonState() {
  const hasSources = Boolean(readLocalDraft()?.sources);
  setButtonLabel(findSourcesButton, hasSources ? "Find sources ✓" : "Find sources");
  findSourcesButton.disabled = !manufacturerSelect.value || !currentProduct;
}

function renderSourceCandidateRow(candidate, source) {
  const row = document.createElement("div");
  row.className = "source-row";

  const title = document.createElement("span");
  title.className = "source-row-title";
  title.textContent = formatValue(candidate.title || candidate.url);
  title.title = formatValue(candidate.rawTitle || candidate.title || candidate.url);

  const action = document.createElement("button");
  action.type = "button";
  action.className = "source-row-action";
  action.textContent = "Open";
  action.addEventListener("click", () => openSourceCandidate(candidate.url, source, action));

  row.append(title, action);
  return row;
}

async function openSourceCandidate(url, source, button) {
  if (!url || !source?.key || !manufacturerSelect.value) return;
  const previousText = button.textContent;
  button.disabled = true;
  button.textContent = "Opening...";

  try {
    const params = new URLSearchParams({
      manufacturerId: manufacturerSelect.value,
      sourceKey: source.key,
      url,
    });
    const response = await fetch(`/api/catalog-maker/open-source?${params.toString()}`);
    const data = await response.json();
    if (!response.ok || data.error) throw new Error(data.error || "Source browser could not be opened.");
  } catch (error) {
    alert(error.message || "Source browser could not be opened.");
  } finally {
    button.disabled = false;
    button.textContent = previousText;
  }
}

function renderManualSearchRow(url, label, source) {
  return renderSourceCandidateRow({
    title: label,
    url,
  }, source);
}

function closeSourceCandidates() {
  sourceModal.hidden = true;
  sourceList.replaceChildren();
}

function openSettings() {
  loadDatabaseMode();
  settingsModal.hidden = false;
}

function syncSidebarForViewport() {
  if (window.matchMedia("(min-width: 981px)").matches && !sidebarUserClosed) openSidebar();
  else closeSidebar();
}

function openSidebar() {
  sidebarUserClosed = false;
  sidebarDrawer.classList.add("is-open");
  sidebarOverlay.hidden = false;
  sidebarOpenButton.hidden = true;
  sidebarOpenButton.setAttribute("aria-expanded", "true");
  sidebarDrawer.setAttribute("aria-hidden", "false");
  catalogScreen.classList.remove("sidebar-collapsed");
}

function closeSidebar() {
  sidebarUserClosed = true;
  sidebarDrawer.classList.remove("is-open");
  sidebarOverlay.hidden = true;
  sidebarOpenButton.hidden = false;
  sidebarOpenButton.setAttribute("aria-expanded", "false");
  sidebarDrawer.setAttribute("aria-hidden", "true");
  catalogScreen.classList.add("sidebar-collapsed");
}

function toggleSecondaryPanel() {
  const isCollapsed = catalogScreen.classList.toggle("secondary-collapsed");
  secondaryPanelToggle.setAttribute("aria-expanded", String(!isCollapsed));
  secondaryPanelToggle.setAttribute("aria-label", isCollapsed ? "Expand product details" : "Collapse product details");
  secondaryPanelToggle.title = isCollapsed ? "Expand product details" : "Collapse product details";
  secondaryPanelToggle.textContent = isCollapsed ? "›" : "‹";
}

function toggleMainWorkspace() {
  const isCollapsed = catalogScreen.classList.toggle("workspace-collapsed");
  mainWorkspaceToggle.setAttribute("aria-expanded", String(!isCollapsed));
  mainWorkspaceToggle.setAttribute("aria-label", isCollapsed ? "Expand main workspace" : "Collapse main workspace");
  mainWorkspaceToggle.title = isCollapsed ? "Expand main workspace" : "Collapse main workspace";
  mainWorkspaceToggle.textContent = isCollapsed ? "›" : "‹";
}

function loadTheme() {
  const isDark = localStorage.getItem("catalog-maker:theme") === "dark";
  document.documentElement.classList.toggle("dark", isDark);
  themeToggle.querySelector("span").textContent = isDark ? "Light theme" : "Dark theme";
  themeToggle.setAttribute("aria-pressed", String(isDark));
}

function loadAppearance() {
  const settings = JSON.parse(localStorage.getItem("catalog-maker:appearance") || "{}");
  lightContrastSelect.value = settings.lightContrast || "medium";
  darkContrastSelect.value = settings.darkContrast || "medium";
  applyAppearance();
}

function saveAppearance() {
  localStorage.setItem("catalog-maker:appearance", JSON.stringify({
    lightContrast: lightContrastSelect.value,
    darkContrast: darkContrastSelect.value,
  }));
  applyAppearance();
}

function applyAppearance() {
  const root = document.documentElement;
  root.classList.remove("contrast-light-low", "contrast-light-medium", "contrast-light-high", "contrast-dark-low", "contrast-dark-medium", "contrast-dark-high");
  root.classList.add(`contrast-light-${lightContrastSelect.value || "medium"}`);
  root.classList.add(`contrast-dark-${darkContrastSelect.value || "medium"}`);
}

function toggleTheme() {
  const isDark = !document.documentElement.classList.contains("dark");
  localStorage.setItem("catalog-maker:theme", isDark ? "dark" : "light");
  loadTheme();
}

function closeSettings() {
  settingsModal.hidden = true;
}

function loadDatabaseMode() {
  const mode = localStorage.getItem("catalog-maker:database-mode") || "local";
  const input = document.querySelector(`input[name="databaseMode"][value="${mode}"]`);
  if (input) input.checked = true;
  applyPermissionInputs(readDatabasePermissions(mode));
  const transactionMode = localStorage.getItem("catalog-maker:transaction-mode") || "transaction";
  const transactionInput = document.querySelector(`input[name="transactionMode"][value="${transactionMode}"]`);
  if (transactionInput) transactionInput.checked = true;
  const localDbSettings = JSON.parse(localStorage.getItem("catalog-maker:local-db-settings") || "{}");
  for (const input of localDbInputs) {
    if (Object.prototype.hasOwnProperty.call(localDbSettings, input.id)) input.value = localDbSettings[input.id];
  }
  syncDatabasePermissions(mode);
  syncLocalDbFields(mode);
  syncAdminerLoginHint();
  renderDatabaseModeNote(mode);
  refreshDatabaseStatusLabel();
}

function saveDatabaseMode(event) {
  const mode = ["local", "live", "scrape"].includes(event.target.value) ? event.target.value : "local";
  localStorage.setItem("catalog-maker:database-mode", mode);
  applyPermissionInputs(readDatabasePermissions(mode));
  syncDatabasePermissions(mode);
  syncLocalDbFields(mode);
  saveSettings();
  renderDatabaseModeNote(mode);
  refreshDatabaseStatusLabel();
}

function saveLocalDbSettings() {
  const settings = {};
  for (const input of localDbInputs) settings[input.id] = input.value;
  localStorage.setItem("catalog-maker:local-db-settings", JSON.stringify(settings));
  syncAdminerLoginHint();
}

function syncAdminerLoginHint() {
  const host = document.querySelector("#localDbHost")?.value || "127.0.0.1";
  const port = document.querySelector("#localDbPort")?.value || "3306";
  const user = document.querySelector("#localDbUser")?.value || "root";
  const catalogDb = document.querySelector("#localDbName")?.value || "9bplus";
  adminerServerValue.textContent = `${host}:${port}`;
  adminerUserValue.textContent = user;
  adminerDatabaseValue.textContent = `${catalogDb} / catalog_scrape`;
}

async function testLocalDbConnection() {
  saveLocalDbSettings();
  testLocalDbButton.disabled = true;
  localDbTestResult.textContent = "Testing connection...";
  localDbTestResult.className = "settings-note";
  const payload = Object.fromEntries([...localDbInputs].map((input) => [
    input.id.replace("localDb", "").toLowerCase(),
    input.value,
  ]));

  try {
    const response = await fetch("/api/database/test", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
    });
    const data = await response.json();
    if (!response.ok || !data.connected) throw new Error(data.message || "Connection failed.");
    localDbTestResult.textContent = `Connected: ${data.version}`;
    localDbTestResult.className = "settings-note connection-ok";
  } catch (error) {
    localDbTestResult.textContent = `Connection error: ${error.message}`;
    localDbTestResult.className = "settings-note connection-error";
  } finally {
    testLocalDbButton.disabled = false;
  }
}

async function openAdminer() {
  openAdminerButton.disabled = true;
  const previousNote = databaseModeNote.textContent;
  databaseModeNote.textContent = "Opening Adminer...";

  try {
    const response = await fetch("/api/tools/open-adminer");
    const data = await response.json();
    if (!response.ok || !data.opened) throw new Error(data.message || "Adminer could not be opened.");
    databaseModeNote.textContent = `Adminer opened: ${data.url}`;
  } catch (error) {
    databaseModeNote.textContent = `Adminer error: ${error.message}`;
  } finally {
    openDbGateButton.disabled = false;
    window.setTimeout(() => {
      if (databaseModeNote.textContent.startsWith("Adminer")) {
        renderDatabaseModeNote(localStorage.getItem("catalog-maker:database-mode") || "local");
      } else if (databaseModeNote.textContent === "Opening Adminer...") {
        databaseModeNote.textContent = previousNote;
      }
    }, 3500);
  }
}

async function openDbGate() {
  openDbGateButton.disabled = true;
  const previousNote = databaseModeNote.textContent;
  databaseModeNote.textContent = "Opening DBGate...";

  try {
    const response = await fetch("/api/tools/open-dbgate");
    const data = await response.json();
    if (!response.ok || !data.opened) throw new Error(data.message || "DBGate could not be opened.");
    databaseModeNote.textContent = `DBGate opened: ${data.url}`;
  } catch (error) {
    databaseModeNote.textContent = `DBGate error: ${error.message}`;
  } finally {
    openDbGateButton.disabled = false;
    window.setTimeout(() => {
      if (databaseModeNote.textContent.startsWith("DBGate")) {
        renderDatabaseModeNote(localStorage.getItem("catalog-maker:database-mode") || "local");
      } else if (databaseModeNote.textContent === "Opening DBGate...") {
        databaseModeNote.textContent = previousNote;
      }
    }, 3500);
  }
}

function syncLocalDbFields(mode) {
  localDbFields.hidden = mode !== "local";
}

function syncDatabasePermissions(mode) {
  const isReadOnly = mode === "live";
  for (const input of permissionInputs) {
    input.disabled = isReadOnly;
    if (isReadOnly) input.checked = false;
  }
}

function saveSettings() {
  const mode = localStorage.getItem("catalog-maker:database-mode") || "local";
  const permissions = {};
  for (const input of permissionInputs) permissions[input.id] = input.checked;
  localStorage.setItem(permissionStorageKeys[mode] || permissionStorageKeys.local, JSON.stringify(permissions));
  if (mode === "local") {
    localStorage.setItem(legacyPermissionStorageKey, JSON.stringify(permissions));
  }
  const transactionMode = [...transactionModeInputs].find((input) => input.checked)?.value || "transaction";
  localStorage.setItem("catalog-maker:transaction-mode", transactionMode);
  refreshDatabaseStatusLabel();
}

function refreshDatabaseStatusLabel() {
  const mode = localStorage.getItem("catalog-maker:database-mode") || "local";
  const transactionMode = localStorage.getItem("catalog-maker:transaction-mode") || "transaction";
  const permissions = readDatabasePermissions(mode);
  const writesEnabled = ["local", "scrape"].includes(mode) && (
    permissions.allowInsertToggle || permissions.allowUpdateToggle || permissions.allowDeleteToggle
  );
  const dbLabel = mode === "live" ? "Live 9bplus DB" : mode === "scrape" ? "Local scrape DB" : "Local 9bplus DB";
  const configuredDriver = databaseStatus.dataset.driver;
  const connectionError = (mode === "local" && configuredDriver !== "mariadb")
    || (mode === "live" && configuredDriver !== "endpoint");
  const connectionLabel = connectionError ? "connection error" : "connected";
  const accessLabel = writesEnabled ? "writes enabled" : "read-only";
  const executionLabel = applicationMode === "dry-run"
    ? "dry-run"
    : (transactionMode === "dry-run" ? "dry-run" : "transaction");
  databaseStatus.textContent = `${dbLabel} / ${connectionLabel} / ${accessLabel} / ${executionLabel}`;
  databaseStatus.classList.toggle("database-error", connectionError);
  databaseStateDot.className = `database-state-dot ${databaseConnectionState === "unknown" ? "is-pending" : connectionError ? "is-error" : "is-ready"}`;
  databaseStateDot.setAttribute("aria-label", connectionError ? "Database disconnected" : databaseConnectionState === "unknown" ? "Database connection pending" : "Database connected");
}

function readDatabasePermissions(mode = "local") {
  const storageKey = permissionStorageKeys[mode] || permissionStorageKeys.local;
  const stored = readJsonStorage(storageKey);
  if (stored) return stored;

  if (mode === "local") {
    const legacy = readJsonStorage(legacyPermissionStorageKey);
    if (legacy) return legacy;
  }

  return {};
}

function applyPermissionInputs(permissions) {
  for (const input of permissionInputs) input.checked = permissions[input.id] === true;
}

function readJsonStorage(key) {
  try {
    const value = localStorage.getItem(key);
    return value ? JSON.parse(value) : null;
  } catch {
    return null;
  }
}

function renderDatabaseModeNote(mode) {
  databaseModeNote.textContent = mode === "live"
    ? "Live 9bplus DB selected for read-only data."
    : mode === "scrape"
      ? "Local scrape DB selected for scraper settings and scraped data. Catalog reads stay on Local 9bplus DB."
      : "Local 9bplus DB selected.";
}

function renderProduct(product) {
  const productKey = `${manufacturerSelect.value}:${product.idProductCatalog || product.supplierReference || product.name || ""}`;
  if (localDraftProductKey && localDraftProductKey !== productKey) clearLocalDraft();
  localDraftProductKey = productKey;
  currentProduct = product;
  recordIdElement.textContent = formatRecordId(product.idProduct);
  currentPosition = Number(product.position) || currentPosition;
  totalPositions = Number(product.total) || 0;
  const enrichedProduct = {
    ...product,
    positionTotal:
      product.position && product.total ? `${product.position}/${product.total}` : "",
  };

  for (const [label, key] of Object.entries(productFieldMap)) {
    const valueElement = document.querySelector(`[data-field="${CSS.escape(label)}"]`);
    if (!valueElement) continue;
    valueElement.textContent = formatValue(enrichedProduct[key]);
  }
  renderPicture(product.imageUrl);
  updateFindSourcesButtonState();
  updateNavigationState();
}

function renderCombinations(combinations) {
  if (!combinations.length) {
    setTableMessage("No combinations found for selected product.");
    return;
  }

  tableBody.replaceChildren(
    ...combinations.map((item) => {
      const row = document.createElement("div");
      row.className = "variant-row";
      row.setAttribute("role", "row");

      row.append(
        ...variantColumns.map((column) => {
          const value = item[column.key];
          const text = formatTableValue(value, column);
          const cell = document.createElement("div");
          cell.className = "variant-cell";
          cell.setAttribute("role", "cell");
          cell.textContent = text;
          cell.title = text;
          cell.dataset.column = column.key;
          cell.dataset.label = column.label;
          cell.style.textAlign = column.align || "";
          if ((column.problem && item.hasProblem) || (column.key === "eanStatus" && text)) {
            cell.classList.add("problem-ean");
          }
          return cell;
        }),
      );
      return row;
    }),
  );
}

function clearProductState(message) {
  clearLocalDraft();
  recordIdElement.textContent = "";
  currentProduct = null;
  currentCombinations = [];
  currentPosition = 1;
  totalPositions = 0;
  for (const valueElement of document.querySelectorAll(".field-value")) {
    valueElement.textContent = "";
  }
  renderPicture();
  setTableMessage(message);
  updateNavigationState();
}

function setTableMessage(message) {
  const row = document.createElement("div");
  row.className = "variant-row variant-message-row";
  row.setAttribute("role", "row");
  const cell = document.createElement("div");
  cell.className = "empty-row";
  cell.setAttribute("role", "cell");
  cell.style.gridColumn = "1 / -1";
  cell.textContent = message;
  row.append(cell);
  tableBody.replaceChildren(row);
}

function formatValue(value) {
  if (value === null || value === undefined) return "";
  if (typeof value === "number") return String(value);

  const text = String(value).trim();
  if (/^\d{4}-\d{2}-\d{2}T/.test(text)) return formatDate(text);
  if (/^-?\d+\.0+$/.test(text)) return String(Number.parseInt(text, 10));
  if (/^-?\d+\.\d+$/.test(text)) return trimDecimal(text);
  return text;
}

function formatTableValue(value, column) {
  if (value === null || value === undefined || value === "") return "";

  if (column.amount) {
    const amount = Number(value);
    if (Number.isFinite(amount)) return String(Math.round(amount));
  }

  return formatValue(value);
}

function formatRecordId(value) {
  const text = formatValue(value);
  return text || "";
}

function formatDate(value) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return new Intl.DateTimeFormat("cs-CZ", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    timeZone: "UTC",
  })
    .format(date)
    .replaceAll(" ", "");
}

function trimDecimal(value) {
  const asNumber = Number(value);
  if (!Number.isFinite(asNumber)) return value;
  return String(asNumber);
}

function setNavigationEnabled(enabled) {
  previousProductButton.disabled = !enabled;
  nextProductButton.disabled = !enabled;
}

function updateNavigationState() {
  previousProductButton.disabled = !manufacturerSelect.value || currentPosition <= 1;
  nextProductButton.disabled =
    !manufacturerSelect.value || (totalPositions > 0 && currentPosition >= totalPositions);
  loadProductButton.disabled = !currentProduct?.idProduct;
  findSourcesButton.disabled = !manufacturerSelect.value || !currentProduct;
  getPicturesButton.disabled = !manufacturerSelect.value || !currentProduct;
}

function normalizeScannedEan(value) {
  const czechKeyboardDigits = new Map([
    ["+", "1"],
    ["ě", "2"],
    ["š", "3"],
    ["č", "4"],
    ["ř", "5"],
    ["ž", "6"],
    ["ý", "7"],
    ["á", "8"],
    ["í", "9"],
    ["é", "0"],
  ]);

  return String(value ?? "")
    .trim()
    .toLowerCase()
    .split("")
    .map((char) => czechKeyboardDigits.get(char) || char)
    .join("")
    .replace(/\D/g, "");
}

function renderPicture(imageUrl) {
  if (!imageUrl) {
    pictureSlot.replaceChildren(Object.assign(document.createElement("span"), { textContent: "No picture" }));
    return;
  }

  const image = document.createElement("img");
  image.src = imageUrl;
  image.alt = currentProduct?.name || "Product preview";
  image.loading = "lazy";
  pictureSlot.replaceChildren(image);
}

async function loadProductInfoForCurrentProduct() {
  if (!currentProduct?.idProduct || !manufacturerSelect.value || !currentProduct.supplierReference) {
    return;
  }

  loadProductButton.disabled = true;
  setButtonLabel(loadProductButton, "Loading info...");

  try {
    const params = new URLSearchParams({
      productId: currentProduct.idProduct,
      manufacturerId: manufacturerSelect.value,
      supplierReference: currentProduct.supplierReference,
    });
    const response = await fetch(`/api/catalog-maker/product-info?${params.toString()}`);
    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.message || "Product info load failed.");
    }

    const infoByCatalogId = new Map(
      data.items.map((item) => [String(item.idProductCatalog), item]),
    );
    currentCombinations = currentCombinations.map((combination) => ({
      ...combination,
      ...(infoByCatalogId.get(String(combination.id)) || {}),
    }));
    renderCombinations(currentCombinations);
  } catch (error) {
    setTableMessage(error.message);
  } finally {
    setButtonLabel(loadProductButton, "Get product info - 00");
    updateNavigationState();
  }
}

export {};
