const DEFAULT_MODEL = "anita-next-24b-dolphin-mistral-uncensored-ita-i1";
const SETTINGS_STORAGE_KEY_PREFIX = "menu2.settings.v2";
const DEFAULT_DIRECT_HOST = "10.0.0.43";
const DEFAULT_DIRECT_PORT = "1234";
const DEFAULT_DIRECT_PATH = "/v1/chat/completions";
const DEFAULT_PROXY_BASE_PATH = "/api/";
const UI_THEME_STORAGE_KEY = "powerup.ui.theme";
const isAdminMode = new URLSearchParams(window.location.search).get("admin") === "1";
const CURRENT_URL = new URL(window.location.href);
const SESSION_TOKEN = CURRENT_URL.searchParams.get("session") || "";

const chatStage = document.getElementById("chatStage");
const chatLog = document.getElementById("chatLog");
const heroPanel = document.getElementById("heroPanel");
const chatToolbar = document.getElementById("chatToolbar");
const newChatButton = document.getElementById("newChatButton");
const promptInput = document.getElementById("promptInput");
const form = document.getElementById("chatForm");
const statusLabel = document.getElementById("statusLabel");
const portalChrome = document.getElementById("portalChrome");
const topbar = document.querySelector(".topbar");
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 dashboardLink = document.getElementById("dashboardLink");
const ordiniLink = document.getElementById("ordiniLink");
const prenotazioniLink = document.getElementById("prenotazioniLink");
const menuLink = document.getElementById("menuLink");
const homemadeLink = document.getElementById("homemadeLink");
const documentsLink = document.getElementById("documentsLink");

const settingsToggle = document.getElementById("settingsToggle");
const settingsClose = document.getElementById("settingsClose");
const settingsPanel = document.getElementById("settingsPanel");
const settingsOverlay = document.getElementById("settingsOverlay");
const venueSettingsSection = document.getElementById("venueSettingsSection");
const technicalSettingsSection = document.getElementById("technicalSettingsSection");
const salesFocusInput = document.getElementById("salesFocusInput");
const menuAssetInput = document.getElementById("menuAssetInput");
const menuCameraInput = document.getElementById("menuCameraInput");
const pickMenuAssetsButton = document.getElementById("pickMenuAssetsButton");
const captureMenuPhotoButton = document.getElementById("captureMenuPhotoButton");
const menuAssetStatus = document.getElementById("menuAssetStatus");
const menuAssetsList = document.getElementById("menuAssetsList");
const generatedAssetsContext = document.getElementById("generatedAssetsContext");
const editGeneratedPromptButton = document.getElementById("editGeneratedPromptButton");
const venueSettingsStatus = document.getElementById("venueSettingsStatus");
const saveVenueSettingsButton = document.getElementById("saveVenueSettingsButton");

const modelInput = document.getElementById("modelInput");
const hostInput = document.getElementById("hostInput");
const portInput = document.getElementById("portInput");
const pathInput = document.getElementById("pathInput");
const endpointPreview = document.getElementById("endpointPreview");
const temperatureInput = document.getElementById("temperatureInput");
const temperatureValue = document.getElementById("temperatureValue");
const systemPromptInput = document.getElementById("systemPromptInput");
const resetPromptButton = document.getElementById("resetPromptButton");

const sheetOverlay = document.getElementById("sheetOverlay");
const contentSheet = document.getElementById("contentSheet");
const sheetTitle = document.getElementById("sheetTitle");
const sheetBody = document.getElementById("sheetBody");
const sheetClose = document.getElementById("sheetClose");

const resourceButtons = document.querySelectorAll("[data-sheet]");
const canManageVenueSettings = Boolean(SESSION_TOKEN);

const conversation = [];
let assistantThreadId = null;
let interactionVersion = 0;
let currentRuntime = buildFallbackRuntime();
let currentSettingsStorageKey = buildSettingsStorageKey(currentRuntime.tenantSlug);
let defaultSettings = buildDefaultSettings(currentRuntime);
let promptManagedByPortal = false;
let generatedPromptEditMode = false;
let menuAccessDenied = false;

bootstrap();

async function bootstrap() {
  initializeUiTheme();
  bindEvents();
  refreshChatLayout();
  syncConfiguredTextareaHeights();

  statusLabel.textContent = "Carico configurazione menu...";
  await hydrateRuntimeFromPortal();
  hydrateVenueContent();
  loadSettingsIntoForm();
  syncPromptControls();
  syncManagementControls();
  if (menuAccessDenied) {
    statusLabel.textContent = "";
    return;
  }
  await hydrateMenuThreadFromPortal();
  statusLabel.textContent = "";
}

function buildFallbackRuntime() {
  const venueName = "il tuo locale";
  return {
    tenantSlug: "default",
    tenantName: venueName,
    venueName,
    assistantDescription:
      "Configura il focus vendita e carica i materiali del locale per personalizzare davvero il concierge del menu.",
    salesFocus: "",
    menuText: "",
    cocktailListText: "",
    uploadedAssetsContext: "",
    uploadedAssetsContextGenerated: "",
    assets: [],
    navigation: buildFallbackNavigation(),
    systemPrompt: buildMenuSystemPrompt({
      venueName,
      salesFocus: "",
      uploadedAssetsContext: ""
    })
  };
}

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);
}

async function hydrateRuntimeFromPortal() {
  const endpoint = buildApiUrl("llm/menu-settings");
  if (!SESSION_TOKEN || !endpoint) {
    currentRuntime = buildFallbackRuntime();
    currentSettingsStorageKey = buildSettingsStorageKey(currentRuntime.tenantSlug);
    defaultSettings = buildDefaultSettings(currentRuntime);
    promptManagedByPortal = false;
    return;
  }

  try {
    const response = await fetch(endpoint, {
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      }
    });
    if (response.status === 403) {
      setMenuAccessDenied(
        "Non hai accesso a Menu. Chiedi all'amministratore del locale di abilitare questa funzione dal pannello Account."
      );
      return;
    }
    if (!response.ok) {
      throw new Error(`Configurazione menu non disponibile (${response.status})`);
    }
    const payload = await response.json();
    currentRuntime = normalizeRemoteRuntime(payload);
    promptManagedByPortal = true;
  } catch (error) {
    console.warn("Impossibile caricare la configurazione menu dal portale", error);
    currentRuntime = buildFallbackRuntime();
    promptManagedByPortal = false;
  }

  currentSettingsStorageKey = buildSettingsStorageKey(currentRuntime.tenantSlug);
  defaultSettings = buildDefaultSettings(currentRuntime);
}

