// ── Native Settings Panels ────────────────────────────────────────────
// Renders Config, Debug, and Logs natively inside the page overlay.
// No iframe, no SPA proxy — uses gateway RPC over WebSocket.

// ── Shared helpers ────────────────────────────────────────────────────

function escapeSettingsHtml(s) {
  if (!s) {
    return "";
  }
  return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

/** Create or get the native content container inside the page overlay. */
function getSettingsContainer() {
  let el = document.getElementById("native-settings-container");
  if (!el) {
    const overlay = document.querySelector("body > .page-overlay");
    if (!overlay) {
      return null;
    }
    el = document.createElement("div");
    el.id = "native-settings-container";
    overlay.appendChild(el);
  }
  return el;
}

/** Show native container, hide iframe. */
function showSettingsContainer() {
  const container = getSettingsContainer();
  const frame = document.getElementById("page-overlay-frame");
  if (frame) {
    frame.style.display = "none";
  }
  if (container) {
    container.style.display = "flex";
  }
  return container;
}

/** Hide native container, restore iframe. */
function hideSettingsContainer() {
  const container = document.getElementById("native-settings-container");
  if (container) {
    container.style.display = "none";
    container.innerHTML = "";
  }
  const frame = document.getElementById("page-overlay-frame");
  if (frame) {
    frame.style.display = "";
  }
}

// ══════════════════════════════════════════════════════════════════════
// CONFIG PANEL
// ══════════════════════════════════════════════════════════════════════

let configData = null;
let configBaseHash = null;
let configEditMode = false;

async function fetchConfig() {
  if (!window.gateway?.connected) {
    return null;
  }
  try {
    const result = await window.gateway.rpc("config.get", {});
    return result;
  } catch (err) {
    console.error("[settings:config] fetch error:", err);
    return null;
  }
}

function renderConfigValue(key, value, depth) {
  const indent = depth * 1;
  if (value === null || value === undefined) {
    return `<span class="cfg-null">null</span>`;
  }
  if (typeof value === "boolean") {
    return `<span class="cfg-bool">${value}</span>`;
  }
  if (typeof value === "number") {
    return `<span class="cfg-num">${value}</span>`;
  }
  if (typeof value === "string") {
    if (value.length > 120) {
      return `<span class="cfg-str">"${escapeSettingsHtml(value.slice(0, 120))}..."</span>`;
    }
    return `<span class="cfg-str">"${escapeSettingsHtml(value)}"</span>`;
  }
  if (Array.isArray(value)) {
    if (value.length === 0) {
      return `<span class="cfg-bracket">[]</span>`;
    }
    const items = value
      .map(
        (v, i) =>
          `<div class="cfg-row" style="padding-left:${indent + 1}em">${renderConfigValue(i, v, depth + 1)}</div>`,
      )
      .join("");
    return `<span class="cfg-bracket">[</span>${items}<div style="padding-left:${indent}em"><span class="cfg-bracket">]</span></div>`;
  }
  if (typeof value === "object") {
    const entries = Object.entries(value);
    if (entries.length === 0) {
      return `<span class="cfg-bracket">{}</span>`;
    }
    const rows = entries
      .map(
        ([k, v]) =>
          `<div class="cfg-row" style="padding-left:${indent + 1}em"><span class="cfg-key">${escapeSettingsHtml(k)}</span><span class="cfg-colon">: </span>${renderConfigValue(k, v, depth + 1)}</div>`,
      )
      .join("");
    return `<span class="cfg-bracket">{</span>${rows}<div style="padding-left:${indent}em"><span class="cfg-bracket">}</span></div>`;
  }
  return escapeSettingsHtml(String(value));
}

function renderConfigSection(key, value, isRedacted) {
  const isExpanded = typeof value === "object" && value !== null;
  const icon = isExpanded ? "&#9662;" : "&#9656;";
  const redactedBadge = isRedacted ? '<span class="cfg-redacted">REDACTED</span>' : "";

  return `
    <div class="settings-section" data-key="${escapeSettingsHtml(key)}">
      <div class="settings-section-header" onclick="this.parentElement.classList.toggle('collapsed')">
        <span class="settings-section-arrow">${icon}</span>
        <span class="settings-section-key">${escapeSettingsHtml(key)}</span>
        ${redactedBadge}
      </div>
      <div class="settings-section-body">
        ${renderConfigValue(key, value, 0)}
      </div>
    </div>
  `;
}

async function openConfigPanel() {
  const container = showSettingsContainer();
  if (!container) {
    return;
  }

  container.innerHTML = `
    <div class="settings-panel">
      <div class="settings-toolbar">
        <div class="settings-toolbar-left">
          <span class="settings-toolbar-label">SYSTEM CONFIGURATION</span>
        </div>
        <div class="settings-toolbar-right">
          <button class="settings-btn" id="config-refresh-btn" title="Refresh">Refresh</button>
          <button class="settings-btn settings-btn--primary" id="config-edit-btn" title="Edit raw JSON">Edit</button>
        </div>
      </div>
      <div class="settings-content" id="config-content">
        <div class="settings-loading">Loading configuration...</div>
      </div>
    </div>
  `;

  // Wire refresh
  document
    .getElementById("config-refresh-btn")
    .addEventListener("click", () => void loadConfigView());
  document.getElementById("config-edit-btn").addEventListener("click", () => toggleConfigEditor());

  await loadConfigView();
}

async function loadConfigView() {
  const content = document.getElementById("config-content");
  if (!content) {
    return;
  }

  content.innerHTML = '<div class="settings-loading">Loading...</div>';
  const result = await fetchConfig();

  if (!result || !result.config) {
    content.innerHTML =
      '<div class="settings-empty">Unable to load configuration. Gateway may not be connected.</div>';
    return;
  }

  configData = result.config;
  configBaseHash = result.hash;
  configEditMode = false;

  // Render tree view
  const redacted = new Set(result.redactedPaths || []);
  const sections = Object.entries(configData);

  if (sections.length === 0) {
    content.innerHTML = '<div class="settings-empty">Configuration is empty.</div>';
    return;
  }

  content.innerHTML = sections
    .map(([key, value]) => {
      const isRedacted = redacted.has(key);
      return renderConfigSection(key, value, isRedacted);
    })
    .join("");
}

function toggleConfigEditor() {
  const content = document.getElementById("config-content");
  const editBtn = document.getElementById("config-edit-btn");
  if (!content) {
    return;
  }

  if (configEditMode) {
    // Switch back to tree view
    configEditMode = false;
    editBtn.textContent = "Edit";
    editBtn.classList.remove("settings-btn--active");
    void loadConfigView();
    return;
  }

  // Switch to editor
  configEditMode = true;
  editBtn.textContent = "View";
  editBtn.classList.add("settings-btn--active");

  const json = JSON.stringify(configData, null, 2);
  content.innerHTML = `
    <div class="config-editor-wrap">
      <textarea class="config-editor" id="config-editor-textarea" spellcheck="false">${escapeSettingsHtml(json)}</textarea>
      <div class="config-editor-actions">
        <span class="config-editor-hint">Edit the JSON above, then save to apply changes.</span>
        <button class="settings-btn settings-btn--danger" id="config-save-btn">Save & Apply</button>
      </div>
    </div>
  `;

  document.getElementById("config-save-btn").addEventListener("click", saveConfig);
}

async function saveConfig() {
  const textarea = document.getElementById("config-editor-textarea");
  const saveBtn = document.getElementById("config-save-btn");
  if (!textarea || !saveBtn) {
    return;
  }

  let parsed;
  try {
    parsed = JSON.parse(textarea.value);
  } catch (err) {
    alert("Invalid JSON: " + err.message);
    return;
  }

  saveBtn.textContent = "Saving...";
  saveBtn.disabled = true;

  try {
    await window.gateway.rpc("config.apply", {
      raw: parsed,
      baseHash: configBaseHash,
    });
    saveBtn.textContent = "Saved!";
    setTimeout(() => {
      configEditMode = false;
      const editBtn = document.getElementById("config-edit-btn");
      if (editBtn) {
        editBtn.textContent = "Edit";
        editBtn.classList.remove("settings-btn--active");
      }
      void loadConfigView();
    }, 800);
  } catch (err) {
    saveBtn.textContent = "Save & Apply";
    saveBtn.disabled = false;
    alert("Save failed: " + err.message);
  }
}

// ══════════════════════════════════════════════════════════════════════
// DEBUG PANEL
// ══════════════════════════════════════════════════════════════════════

let debugEntries = [];
let debugPaused = false;
const DEBUG_MAX = 500;

function openDebugPanel() {
  const container = showSettingsContainer();
  if (!container) {
    return;
  }

  container.innerHTML = `
    <div class="settings-panel">
      <div class="settings-toolbar">
        <div class="settings-toolbar-left">
          <span class="settings-toolbar-label">DEBUG CONSOLE</span>
          <span class="settings-toolbar-count" id="debug-count">0 events</span>
        </div>
        <div class="settings-toolbar-right">
          <button class="settings-btn" id="debug-pause-btn">Pause</button>
          <button class="settings-btn" id="debug-clear-btn">Clear</button>
          <button class="settings-btn" id="debug-export-btn">Export</button>
        </div>
      </div>
      <div class="settings-content debug-scroll" id="debug-content">
        <div class="settings-empty">Listening for events...</div>
      </div>
    </div>
  `;

  debugEntries = [];
  debugPaused = false;

  document.getElementById("debug-pause-btn").addEventListener("click", () => {
    debugPaused = !debugPaused;
    document.getElementById("debug-pause-btn").textContent = debugPaused ? "Resume" : "Pause";
  });

  document.getElementById("debug-clear-btn").addEventListener("click", () => {
    debugEntries = [];
    renderDebugEntries();
  });

  document.getElementById("debug-export-btn").addEventListener("click", () => {
    const text = debugEntries.map((e) => `${e.time} [${e.type}] ${e.summary}`).join("\n");
    const blob = new Blob([text], { type: "text/plain" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `symi-debug-${new Date().toISOString().slice(0, 19)}.txt`;
    a.click();
  });

  // Start capturing events. If a previous capture closure is still attached
  // (rapid re-open race), mark it cancelled so its body no-ops before we
  // swap in the new one. Without this, the previous closure keeps reading
  // the old panel's DOM even after it's gone and throws silently.
  if (window.__debugPanelCapture && typeof window.__debugPanelCapture === "function") {
    try {
      window.__debugPanelCapture.__cancelled = true;
    } catch {
      /* non-extensible function — ignore */
    }
  }
  const capture = function (type, payload) {
    if (capture.__cancelled) {
      return;
    }
    if (debugPaused) {
      return;
    }
    const time = new Date().toLocaleTimeString("en-US", { hour12: false });
    let summary = "";
    try {
      summary =
        typeof payload === "object"
          ? JSON.stringify(payload).slice(0, 200)
          : String(payload).slice(0, 200);
    } catch {
      summary = "[unserializable]";
    }
    debugEntries.unshift({ time, type, summary, payload });
    if (debugEntries.length > DEBUG_MAX) {
      debugEntries.length = DEBUG_MAX;
    }
    renderDebugEntries();
  };
  capture.__cancelled = false;
  window.__debugPanelCapture = capture;
}

function renderDebugEntries() {
  const content = document.getElementById("debug-content");
  const count = document.getElementById("debug-count");
  if (!content) {
    return;
  }
  if (count) {
    count.textContent = `${debugEntries.length} events`;
  }

  if (debugEntries.length === 0) {
    content.innerHTML = '<div class="settings-empty">Listening for events...</div>';
    return;
  }

  const html = debugEntries
    .map((e) => {
      const typeClass =
        e.type === "error"
          ? "debug-type--error"
          : e.type === "chat"
            ? "debug-type--chat"
            : e.type === "agent"
              ? "debug-type--agent"
              : "debug-type--other";
      return `<div class="debug-entry-row">
      <span class="debug-time">${e.time}</span>
      <span class="debug-type ${typeClass}">${escapeSettingsHtml(e.type)}</span>
      <span class="debug-summary">${escapeSettingsHtml(e.summary)}</span>
    </div>`;
    })
    .join("");

  content.innerHTML = html;
}

function closeDebugPanel() {
  // Cancel any pending capture closure before dropping the reference so
  // in-flight capture() calls short-circuit if they were already scheduled.
  const existing = window.__debugPanelCapture;
  if (existing && typeof existing === "function") {
    try {
      existing.__cancelled = true;
    } catch {
      /* non-extensible — ignore */
    }
  }
  window.__debugPanelCapture = null;
}

// ══════════════════════════════════════════════════════════════════════
// LOGS PANEL (replaces the dead-code logs.js)
// ══════════════════════════════════════════════════════════════════════

const LOGS_POLL_MS = 3000;
const LOGS_LIMIT = 500;
const LOGS_MAX_BYTES = 250000;

const LEVEL_COLORS = {
  TRACE: "#6b7280",
  DEBUG: "#8b5cf6",
  INFO: "#60a5fa",
  WARN: "#f59e0b",
  ERROR: "#ef4444",
  FATAL: "#dc2626",
};

const LEVEL_ORDER = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];

let logsEntries = [];
let logsCursor = null;
let logsTimer = null;
let logsAutoFollow = true;
let logsLevelFilters = new Set(LEVEL_ORDER);
let logsFilterText = "";

function parseLogLine(line) {
  try {
    const obj = JSON.parse(line);
    const level = obj._meta?.logLevelName || "INFO";
    const time = obj.time || obj._meta?.date || "";
    const msg = obj["1"] || obj.msg || obj.message || "";
    const sub = obj["0"] || "";
    return { level, time, msg, sub, raw: line, parsed: obj };
  } catch {
    return { level: "INFO", time: "", msg: line, sub: "", raw: line, parsed: null };
  }
}

function getVisibleLogEntries() {
  return logsEntries.filter((e) => {
    if (!logsLevelFilters.has(e.level)) {
      return false;
    }
    if (logsFilterText && !e.raw.toLowerCase().includes(logsFilterText)) {
      return false;
    }
    return true;
  });
}

function renderLogEntries() {
  const el = document.getElementById("logs-entries");
  if (!el) {
    return;
  }

  const visible = getVisibleLogEntries();
  const html = visible
    .map((e) => {
      const color = LEVEL_COLORS[e.level] || "#999";
      const timeStr = e.time ? new Date(e.time).toLocaleTimeString("en-US", { hour12: false }) : "";
      const escapedMsg = escapeSettingsHtml(e.msg).substring(0, 500);
      const escapedSub = escapeSettingsHtml(e.sub);
      return `<div class="log-row">
      <span class="log-time">${timeStr}</span>
      <span class="log-level" style="color:${color}">${e.level.padEnd(5)}</span>
      ${escapedSub ? `<span class="log-sub">${escapedSub}</span>` : ""}
      <span class="log-msg">${escapedMsg}</span>
    </div>`;
    })
    .join("");

  el.innerHTML = html || '<div class="settings-empty">No log entries.</div>';

  if (logsAutoFollow) {
    const scroll = document.getElementById("logs-scroll-area");
    if (scroll) {
      scroll.scrollTop = scroll.scrollHeight;
    }
  }
}

async function fetchLogs() {
  if (!window.gateway?.connected) {
    return;
  }
  try {
    const params = { limit: LOGS_LIMIT, maxBytes: LOGS_MAX_BYTES };
    if (logsCursor != null) {
      params.cursor = logsCursor;
    }
    const result = await window.gateway.rpc("logs.tail", params);
    if (!result) {
      return;
    }

    if (result.reset) {
      logsEntries = [];
      logsCursor = null;
    }

    if (result.lines && result.lines.length > 0) {
      const newEntries = result.lines.map(parseLogLine);
      logsEntries.push(...newEntries);
      if (logsEntries.length > 2000) {
        logsEntries = logsEntries.slice(logsEntries.length - 2000);
      }
      renderLogEntries();
    }

    if (typeof result.cursor === "number") {
      logsCursor = result.cursor;
    }
  } catch (err) {
    console.error("[settings:logs] fetch error:", err);
  }
}

function startLogsPolling() {
  logsCursor = null;
  logsEntries = [];

  if (!window.gateway?.connected) {
    const waitIv = setInterval(() => {
      if (window.gateway?.connected) {
        clearInterval(waitIv);
        void fetchLogs();
        if (logsTimer) {
          clearInterval(logsTimer);
        }
        logsTimer = setInterval(fetchLogs, LOGS_POLL_MS);
      }
    }, 500);
    setTimeout(() => clearInterval(waitIv), 30000);
    return;
  }

  void fetchLogs();
  if (logsTimer) {
    clearInterval(logsTimer);
  }
  logsTimer = setInterval(fetchLogs, LOGS_POLL_MS);
}

function stopLogsPolling() {
  if (logsTimer) {
    clearInterval(logsTimer);
    logsTimer = null;
  }
}

function openLogsPanel() {
  const container = showSettingsContainer();
  if (!container) {
    return;
  }

  container.innerHTML = `
    <div class="settings-panel">
      <div class="settings-toolbar">
        <div class="settings-toolbar-left">
          <span class="settings-toolbar-label">SYSTEM LOGS</span>
          <span class="settings-toolbar-count" id="logs-count">0 entries</span>
        </div>
        <div class="settings-toolbar-right">
          <div class="logs-level-filters">
            ${LEVEL_ORDER.map(
              (l) => `
              <label class="logs-level-chip" data-level="${l}">
                <input type="checkbox" checked data-level="${l}">
                <span style="color:${LEVEL_COLORS[l]}">${l}</span>
              </label>
            `,
            ).join("")}
          </div>
          <input type="text" class="settings-search" id="logs-filter-input" placeholder="Filter..." />
          <label class="settings-checkbox-label">
            <input type="checkbox" id="logs-follow-cb" checked> Follow
          </label>
          <button class="settings-btn" id="logs-export-btn">Export</button>
        </div>
      </div>
      <div class="settings-content logs-scroll" id="logs-scroll-area">
        <div id="logs-entries">
          <div class="settings-loading">Connecting to log stream...</div>
        </div>
      </div>
    </div>
  `;

  // Wire level filters
  container.querySelectorAll(".logs-level-chip input").forEach((cb) => {
    cb.addEventListener("change", () => {
      const level = cb.dataset.level;
      if (cb.checked) {
        logsLevelFilters.add(level);
      } else {
        logsLevelFilters.delete(level);
      }
      renderLogEntries();
    });
  });

  // Wire filter text
  document.getElementById("logs-filter-input").addEventListener("input", (e) => {
    logsFilterText = e.target.value.toLowerCase();
    renderLogEntries();
  });

  // Wire auto-follow
  document.getElementById("logs-follow-cb").addEventListener("change", (e) => {
    logsAutoFollow = e.target.checked;
    if (logsAutoFollow) {
      const scroll = document.getElementById("logs-scroll-area");
      if (scroll) {
        scroll.scrollTop = scroll.scrollHeight;
      }
    }
  });

  // Wire export
  document.getElementById("logs-export-btn").addEventListener("click", () => {
    const visible = getVisibleLogEntries();
    const text = visible.map((e) => e.raw).join("\n");
    const blob = new Blob([text], { type: "text/plain" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `symi-logs-${new Date().toISOString().slice(0, 19)}.txt`;
    a.click();
  });

  startLogsPolling();
}

function closeLogsPanel() {
  stopLogsPolling();
}

// ══════════════════════════════════════════════════════════════════════
// GENERIC RPC LIST PANEL — used by Overview, Channels, Sessions, etc.
// ══════════════════════════════════════════════════════════════════════

const PAGE_RPC = {
  overview: { method: "system.status", label: "SYSTEM OVERVIEW", type: "object" },
  channels: {
    method: "channels.status",
    label: "MESSAGING CHANNELS",
    type: "list",
    key: "channels",
  },
  instances: { method: "system.status", label: "AI INSTANCES", type: "object" },
  sessions: { method: "sessions.list", label: "ACTIVE SESSIONS", type: "list", key: "sessions" },
  usage: { method: "usage.status", label: "USAGE & TOKENS", type: "object" },
  cron: { method: "cron.list", label: "SCHEDULED TASKS", type: "list", key: "jobs" },
  tasks: { method: "tasks.list", label: "BACKGROUND TASKS", type: "list", key: "tasks" },
  agents: { method: "agents.list", label: "AGENT MANAGEMENT", type: "list", key: "agents" },
  skills: { method: "skills.status", label: "AGENT SKILLS", type: "object" },
  nodes: { method: "node.list", label: "CONNECTED DEVICES", type: "list", key: "nodes" },
  tools: { method: "chat.tools.list", label: "AGENT TOOLS", type: "list", key: "tools" },
};

function renderDataCard(key, value) {
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
    const rows = Object.entries(value)
      .map(([k, v]) => {
        const display = v == null ? "—" : JSON.stringify(v);
        return `<div class="data-card-row">
        <span class="data-card-label">${escapeSettingsHtml(k)}</span>
        <span class="data-card-value">${escapeSettingsHtml(display.slice(0, 200))}</span>
      </div>`;
      })
      .join("");
    return `<div class="data-card">
      <div class="data-card-header">${escapeSettingsHtml(String(key))}</div>
      ${rows}
    </div>`;
  }
  return `<div class="data-card">
    <div class="data-card-header">${escapeSettingsHtml(String(key))}</div>
    <div class="data-card-row"><span class="data-card-value">${escapeSettingsHtml(value != null ? JSON.stringify(value) : "—")}</span></div>
  </div>`;
}

// Pagination state per generic panel — keyed by the page identifier.
// Resets when the panel is opened; survives across refresh within one session.
const GENERIC_PAGE_SIZE = 50;
const genericShownCount = new Map();

function resetGenericPagination(page) {
  genericShownCount.set(page, GENERIC_PAGE_SIZE);
}

function bumpGenericPagination(page) {
  const cur = genericShownCount.get(page) ?? GENERIC_PAGE_SIZE;
  genericShownCount.set(page, cur + GENERIC_PAGE_SIZE);
}

function renderListData(items, page) {
  if (!items || items.length === 0) {
    return '<div class="settings-empty">No items found.</div>';
  }
  const shown = page ? (genericShownCount.get(page) ?? GENERIC_PAGE_SIZE) : items.length;
  const slice = items.slice(0, shown);
  const html = slice
    .map((item, i) => {
      const title = item.name || item.id || item.sessionKey || item.label || `Item ${i + 1}`;
      return renderDataCard(title, item);
    })
    .join("");
  if (!page || items.length <= shown) {
    return html;
  }
  const remaining = items.length - shown;
  const nextBatch = Math.min(GENERIC_PAGE_SIZE, remaining);
  return `${html}
    <div class="settings-pagination">
      <span class="settings-pagination-info">Showing ${shown} of ${items.length}</span>
      <button class="settings-btn" id="generic-show-more-btn" data-page="${escapeSettingsHtml(page)}">Show ${nextBatch} more</button>
    </div>`;
}

function renderObjectData(data) {
  if (!data || Object.keys(data).length === 0) {
    return '<div class="settings-empty">No data available.</div>';
  }
  return Object.entries(data)
    .map(([key, value]) => {
      if (Array.isArray(value)) {
        return `<div class="data-card">
        <div class="data-card-header">${escapeSettingsHtml(key)} <span class="data-card-badge">${value.length}</span></div>
        ${
          value.length === 0
            ? '<div class="data-card-row"><span class="data-card-value">Empty</span></div>'
            : value
                .slice(0, 20)
                .map((item, i) => {
                  const label =
                    typeof item === "object" ? item.name || item.id || `#${i + 1}` : String(item);
                  const detail = typeof item === "object" ? JSON.stringify(item).slice(0, 150) : "";
                  return `<div class="data-card-row">
                <span class="data-card-label">${escapeSettingsHtml(String(label))}</span>
                ${detail ? `<span class="data-card-value">${escapeSettingsHtml(detail)}</span>` : ""}
              </div>`;
                })
                .join("") +
              (value.length > 20
                ? `<div class="data-card-row"><span class="data-card-value">... and ${value.length - 20} more</span></div>`
                : "")
        }
      </div>`;
      }
      if (typeof value === "object" && value !== null) {
        return renderDataCard(key, value);
      }
      return `<div class="data-card-inline">
      <span class="data-card-label">${escapeSettingsHtml(key)}</span>
      <span class="data-card-value">${escapeSettingsHtml(value != null ? JSON.stringify(value) : "—")}</span>
    </div>`;
    })
    .join("");
}

async function openGenericPanel(page) {
  const spec = PAGE_RPC[page];
  if (!spec) {
    return;
  }

  const container = showSettingsContainer();
  if (!container) {
    return;
  }

  container.innerHTML = `
    <div class="settings-panel">
      <div class="settings-toolbar">
        <div class="settings-toolbar-left">
          <span class="settings-toolbar-label">${spec.label}</span>
        </div>
        <div class="settings-toolbar-right">
          <button class="settings-btn" id="generic-refresh-btn">Refresh</button>
        </div>
      </div>
      <div class="settings-content" id="generic-content">
        <div class="settings-loading">Loading...</div>
      </div>
    </div>
  `;

  document.getElementById("generic-refresh-btn").addEventListener("click", () => {
    resetGenericPagination(page);
    void loadGenericData(page);
  });
  // Delegated click handler for the "Show more" button rendered inside
  // generic-content. We attach on the container so it survives innerHTML
  // re-renders driven by loadGenericData.
  container.addEventListener("click", (e) => {
    const target = e.target;
    if (target && target.id === "generic-show-more-btn") {
      bumpGenericPagination(page);
      void loadGenericData(page);
    }
  });
  resetGenericPagination(page);
  await loadGenericData(page);
}

async function loadGenericData(page) {
  const spec = PAGE_RPC[page];
  const content = document.getElementById("generic-content");
  if (!spec || !content) {
    return;
  }

  content.innerHTML = '<div class="settings-loading">Loading...</div>';

  if (!window.gateway?.connected) {
    content.innerHTML = '<div class="settings-empty">Gateway not connected.</div>';
    return;
  }

  try {
    const result = await window.gateway.rpc(spec.method, {});
    if (!result) {
      content.innerHTML = '<div class="settings-empty">No data returned.</div>';
      return;
    }
    if (spec.type === "list") {
      const items = spec.key ? result[spec.key] : Array.isArray(result) ? result : [];
      content.innerHTML = renderListData(items, page);
    } else {
      content.innerHTML = renderObjectData(result);
    }
  } catch (err) {
    content.innerHTML = `<div class="settings-empty">Error: ${escapeSettingsHtml(err.message)}</div>`;
  }
}

// ══════════════════════════════════════════════════════════════════════
// PUBLIC API — called by menu.js
// ══════════════════════════════════════════════════════════════════════

let activeSettingsPanel = null;

window.openNativeSettings = function (page) {
  // Close previous panel cleanup
  closeActiveSettingsPanel();

  activeSettingsPanel = page;

  switch (page) {
    case "config":
      void openConfigPanel();
      break;
    case "debug":
      openDebugPanel();
      break;
    case "logs":
      openLogsPanel();
      break;
    default:
      if (PAGE_RPC[page]) {
        void openGenericPanel(page);
      } else {
        console.warn("[settings] unknown page:", page);
      }
      break;
  }
};

window.closeNativeSettings = function () {
  closeActiveSettingsPanel();
  hideSettingsContainer();
  activeSettingsPanel = null;
};

function closeActiveSettingsPanel() {
  if (activeSettingsPanel === "debug") {
    closeDebugPanel();
  }
  if (activeSettingsPanel === "logs") {
    closeLogsPanel();
  }
}