function normalizeRemoteRuntime(payload) {
  const venueName = readText(payload?.venue_name) || "il tuo locale";
  const tenantSlug = readText(payload?.tenant_slug) || venueName.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "default";
  const salesFocus = readText(payload?.sales_focus);
  const menuText = "";
  const cocktailListText = "";
  const uploadedAssetsContext = readText(payload?.uploaded_assets_context);
  const uploadedAssetsContextGenerated =
    readText(payload?.uploaded_assets_context_generated) || uploadedAssetsContext;
  const assistantDescription = readText(payload?.assistant_description) || buildAssistantDescription(venueName);
  const assets = Array.isArray(payload?.assets)
    ? payload.assets.map((item) => ({
        id: readText(item?.id),
        originalName: readText(item?.original_name),
        displayName: readText(item?.display_name),
        mimeType: readText(item?.mime_type),
        kind: readText(item?.kind),
        fileSizeBytes: Number(item?.file_size_bytes) || 0,
        status: readText(item?.status) || "ready",
        errorDetail: readText(item?.error_detail),
        analysisText: readText(item?.analysis_text),
        createdAt: readText(item?.created_at),
        updatedAt: readText(item?.updated_at)
      }))
    : [];
  const systemPrompt =
    readText(payload?.prompt_preview) ||
    buildMenuSystemPrompt({
      venueName,
      salesFocus,
      uploadedAssetsContext
    });

  return {
    tenantSlug,
    tenantName: readText(payload?.tenant_name) || venueName,
    venueName,
    assistantDescription,
    salesFocus,
    menuText,
    cocktailListText,
    uploadedAssetsContext,
    uploadedAssetsContextGenerated,
    assets,
    navigation: {
      dashboardUrl: readText(payload?.dashboard_url) || buildFallbackNavigation().dashboardUrl,
      documentsUrl: readText(payload?.documents_url) || buildFallbackNavigation().documentsUrl,
      ordiniUrl: readText(payload?.ordini_url) || buildFallbackNavigation().ordiniUrl,
      prenotazioniUrl: readText(payload?.prenotazioni_url) || buildFallbackNavigation().prenotazioniUrl,
      menuUrl: readText(payload?.menu_url) || buildCurrentMenuUrl(),
      homemadeUrl: readText(payload?.homemade_url) || buildFallbackNavigation().homemadeUrl
    },
    systemPrompt
  };
}

function readText(value) {
  return typeof value === "string" ? value.trim() : "";
}

function buildAssistantDescription(venueName) {
  return `Chiedi un consiglio su piatti, cocktail o abbinamenti di ${venueName}. L'assistente usa solo i contenuti configurati dal locale.`;
}

function buildFallbackNavigation() {
  return {
    dashboardUrl: "/dashboard",
    documentsUrl: "/documents",
    ordiniUrl: appendSessionParam("/ordini/"),
    prenotazioniUrl: "/modules/prenotazioni",
    menuUrl: buildCurrentMenuUrl(),
    homemadeUrl: "/modules/homemade"
  };
}

function appendSessionParam(url) {
  try {
    const parsedUrl = new URL(url, window.location.origin);
    if (SESSION_TOKEN) {
      parsedUrl.searchParams.set("session", SESSION_TOKEN);
    }
    return parsedUrl.toString();
  } catch {
    return url;
  }
}

function buildCurrentMenuUrl() {
  const currentMenuUrl = new URL(CURRENT_URL.pathname, CURRENT_URL.origin);
  CURRENT_URL.searchParams.forEach((value, key) => {
    currentMenuUrl.searchParams.set(key, value);
  });
  return currentMenuUrl.toString();
}

function buildMenuSystemPrompt({ venueName, salesFocus, uploadedAssetsContext }) {
  const sections = [
    "Sei il concierge digitale del menu del locale. Rispondi sempre in italiano, con tono curato, chiaro e orientato alla vendita assistita. Usa solo i dati realmente configurati dal locale. Non inventare piatti, drink, prezzi, ingredienti, disponibilita, promozioni o allergeni. Se un'informazione manca, dichiaralo in modo semplice e rimanda al personale del locale. Quando consigli cosa scegliere, proponi al massimo tre opzioni motivate.",
    "",
    `Locale: ${venueName}`
  ];

  if (salesFocus.trim()) {
    sections.push("", "Obiettivo vendita prioritario:", salesFocus.trim());
  }

  if (uploadedAssetsContext.trim()) {
    sections.push("", "Materiale del locale elaborato dai file caricati:", uploadedAssetsContext.trim());
  }

  if (!uploadedAssetsContext.trim()) {
    sections.push(
      "",
      "Dati non ancora configurati:",
      "Il menu del locale non e ancora stato caricato tramite file. Non inventare contenuti."
    );
  }

  return sections.join("\n");
}

function buildSettingsStorageKey(tenantSlug) {
  return `${SETTINGS_STORAGE_KEY_PREFIX}:${tenantSlug || "default"}`;
}

function buildApiUrl(path) {
  const apiBase = resolveApiBaseUrl();
  if (!apiBase) {
    return null;
  }

  try {
    return new URL(path.replace(/^\//, ""), ensureTrailingSlash(apiBase)).toString();
  } catch {
    return null;
  }
}

function resolveApiBaseUrl() {
  const rawValue = CURRENT_URL.searchParams.get("api");
  if (rawValue) {
    return rawValue;
  }

  if (typeof window === "undefined") {
    return "";
  }

  if (window.location.protocol !== "https:") {
    return "";
  }

  return new URL(DEFAULT_PROXY_BASE_PATH, window.location.origin).toString();
}

function ensureTrailingSlash(value) {
  return value.endsWith("/") ? value : `${value}/`;
}

function buildProxyEndpoint() {
  return buildApiUrl("llm/openai/chat/completions");
}

function buildManagedMenuChatEndpoint() {
  return buildApiUrl("llm/menu-chat");
}

function canUseManagedMenuChat() {
  return Boolean(SESSION_TOKEN && buildManagedMenuChatEndpoint());
}

function shouldUseHostedProxy() {
  return Boolean(buildProxyEndpoint());
}

function buildDefaultSettings(runtime) {
  const proxyEndpoint = buildProxyEndpoint();
  if (proxyEndpoint) {
    try {
      const url = new URL(proxyEndpoint);
      return {
        model: DEFAULT_MODEL,
        apiHost: `${url.protocol}//${url.hostname}`,
        apiPort: url.port || defaultPortForProtocol(url.protocol),
        apiPath: `${url.pathname}${url.search}`,
        temperature: "0.3",
        systemPrompt: runtime.systemPrompt
      };
    } catch {
      // Fallback to direct settings below.
    }
  }

  return {
    model: DEFAULT_MODEL,
    apiHost: DEFAULT_DIRECT_HOST,
    apiPort: DEFAULT_DIRECT_PORT,
    apiPath: DEFAULT_DIRECT_PATH,
    temperature: "0.3",
    systemPrompt: runtime.systemPrompt
  };
}

function bindEvents() {
  themeToggleButton?.addEventListener("click", toggleUiTheme);
  temperatureInput.addEventListener("input", () => {
    temperatureValue.textContent = temperatureInput.value;
    persistSettings();
  });

  [modelInput, hostInput, portInput, pathInput, systemPromptInput].forEach((node) => {
    node.addEventListener("input", () => {
      persistSettings();
    });
  });

  endpointPreview.addEventListener("input", () => {
    syncFieldsFromEndpoint();
    persistSettings();
  });

  promptInput.addEventListener("input", () => {
    syncTextareaHeight(promptInput, 220);
  });

  salesFocusInput.addEventListener("input", () => {
    syncTextareaHeight(salesFocusInput, 180);
  });

  generatedAssetsContext.addEventListener("input", () => {
    syncTextareaHeight(generatedAssetsContext, generatedPromptEditMode ? 520 : 240);
  });

  promptInput.addEventListener("keydown", (event) => {
    if (event.key === "Enter" && !event.shiftKey) {
      event.preventDefault();
      form.requestSubmit();
    }
  });

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const prompt = promptInput.value.trim();
    if (!prompt) {
      return;
    }

    interactionVersion += 1;
    const requestVersion = interactionVersion;
    const priorConversation = conversation.slice(-24);
    revealConversation();
    addMessage("user", prompt);
    conversation.push({ role: "user", content: prompt });
    promptInput.value = "";
    syncTextareaHeight(promptInput, 220);
    promptInput.focus();

    let assistantMessageEl;
    let finalReply = "";

    try {
      toggleFormState(true, "Sto pensando...");
      assistantMessageEl = addMessage("assistant", "");
      if (canUseManagedMenuChat()) {
        finalReply = await requestManagedMenuReply(prompt, priorConversation);
      } else {
        finalReply = await streamLocalModel((chunk) => {
          if (requestVersion !== interactionVersion) {
            return;
          }
          if (!assistantMessageEl) {
            return;
          }
          assistantMessageEl.textContent = (assistantMessageEl.textContent + chunk).trimStart();
        });
      }

      if (requestVersion !== interactionVersion) {
        return;
      }
      if (finalReply) {
        const normalizedReply = finalReply.trim();
        assistantMessageEl.textContent = normalizedReply;
        conversation.push({ role: "assistant", content: normalizedReply });
        refreshChatLayout();
      } else {
        assistantMessageEl.textContent =
          "Non ho ricevuto una risposta dal modello. Controlla la configurazione del servizio e riprova.";
      }
    } catch (error) {
      if (requestVersion !== interactionVersion) {
        return;
      }
      console.error(error);
      const errorMessage = formatModelError(error);
      if (assistantMessageEl) {
        assistantMessageEl.textContent = errorMessage;
      } else {
        addMessage("assistant", errorMessage);
      }
    } finally {
      if (requestVersion === interactionVersion) {
        toggleFormState(false);
      }
    }
  });

  if (newChatButton) {
    newChatButton.addEventListener("click", () => {
      void handleClearConversation();
    });
  }

  settingsToggle.addEventListener("click", () => {
    setSettingsOpen(true);
  });

  settingsClose.addEventListener("click", () => {
    setSettingsOpen(false);
  });

  settingsOverlay.addEventListener("click", () => {
    setSettingsOpen(false);
  });

  pickMenuAssetsButton.addEventListener("click", () => {
    menuAssetInput.click();
  });

  captureMenuPhotoButton.addEventListener("click", () => {
    menuCameraInput.click();
  });

  menuAssetInput.addEventListener("change", (event) => {
    void uploadMenuAssets(event.target.files);
  });

  menuCameraInput.addEventListener("change", (event) => {
    void uploadMenuAssets(event.target.files, { source: "camera" });
  });

  saveVenueSettingsButton.addEventListener("click", () => {
    void saveVenueSettings();
  });

  editGeneratedPromptButton.addEventListener("click", () => {
    generatedPromptEditMode = !generatedPromptEditMode;
    syncGeneratedPromptEditor();
    if (generatedPromptEditMode) {
      generatedAssetsContext.focus();
      generatedAssetsContext.setSelectionRange(
        generatedAssetsContext.value.length,
        generatedAssetsContext.value.length
      );
    }
  });

  resetPromptButton.addEventListener("click", () => {
    if (promptManagedByPortal) {
      return;
    }
    systemPromptInput.value = defaultSettings.systemPrompt;
    persistSettings();
  });

  resourceButtons.forEach((button) => {
    button.addEventListener("click", () => {
      openContentSheet(button.dataset.sheet);
    });
  });

  sheetClose.addEventListener("click", closeContentSheet);
  sheetOverlay.addEventListener("click", closeContentSheet);

  window.addEventListener("keydown", (event) => {
    if (event.key === "Escape") {
      closeContentSheet();
      setSettingsOpen(false);
    }
  });
}

function hydrateVenueContent() {
  document.title = `${currentRuntime.venueName} | Menu Assistant`;
  document.getElementById("assistantDescription").textContent = currentRuntime.assistantDescription;
  topbarTitle.textContent = currentRuntime.tenantName || currentRuntime.venueName;
  topbarSubtitle.textContent = `Concierge digitale di ${currentRuntime.venueName} integrato nel gestionale.`;
  tenantNameLabel.textContent = currentRuntime.venueName;
  tenantMetaLabel.textContent = "Concierge menu integrato nel gestionale";
  dashboardLink.href = currentRuntime.navigation.dashboardUrl;
  documentsLink.href = currentRuntime.navigation.documentsUrl;
  ordiniLink.href = appendSessionParam(currentRuntime.navigation.ordiniUrl);
  prenotazioniLink.href = currentRuntime.navigation.prenotazioniUrl;
  menuLink.href = buildCurrentMenuUrl();
  homemadeLink.href = currentRuntime.navigation.homemadeUrl;
}

function loadSettingsIntoForm() {
  const savedSettings = loadSavedSettings();

  salesFocusInput.value = currentRuntime.salesFocus || "";
  generatedAssetsContext.value = currentRuntime.uploadedAssetsContext || "";
  generatedPromptEditMode = false;
  modelInput.value = savedSettings.model;
  hostInput.value = savedSettings.apiHost;
  portInput.value = savedSettings.apiPort;
  pathInput.value = savedSettings.apiPath;
  temperatureInput.value = savedSettings.temperature;
  temperatureValue.textContent = savedSettings.temperature;
  systemPromptInput.value = savedSettings.systemPrompt;

  renderMenuAssets();
  updateEndpointPreview();
  syncConfiguredTextareaHeights();
  syncGeneratedPromptEditor();
}

function loadSavedSettings() {
  try {
    const raw = localStorage.getItem(currentSettingsStorageKey);
    if (!raw) {
      return defaultSettings;
    }

    const parsed = JSON.parse(raw);
    const mergedSettings = {
      ...defaultSettings,
      ...parsed
    };

    if (promptManagedByPortal) {
      mergedSettings.systemPrompt = defaultSettings.systemPrompt;
    } else {
      mergedSettings.systemPrompt = readText(parsed.systemPrompt) || defaultSettings.systemPrompt;
    }

    if (!shouldUseHostedProxy()) {
      return mergedSettings;
    }

    const endpoint = buildEndpoint(mergedSettings);
    if (!/^http:\/\//i.test(endpoint)) {
      return mergedSettings;
    }

    return {
      ...mergedSettings,
      apiHost: defaultSettings.apiHost,
      apiPort: defaultSettings.apiPort,
      apiPath: defaultSettings.apiPath
    };
  } catch (error) {
    console.warn("Impossibile leggere le impostazioni salvate", error);
    return defaultSettings;
  }
}

function persistSettings() {
  const settings = getSettingsFromForm();
  temperatureValue.textContent = settings.temperature;
  endpointPreview.value = buildEndpoint(settings);

  try {
    localStorage.setItem(currentSettingsStorageKey, JSON.stringify(settings));
  } catch (error) {
    console.warn("Impossibile salvare le impostazioni", error);
  }
}

function getSettingsFromForm() {
  return {
    model: modelInput.value.trim() || DEFAULT_MODEL,
    apiHost: hostInput.value.trim() || defaultSettings.apiHost,
    apiPort: portInput.value.trim() || defaultSettings.apiPort,
    apiPath: normalizePath(pathInput.value.trim() || defaultSettings.apiPath),
    temperature: temperatureInput.value,
    systemPrompt: promptManagedByPortal
      ? defaultSettings.systemPrompt
      : systemPromptInput.value.trim() || defaultSettings.systemPrompt
  };
}

function syncPromptControls() {
  systemPromptInput.disabled = promptManagedByPortal;
  systemPromptInput.placeholder = promptManagedByPortal
    ? "Prompt gestito dal portale"
    : "Prompt usato dal locale";
  resetPromptButton.hidden = promptManagedByPortal;
}

function syncManagementControls() {
  const canOpenSettings = isAdminMode || canManageVenueSettings;
  portalChrome.hidden = !canOpenSettings;
  topbar.hidden = !canOpenSettings;
  settingsToggle.hidden = !canOpenSettings;
  settingsToggle.textContent = canManageVenueSettings ? "Configura menu" : "Impostazioni";
  venueSettingsSection.hidden = !canManageVenueSettings;
  technicalSettingsSection.hidden = !isAdminMode;
}

function setMenuAccessDenied(message) {
  menuAccessDenied = true;
  heroPanel.hidden = false;
  chatLog.hidden = true;
  chatToolbar.hidden = true;
  form.hidden = true;
  settingsToggle.hidden = true;
  portalChrome.hidden = true;
  topbar.hidden = false;
  topbarTitle.textContent = "Accesso non autorizzato";
  topbarSubtitle.textContent = "Questo account non puo usare il modulo Menu";
  heroPanel.innerHTML = `
    <div class="hero-copy">
      <p class="eyebrow">Menu</p>
      <h1>Funzione non disponibile</h1>
      <p>${message}</p>
    </div>
  `;
  statusLabel.textContent = "";
}

function syncGeneratedPromptEditor() {
  const canEdit = canManageVenueSettings;
  if (!canEdit) {
    editGeneratedPromptButton.hidden = true;
    generatedAssetsContext.readOnly = true;
    generatedAssetsContext.classList.remove("is-expanded");
    return;
  }

  editGeneratedPromptButton.hidden = false;
  generatedAssetsContext.readOnly = !generatedPromptEditMode;
  generatedAssetsContext.classList.toggle("is-expanded", generatedPromptEditMode);
  editGeneratedPromptButton.textContent = generatedPromptEditMode ? "Chiudi modifica" : "Modifica";
  syncTextareaHeight(generatedAssetsContext, generatedPromptEditMode ? 520 : 240);
}

function applyRemoteRuntimePayload(payload) {
  currentRuntime = normalizeRemoteRuntime(payload);
  promptManagedByPortal = true;
  defaultSettings = buildDefaultSettings(currentRuntime);
  hydrateVenueContent();
  loadSettingsIntoForm();
  syncPromptControls();
  syncManagementControls();
}

function setMenuAssetStatus(message, state = "") {
  menuAssetStatus.textContent = message;
  menuAssetStatus.dataset.state = state;
}

function formatFileSize(bytes) {
  const size = Number(bytes) || 0;
  if (size >= 1024 * 1024) {
    return `${(size / (1024 * 1024)).toFixed(1)} MB`;
  }
  if (size >= 1024) {
    return `${Math.round(size / 1024)} KB`;
  }
  return `${size} B`;
}

function formatAssetKind(kind) {
  const kindMap = {
    image: "Foto",
    pdf: "PDF",
    text: "Testo"
  };
  return kindMap[kind] || "File";
}

function formatAssetStatus(status) {
  const statusMap = {
    ready: "Pronto",
    processing: "Analisi in corso",
    error: "Errore"
  };
  return statusMap[status] || "Disponibile";
}

function renderMenuAssets() {
  menuAssetsList.replaceChildren();

  const assets = Array.isArray(currentRuntime.assets) ? currentRuntime.assets : [];
  if (!assets.length) {
    const emptyState = document.createElement("div");
    emptyState.className = "asset-empty";
    emptyState.textContent = "Nessun file caricato. Puoi aggiungere menu scannerizzati, foto o PDF del locale.";
    menuAssetsList.appendChild(emptyState);
    return;
  }

  assets.forEach((asset) => {
    const card = document.createElement("article");
    card.className = `asset-card is-${asset.status || "ready"}`;

    const head = document.createElement("div");
    head.className = "asset-card-head";

    const titleWrap = document.createElement("div");
    titleWrap.className = "asset-card-title";

    const title = document.createElement("strong");
    title.textContent = asset.displayName || asset.originalName || "Materiale caricato";

    const meta = document.createElement("p");
    meta.className = "asset-meta";
    meta.textContent = `${formatAssetKind(asset.kind)} · ${formatFileSize(asset.fileSizeBytes)}`;

    titleWrap.append(title, meta);

    const statusPill = document.createElement("span");
    statusPill.className = "asset-status-pill";
    statusPill.dataset.state = asset.status || "ready";
    statusPill.textContent = formatAssetStatus(asset.status);

    head.append(titleWrap, statusPill);

    const renameRow = document.createElement("div");
    renameRow.className = "asset-rename-row";

    const renameInput = document.createElement("input");
    renameInput.type = "text";
    renameInput.value = asset.displayName || asset.originalName || "";
    renameInput.className = "asset-rename-input";

    const renameButton = document.createElement("button");
    renameButton.type = "button";
    renameButton.className = "asset-action-button";
    renameButton.textContent = "Rinomina";
    renameButton.addEventListener("click", () => {
      void renameMenuAsset(asset.id, renameInput.value, renameButton, deleteButton);
    });

    renameRow.append(renameInput, renameButton);

    const summary = document.createElement("p");
    summary.className = "asset-summary";
    if (asset.status === "error") {
      summary.textContent = asset.errorDetail || "Il file e stato caricato ma non sono riuscito ad analizzarlo.";
      summary.dataset.state = "error";
    } else if (asset.status === "processing") {
      summary.textContent = "Analisi del file in corso.";
    } else {
      summary.textContent = asset.analysisText || "Materiale analizzato e pronto per il prompt.";
    }

    const actions = document.createElement("div");
    actions.className = "asset-actions";

    const openButton = document.createElement("button");
    openButton.type = "button";
    openButton.className = "asset-action-button";
    openButton.textContent = "Apri";
    openButton.addEventListener("click", () => {
      void openMenuAsset(asset.id, asset.displayName || asset.originalName);
    });

    const deleteButton = document.createElement("button");
    deleteButton.type = "button";
    deleteButton.className = "asset-action-button is-danger";
    deleteButton.textContent = "Elimina";
    deleteButton.addEventListener("click", () => {
      void deleteMenuAsset(asset.id, asset.displayName || asset.originalName, deleteButton, renameButton);
    });

    actions.append(openButton, deleteButton);
    card.append(head, renameRow, summary, actions);
    menuAssetsList.appendChild(card);
  });
}

async function uploadMenuAssets(fileList, options = {}) {
  const files = Array.from(fileList || []);
  if (!canManageVenueSettings || !files.length) {
    menuAssetInput.value = "";
    menuCameraInput.value = "";
    return;
  }

  const source = options.source === "camera" ? "camera" : "files";

  const endpoint = buildApiUrl("llm/menu-settings/assets");
  if (!endpoint) {
    setMenuAssetStatus("Endpoint upload materiali non disponibile.", "error");
    menuAssetInput.value = "";
    menuCameraInput.value = "";
    return;
  }

  if (source === "camera") {
    setMenuAssetStatus(files.length === 1 ? "Carico e analizzo la foto appena scattata..." : `Carico e analizzo ${files.length} foto appena scattate...`);
  } else {
    setMenuAssetStatus(files.length === 1 ? "Carico e analizzo 1 file..." : `Carico e analizzo ${files.length} file...`);
  }
  pickMenuAssetsButton.disabled = true;
  captureMenuPhotoButton.disabled = true;

  try {
    const formData = new FormData();
    files.forEach((file) => {
      formData.append("files", file, file.name);
    });

    const response = await fetch(endpoint, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      },
      body: formData
    });

    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }

    const payload = await response.json();
    applyRemoteRuntimePayload(payload);
    setMenuAssetStatus(
      source === "camera"
        ? files.length === 1
          ? "Foto caricata. Il prompt del menu e stato aggiornato con l'analisi dello scatto."
          : "Foto caricate. Il prompt del menu e stato aggiornato con l'analisi degli scatti."
        : files.length === 1
          ? "Materiale caricato. Il prompt del menu e stato aggiornato con l'analisi del file."
          : "Materiali caricati. Il prompt del menu e stato aggiornato con l'analisi dei file.",
      "success"
    );
  } catch (error) {
    setMenuAssetStatus(error instanceof Error ? error.message : "Caricamento materiali non riuscito.", "error");
  } finally {
    pickMenuAssetsButton.disabled = false;
    captureMenuPhotoButton.disabled = false;
    menuAssetInput.value = "";
    menuCameraInput.value = "";
  }
}

async function renameMenuAsset(assetId, nextName, ...buttons) {
  const endpoint = buildApiUrl(`llm/menu-settings/assets/${encodeURIComponent(assetId)}`);
  if (!endpoint) {
    setMenuAssetStatus("Endpoint rename materiali non disponibile.", "error");
    return;
  }

  const trimmedName = nextName.trim();
  if (!trimmedName) {
    setMenuAssetStatus("Inserisci un nome file valido prima di rinominare.", "error");
    return;
  }

  buttons.forEach((button) => {
    if (button) {
      button.disabled = true;
    }
  });
  setMenuAssetStatus("Rinomino il file selezionato...");

  try {
    const response = await fetch(endpoint, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${SESSION_TOKEN}`
      },
      body: JSON.stringify({ display_name: trimmedName })
    });

    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }

    const payload = await response.json();
    applyRemoteRuntimePayload(payload);
    setMenuAssetStatus("File rinominato.", "success");
  } catch (error) {
    setMenuAssetStatus(error instanceof Error ? error.message : "Rinomina file non riuscita.", "error");
  } finally {
    buttons.forEach((button) => {
      if (button) {
        button.disabled = false;
      }
    });
  }
}

async function deleteMenuAsset(assetId, assetName, ...buttons) {
  if (!window.confirm(`Eliminare ${assetName}?`)) {
    return;
  }

  const endpoint = buildApiUrl(`llm/menu-settings/assets/${encodeURIComponent(assetId)}`);
  if (!endpoint) {
    setMenuAssetStatus("Endpoint eliminazione materiali non disponibile.", "error");
    return;
  }

  buttons.forEach((button) => {
    if (button) {
      button.disabled = true;
    }
  });
  setMenuAssetStatus("Elimino il file selezionato...");

  try {
    const response = await fetch(endpoint, {
      method: "DELETE",
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      }
    });

    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }

    const payload = await response.json();
    applyRemoteRuntimePayload(payload);
    setMenuAssetStatus("File eliminato.", "success");
  } catch (error) {
    setMenuAssetStatus(error instanceof Error ? error.message : "Eliminazione file non riuscita.", "error");
  } finally {
    buttons.forEach((button) => {
      if (button) {
        button.disabled = false;
      }
    });
  }
}

async function openMenuAsset(assetId, assetName) {
  const endpoint = buildApiUrl(`llm/menu-settings/assets/${encodeURIComponent(assetId)}/download`);
  if (!endpoint) {
    setMenuAssetStatus("Endpoint apertura file non disponibile.", "error");
    return;
  }

  setMenuAssetStatus(`Apro ${assetName}...`);

  try {
    const response = await fetch(endpoint, {
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      }
    });

    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }

    const blob = await response.blob();
    const objectUrl = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = objectUrl;
    link.target = "_blank";
    link.rel = "noreferrer";
    document.body.appendChild(link);
    link.click();
    link.remove();
    window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000);
    setMenuAssetStatus(`File aperto: ${assetName}.`, "success");
  } catch (error) {
    setMenuAssetStatus(error instanceof Error ? error.message : "Apertura file non riuscita.", "error");
  }
}

async function readErrorDetail(response) {
  const fallback = `Richiesta fallita (${response.status})`;
  try {
    const rawText = await response.text();
    if (!rawText.trim()) {
      return fallback;
    }

    try {
      const payload = JSON.parse(rawText);
      if (payload && typeof payload.detail === "string" && payload.detail.trim()) {
        return payload.detail.trim();
      }
    } catch {
      // Ignore JSON parsing errors and return the raw text body below.
    }

    return rawText.trim() || fallback;
  } catch {
    return fallback;
  }
}

async function saveVenueSettings() {
  if (!canManageVenueSettings) {
    return;
  }

  const endpoint = buildApiUrl("llm/menu-settings");
  if (!endpoint) {
    venueSettingsStatus.textContent = "Endpoint impostazioni menu non disponibile.";
    venueSettingsStatus.dataset.state = "error";
    return;
  }

  venueSettingsStatus.textContent = "Salvataggio configurazione locale...";
  venueSettingsStatus.dataset.state = "";
  saveVenueSettingsButton.disabled = true;

  try {
    const generatedContext = (currentRuntime.uploadedAssetsContextGenerated || "").trim();
    const editedContext = generatedAssetsContext.value.trim();
    const uploadedAssetsContextOverride =
      editedContext && editedContext !== generatedContext ? editedContext : "";

    const response = await fetch(endpoint, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${SESSION_TOKEN}`
      },
      body: JSON.stringify({
        sales_focus: salesFocusInput.value.trim(),
        menu_text: "",
        cocktail_list_text: "",
        uploaded_assets_context_override: uploadedAssetsContextOverride
      })
    });

    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }

    const payload = await response.json();
    applyRemoteRuntimePayload(payload);
    generatedPromptEditMode = false;
    syncGeneratedPromptEditor();
    venueSettingsStatus.textContent = "Configurazione locale salvata. Il modulo usera subito il nuovo prompt.";
    venueSettingsStatus.dataset.state = "success";
  } catch (error) {
    venueSettingsStatus.textContent =
      error instanceof Error ? error.message : "Salvataggio configurazione locale non riuscito.";
    venueSettingsStatus.dataset.state = "error";
  } finally {
    saveVenueSettingsButton.disabled = false;
  }
}

function updateEndpointPreview() {
  endpointPreview.value = buildEndpoint(getSettingsFromForm());
}

function syncFieldsFromEndpoint() {
  const endpoint = endpointPreview.value.trim();
  if (!endpoint) {
    return;
  }

  try {
    const url = new URL(endpoint);
    hostInput.value = `${url.protocol}//${url.hostname}`;
    portInput.value = url.port || defaultPortForProtocol(url.protocol);
    pathInput.value = `${url.pathname}${url.search}`;
  } catch {
    // Manteniamo il valore libero se l'URL e' ancora in fase di scrittura.
  }
}

function buildEndpoint(settings) {
  const host = settings.apiHost.trim();
  const port = settings.apiPort.trim();
  const path = normalizePath(settings.apiPath);

  if (/^https?:\/\//i.test(host)) {
    const normalizedHost = host.replace(/\/$/, "");
    const hasPort = /:\d+$/.test(normalizedHost);
    return `${normalizedHost}${!hasPort && port ? `:${port}` : ""}${path}`;
  }

  if (!host) {
    return path;
  }

  return `http://${host}:${port}${path}`;
}

function normalizePath(path) {
  return path.startsWith("/") ? path : `/${path}`;
}

function defaultPortForProtocol(protocol) {
  if (protocol === "https:") {
    return "443";
  }
  return "80";
}

function revealConversation() {
  heroPanel.hidden = true;
  chatLog.hidden = false;
  refreshChatLayout();
}

function hideConversation() {
  heroPanel.hidden = false;
  chatLog.hidden = true;
  refreshChatLayout();
}

function refreshChatLayout() {
  const hasMessages = chatLog.childElementCount > 0;
  chatStage.classList.toggle("is-empty", !hasMessages);
  if (chatToolbar) {
    chatToolbar.hidden = !hasMessages;
  }
}

function syncConfiguredTextareaHeights() {
  syncTextareaHeight(promptInput, 220);
  syncTextareaHeight(salesFocusInput, 180);
  syncTextareaHeight(generatedAssetsContext, generatedPromptEditMode ? 520 : 240);
}

function syncTextareaHeight(textarea, maxHeight = 220) {
  if (!textarea) {
    return;
  }
  textarea.style.height = "auto";
  textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
  textarea.style.overflowY = textarea.scrollHeight > maxHeight ? "auto" : "hidden";
}

function addMessage(role, content, options = {}) {
  const message = document.createElement("div");
  message.className = `message ${role}`;
  message.textContent = content;
  chatLog.appendChild(message);
  if (options.scroll !== false) {
    chatLog.scrollTo({ top: chatLog.scrollHeight, behavior: "smooth" });
  }
  refreshChatLayout();
  return message;
}

function renderConversationHistory() {
  chatLog.replaceChildren();
  if (!conversation.length) {
    hideConversation();
    return;
  }

  revealConversation();
  conversation.forEach((message) => {
    if (!message || !message.role || !message.content) {
      return;
    }
    addMessage(message.role, message.content, { scroll: false });
  });
  chatLog.scrollTop = chatLog.scrollHeight;
}

async function hydrateMenuThreadFromPortal() {
  if (menuAccessDenied) {
    return;
  }
  if (!canUseManagedMenuChat()) {
    assistantThreadId = null;
    return;
  }

  const endpoint = buildApiUrl("llm/chat/thread?surface=menu");
  if (!endpoint) {
    return;
  }

  try {
    const response = await fetch(endpoint, {
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      }
    });
    if (response.status === 403) {
      setMenuAccessDenied(
        "Non hai accesso a Menu. Chiedi all'amministratore del locale di abilitare questa funzione dal pannello Account."
      );
      return;
    }
    if (!response.ok) {
      throw new Error(await readErrorDetail(response));
    }
    const payload = await response.json();
    assistantThreadId = typeof payload?.thread_id === "string" ? payload.thread_id : null;
    conversation.splice(0, conversation.length);
    const messages = Array.isArray(payload?.messages) ? payload.messages : [];
    messages.forEach((message) => {
      const role = readText(message?.role);
      const content = readText(message?.content);
      if (!role || !content) {
        return;
      }
      conversation.push({ role, content });
    });
    renderConversationHistory();
  } catch (error) {
    console.warn("Impossibile caricare il thread assistente menu", error);
  }
}

async function requestManagedMenuReply(prompt, priorConversation) {
  const endpoint = buildManagedMenuChatEndpoint();
  if (!endpoint) {
    throw new Error("Endpoint assistente menu non disponibile.");
  }

  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${SESSION_TOKEN}`
    },
    body: JSON.stringify({
      message: prompt,
      thread_id: assistantThreadId,
      conversation: priorConversation
    })
  });

  if (response.status === 403) {
    setMenuAccessDenied(
      "Non hai accesso a Menu. Chiedi all'amministratore del locale di abilitare questa funzione dal pannello Account."
    );
    throw new Error("Accesso a Menu non autorizzato.");
  }

  if (!response.ok) {
    throw new Error(await readErrorDetail(response));
  }

  const payload = await response.json();
  assistantThreadId = readText(payload?.thread_id) || assistantThreadId;
  return readText(payload?.reply);
}

async function clearManagedMenuThread() {
  if (!canUseManagedMenuChat()) {
    assistantThreadId = null;
    return;
  }

  const endpoint = buildApiUrl("llm/chat/thread?surface=menu");
  if (!endpoint) {
    assistantThreadId = null;
    return;
  }

  const targetThreadId = assistantThreadId;
  assistantThreadId = null;
  try {
    const url = new URL(endpoint);
    if (targetThreadId) {
      url.searchParams.set("thread_id", targetThreadId);
    }
    const response = await fetch(url.toString(), {
      method: "DELETE",
      headers: {
        Authorization: `Bearer ${SESSION_TOKEN}`
      }
    });
    if (!response.ok && response.status !== 204) {
      throw new Error(await readErrorDetail(response));
    }
  } catch (error) {
    console.warn("Impossibile azzerare il thread assistente menu", error);
  }
}

async function handleClearConversation() {
  interactionVersion += 1;
  conversation.splice(0, conversation.length);
  chatLog.replaceChildren();
  promptInput.value = "";
  syncTextareaHeight(promptInput, 220);
  toggleFormState(false);
  statusLabel.textContent = "";
  await clearManagedMenuThread();
  renderConversationHistory();
  promptInput.focus();
}

function toggleFormState(disabled, statusText = "") {
  form.querySelectorAll("textarea, button").forEach((node) => {
    node.disabled = disabled;
  });
  statusLabel.textContent = statusText;
}

function setSettingsOpen(isOpen) {
  if (!(isAdminMode || canManageVenueSettings) && isOpen) {
    return;
  }

  settingsPanel.hidden = !isOpen;
  settingsOverlay.hidden = !isOpen;
}

function openContentSheet(kind) {
  const generatedBody = currentRuntime.uploadedAssetsContext || "Il materiale del locale non e ancora stato configurato.";
  const contentMap = {
    menu: {
      title: "Menu del locale",
      body: generatedBody
    },
    wines: {
      title: "Cocktail list",
      body: generatedBody
    }
  };

  const selected = contentMap[kind];
  if (!selected) {
    return;
  }

  sheetTitle.textContent = selected.title;
  sheetBody.textContent = selected.body;
  contentSheet.hidden = false;
  sheetOverlay.hidden = false;
}

function closeContentSheet() {
  contentSheet.hidden = true;
  sheetOverlay.hidden = true;
}

function formatModelError(error) {
  if (!isAdminMode) {
    return "Il servizio assistente non e disponibile in questo momento. Riprova tra poco o chiedi supporto al personale.";
  }

  const settings = getSettingsFromForm();
  const endpoint = buildEndpoint(settings);
  const rawMessage = error instanceof Error ? error.message : String(error);
  const hints = [];

  if (/Failed to fetch/i.test(rawMessage)) {
    hints.push(`Il browser non riesce a raggiungere ${endpoint}.`);
    hints.push("Controlla che il servizio LLM sia attivo e che host, porta e percorso siano corretti.");

    if (location.protocol === "https:" && /^http:\/\//i.test(endpoint)) {
      hints.push(
        "La pagina e in HTTPS e il browser blocca endpoint HTTP come mixed content. Usa il proxy della piattaforma o un endpoint HTTPS."
      );
    }

    if (location.protocol === "file:") {
      hints.push("La pagina sembra aperta come file locale. In questo caso e meglio servirla via http locale.");
    }
  }

  if (/CORS|preflight|origin/i.test(rawMessage)) {
    hints.push("Sembra un problema di CORS o di preflight OPTIONS.");
  }

  if (/404/.test(rawMessage)) {
    hints.push("L'endpoint risponde ma il percorso potrebbe essere sbagliato.");
  }

  if (/401|403/.test(rawMessage)) {
    hints.push("L'endpoint richiede autorizzazione oppure sta rifiutando la richiesta.");
  }

  const parts = [
    "Errore nella chiamata al modello.",
    `Dettaglio: ${rawMessage}.`,
    `Endpoint usato: ${endpoint}.`
  ];

  if (hints.length) {
    parts.push(`Controlli rapidi: ${hints.join(" ")}`);
  }

  return parts.join(" ");
}

async function queryLocalModel() {
  const settings = getSettingsFromForm();
  const endpoint = buildEndpoint(settings);

  const payload = {
    model: settings.model,
    temperature: parseFloat(settings.temperature),
    messages: [{ role: "system", content: settings.systemPrompt }, ...conversation]
  };

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

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Errore API: ${response.status} ${errorText}`);
  }

  const data = await response.json();
  const assistantMessage = data?.choices?.[0]?.message?.content || data?.choices?.[0]?.text || null;

  return assistantMessage?.trim();
}

async function streamLocalModel(onChunk) {
  const settings = getSettingsFromForm();
  const endpoint = buildEndpoint(settings);

  const payload = {
    model: settings.model,
    temperature: parseFloat(settings.temperature),
    stream: true,
    messages: [{ role: "system", content: settings.systemPrompt }, ...conversation]
  };

  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "text/event-stream"
    },
    body: JSON.stringify(payload)
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Errore API: ${response.status} ${errorText}`);
  }

  const reader = response.body?.getReader();
  if (!reader) {
    return queryLocalModel();
  }

  const decoder = new TextDecoder("utf-8");
  let buffer = "";
  let fullReply = "";

  while (true) {
    const { value, done } = await reader.read();
    if (done) {
      break;
    }

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const rawLine of lines) {
      const line = rawLine.trim();
      if (!line || line === "data:" || !line.startsWith("data:")) {
        continue;
      }

      const dataString = line.replace(/^data:\s*/, "");
      if (dataString === "[DONE]") {
        return fullReply;
      }

      try {
        const parsed = JSON.parse(dataString);
        const delta = parsed?.choices?.[0]?.delta?.content || parsed?.choices?.[0]?.text || "";

        if (delta) {
          fullReply += delta;
          onChunk(delta);
        }
      } catch (error) {
        console.warn("Errore parsing chunk", error, dataString);
      }
    }
  }

  return fullReply.trim();
}
