import {
  DEFAULT_BASE_URL,
  DEFAULT_MODEL,
  DEFAULT_SETTINGS,
  DEFAULT_SYSTEM_PROMPT
} from "./shared/settings.js";
import { clearDiagnostics, getDiagnostics, recordDiagnostic } from "./shared/diagnostics.js";
import { copyText, formatBytes } from "./sidepanel/utils.js";
import {
  buildAttachmentReuseMessage,
  commitAttachmentCache,
  planAttachmentReuse
} from "./sidepanel/services/attachment-dedupe.js";
import { captureContextFromActiveTab } from "./sidepanel/services/page-context.js";
import { buildAnalyzeRequest } from "./sidepanel/services/request-builder.js";
import { analyzeStream, getSettings, saveSettings } from "./sidepanel/services/runtime-api.js";
import {
  clearAllStoredHistories,
  createEmptyChatSession,
  ensurePageSession,
  hydratePageSession,
  persistCurrentSession,
  persistSessionByKey
} from "./sidepanel/services/session-storage.js";
import {
  buildStorageBreakdownHtml,
  getStorageInventory,
  getStorageStats
} from "./sidepanel/services/storage-stats.js";
import { renderMessages } from "./sidepanel/render/messages.js";

let statusHideTimer = null;
const STREAM_FIRST_TOKEN_TIMEOUT_MS = 45000;
const STREAM_IDLE_TIMEOUT_MS = 90000;

document.addEventListener("DOMContentLoaded", async () => {
  const elements = getElements();
  const state = createInitialState();

  await loadSettings(elements, state);
  await hydratePageSession(state);
  renderMessages(elements.messages, state.chatSession.messages, state.debugMode, state);
  updateContextSummary(state, elements);
  await refreshStorageSummary(elements);
  registerActiveTabObservers(state, elements);
  setSettingsTab(elements, "config");
  bindEvents(elements, state);
});

function getElements() {
  return {
    messages: document.getElementById("messages"),
    messagesWrap: document.getElementById("messages-wrap"),
    instruction: document.getElementById("instruction"),
    sendButton: document.getElementById("send-button"),
    composer: document.getElementById("composer"),
    clearHistory: document.getElementById("clear-history"),
    clearStoredHistory: document.getElementById("clear-stored-history"),
    exportDiagnostics: document.getElementById("export-diagnostics"),
    clearDiagnostics: document.getElementById("clear-diagnostics"),
    status: document.getElementById("status"),
    contextSummary: document.getElementById("context-summary"),
    settingsToggle: document.getElementById("settings-toggle"),
    settings: document.getElementById("settings"),
    settingsTabConfig: document.getElementById("settings-tab-config"),
    settingsTabStorage: document.getElementById("settings-tab-storage"),
    settingsTabAbout: document.getElementById("settings-tab-about"),
    settingsPanelConfig: document.getElementById("settings-panel-config"),
    settingsPanelStorage: document.getElementById("settings-panel-storage"),
    settingsPanelAbout: document.getElementById("settings-panel-about"),
    storageSummary: document.getElementById("storage-summary"),
    storageBreakdown: document.getElementById("storage-breakdown"),
    includePageContextMain: document.getElementById("include-page-context-main"),
    includeScreenshotMain: document.getElementById("include-screenshot-main"),
    includePageContextDefault: document.getElementById("include-page-context-default"),
    includeScreenshotDefault: document.getElementById("include-screenshot-default"),
    apiKey: document.getElementById("api-key"),
    baseUrl: document.getElementById("base-url"),
    model: document.getElementById("model"),
    systemPrompt: document.getElementById("system-prompt"),
    fontScale: document.getElementById("font-scale"),
    screenshotMode: document.getElementById("screenshot-mode"),
    debugMode: document.getElementById("debug-mode"),
    saveSettings: document.getElementById("save-settings")
  };
}

function createInitialState() {
  return {
    chatSession: createEmptyChatSession(),
    pageSnapshot: null,
    turnAttachments: null,
    activeStream: null,
    currentPageKey: "",
    currentPageTitle: "",
    currentPageUrl: "",
    isComposing: false,
    debugMode: DEFAULT_SETTINGS.debugMode,
    activeTabRefreshTimer: null
  };
}

async function loadSettings(elements, state) {
  const settingsResponse = await getSettings();
  const settings = settingsResponse?.ok ? settingsResponse.settings || DEFAULT_SETTINGS : DEFAULT_SETTINGS;

  elements.apiKey.value = settings.openaiApiKey || "";
  elements.baseUrl.value = settings.openaiBaseUrl || DEFAULT_BASE_URL;
  elements.model.value = settings.openaiModel || DEFAULT_MODEL;
  elements.systemPrompt.value = settings.systemPrompt || DEFAULT_SYSTEM_PROMPT;
  elements.fontScale.value = settings.fontScale || DEFAULT_SETTINGS.fontScale;
  elements.screenshotMode.value = settings.screenshotMode || DEFAULT_SETTINGS.screenshotMode;
  elements.includePageContextDefault.checked = settings.includePageContext !== false;
  elements.includeScreenshotDefault.checked = settings.includeScreenshot !== false;
  elements.includePageContextMain.checked = settings.includePageContext !== false;
  elements.includeScreenshotMain.checked = settings.includeScreenshot !== false;
  elements.debugMode.checked = settings.debugMode === true;
  state.debugMode = settings.debugMode === true;
  applyFontScale(settings.fontScale || DEFAULT_SETTINGS.fontScale);
}

function bindEvents(elements, state) {
  window.addEventListener("pagehide", () => {
    cancelActiveStream(state, { silent: true, reason: "面板已关闭，已停止当前生成。" });
  });

  elements.includePageContextMain.addEventListener("change", () => {
    elements.includePageContextDefault.checked = elements.includePageContextMain.checked;
  });

  elements.includeScreenshotMain.addEventListener("change", () => {
    elements.includeScreenshotDefault.checked = elements.includeScreenshotMain.checked;
  });

  elements.includePageContextDefault.addEventListener("change", () => {
    elements.includePageContextMain.checked = elements.includePageContextDefault.checked;
  });

  elements.includeScreenshotDefault.addEventListener("change", () => {
    elements.includeScreenshotMain.checked = elements.includeScreenshotDefault.checked;
  });

  elements.settingsToggle.addEventListener("click", () => {
    elements.settings.hidden = !elements.settings.hidden;
    const settingsOpen = !elements.settings.hidden;
    elements.messagesWrap.hidden = settingsOpen;
    elements.composer.hidden = settingsOpen;
  });

  elements.settingsTabConfig.addEventListener("click", () => setSettingsTab(elements, "config"));
  elements.settingsTabStorage.addEventListener("click", () => setSettingsTab(elements, "storage"));
  elements.settingsTabAbout.addEventListener("click", () => setSettingsTab(elements, "about"));

  elements.saveSettings.addEventListener("click", async () => {
    const response = await saveSettings({
      openaiApiKey: elements.apiKey.value.trim(),
      openaiBaseUrl: elements.baseUrl.value.trim() || DEFAULT_BASE_URL,
      openaiModel: elements.model.value.trim() || DEFAULT_MODEL,
      systemPrompt: elements.systemPrompt.value.trim() || DEFAULT_SYSTEM_PROMPT,
      fontScale: elements.fontScale.value || DEFAULT_SETTINGS.fontScale,
      screenshotMode: elements.screenshotMode.value || DEFAULT_SETTINGS.screenshotMode,
      includePageContext: elements.includePageContextDefault.checked,
      includeScreenshot: elements.includeScreenshotDefault.checked,
      debugMode: elements.debugMode.checked
    });

    state.debugMode = elements.debugMode.checked;
    applyFontScale(elements.fontScale.value || DEFAULT_SETTINGS.fontScale);
    renderMessages(elements.messages, state.chatSession.messages, state.debugMode, state);
    await recordDiagnostic({
      source: "sidepanel",
      event: response?.ok ? "settings_saved" : "settings_save_failed",
      details: response?.ok ? null : response?.error || "Unknown error"
    });
    setStatus(elements.status, response?.ok ? "设置已保存。" : `保存失败：${response?.error || "Unknown error"}`);
  });

  elements.sendButton.addEventListener("click", async () => {
    if (isCurrentPageStreaming(state)) {
      cancelActiveStream(state, { silent: false, reason: "已停止当前生成。" });
      return;
    }
    if (hasActiveStream(state)) {
      setStatus(elements.status, "另一个页面正在生成，请回到对应页面等待完成或停止。");
      return;
    }
    await sendMessage(state, elements);
  });

  elements.instruction.addEventListener("compositionstart", () => {
    state.isComposing = true;
  });

  elements.instruction.addEventListener("compositionend", () => {
    state.isComposing = false;
  });

  elements.instruction.addEventListener("keydown", async (event) => {
    if (event.key !== "Enter" || event.shiftKey) {
      return;
    }
    if (state.isComposing || event.isComposing || event.keyCode === 229) {
      return;
    }
    if (!event.metaKey && !event.ctrlKey && !event.altKey) {
      event.preventDefault();
      if (hasActiveStream(state) && !isCurrentPageStreaming(state)) {
        setStatus(elements.status, "另一个页面正在生成，请回到对应页面等待完成或停止。");
        return;
      }
      await sendMessage(state, elements);
    }
  });

  elements.clearHistory.addEventListener("click", async () => {
    if (isCurrentPageStreaming(state)) {
      cancelActiveStream(state, { silent: true, reason: "已清空当前会话，已停止当前生成。" });
    }
    state.chatSession = createEmptyChatSession();
    state.turnAttachments = null;
    await captureFreshPageSummary(state);
    renderMessages(elements.messages, state.chatSession.messages, state.debugMode, state);
    await persistCurrentSession(state);
    updateContextSummary(state, elements);
    setStatus(elements.status, "当前页面历史对话已清空。");
    await refreshStorageSummary(elements);
  });

  elements.clearStoredHistory.addEventListener("click", async () => {
    cancelActiveStream(state, { silent: true, reason: "已清空本地会话，已停止当前生成。" });
    await clearAllStoredHistories();
    state.chatSession = createEmptyChatSession();
    state.turnAttachments = null;
    await captureFreshPageSummary(state);
    renderMessages(elements.messages, state.chatSession.messages, state.debugMode, state);
    updateContextSummary(state, elements);
    await refreshStorageSummary(elements);
    setStatus(elements.status, "已清空本地保存的所有对话历史。");
  });

  elements.exportDiagnostics.addEventListener("click", async () => {
    const logs = await getDiagnostics();
    const diagnosticExport = await buildDiagnosticExport(logs, state);
    downloadJsonFile(`sensa-basic-diagnostics-${Date.now()}.json`, diagnosticExport);
    setStatus(elements.status, "已导出诊断日志。");
  });

  elements.clearDiagnostics.addEventListener("click", async () => {
    await clearDiagnostics();
    setStatus(elements.status, "已清空诊断日志。");
  });

  elements.messages.addEventListener("click", async (event) => {
    const copyButton = event.target.closest("[data-copy-index]");
    if (!copyButton) {
      return;
    }
    const index = Number(copyButton.dataset.copyIndex || -1);
    const message = state.chatSession.messages[index];
    if (!message?.text) {
      setStatus(elements.status, "没有可复制的内容。");
      return;
    }
    try {
      await copyText(message.text);
      setStatus(elements.status, "已复制消息内容。");
    } catch (error) {
      await recordDiagnostic({
        source: "sidepanel",
        event: "copy_failed",
        details: error instanceof Error ? error.message : String(error)
      });
      setStatus(elements.status, `复制失败：${error instanceof Error ? error.message : String(error)}`);
    }
  });
}

function registerActiveTabObservers(state, elements) {
  const scheduleRefresh = () => {
    if (state.activeTabRefreshTimer) {
      window.clearTimeout(state.activeTabRefreshTimer);
    }
    state.activeTabRefreshTimer = window.setTimeout(() => {
      state.activeTabRefreshTimer = null;
      refreshForActiveTabChange(state, elements).catch((error) => {
        recordDiagnostic({
          source: "sidepanel",
          event: "page_sync_failed",
          details: error instanceof Error ? error.message : String(error)
        }).catch(() => {});
        setStatus(
          elements.status,
          `页面切换同步失败：${error instanceof Error ? error.message : String(error)}`
        );
      });
    }, 180);
  };

  chrome.tabs.onActivated.addListener(scheduleRefresh);
  chrome.tabs.onUpdated.addListener((_tabId, changeInfo, tab) => {
    if (!tab?.active) {
      return;
    }
    if (!changeInfo.status && typeof changeInfo.url === "undefined" && typeof changeInfo.title === "undefined") {
      return;
    }
    scheduleRefresh();
  });
  chrome.windows.onFocusChanged.addListener((windowId) => {
    if (windowId === chrome.windows.WINDOW_ID_NONE) {
      return;
    }
    scheduleRefresh();
  });
}

async function refreshForActiveTabChange(state, elements) {
  await persistCurrentSession(state);
  const pageChanged = await ensurePageSession(state);
  if (!pageChanged) {
    updateContextSummary(state, elements);
    syncBusyState(elements, state);
    return;
  }
  renderMessages(elements.messages, state.chatSession.messages, state.debugMode, state);
  updateContextSummary(state, elements);
  syncBusyState(elements, state);
}

async function sendMessage(state, elements) {
  await ensurePageSession(state);

  const text = elements.instruction.value.trim();
  if (!text || hasActiveStream(state)) {
    return;
  }

  const includePageContext = elements.includePageContextMain.checked;
  const includeScreenshot = elements.includeScreenshotMain.checked;
  const screenshotMode = elements.screenshotMode.value || DEFAULT_SETTINGS.screenshotMode;
  const activePageKey = state.currentPageKey;
  state.turnAttachments = null;
  cancelActiveStream(state, { silent: true, reason: "新的请求已开始，已停止上一轮生成。" });

  syncBusyState(elements, state);

  try {
    let reusePlan = null;
    if (includePageContext || includeScreenshot) {
      setStatus(
        elements.status,
        includeScreenshot ? "正在抓取最新页面内容和截图..." : "正在抓取最新页面内容...",
        { autoHide: false }
      );
      const capturedContext = await captureContextFromActiveTab(includeScreenshot, screenshotMode);
      state.pageSnapshot = stripTransportFields(capturedContext);
      state.turnAttachments = buildTurnAttachments(capturedContext);
      updateContextSummary(state, elements);
      reusePlan = await planAttachmentReuse({
        includePageContext,
        includeScreenshot,
        pageSnapshot: state.pageSnapshot,
        turnAttachments: state.turnAttachments,
        attachmentCache: state.chatSession.attachmentCache
      });
    }

    const effectiveIncludePageContext = reusePlan ? reusePlan.includePageContext : includePageContext;
    const effectiveIncludeScreenshot = reusePlan ? reusePlan.includeScreenshot : includeScreenshot;
    const userMessage = {
      role: "user",
      text,
      details: state.debugMode
        ? {
            contextPreview: effectiveIncludePageContext
              ? buildContextPreview(state.pageSnapshot, state.turnAttachments)
              : "本轮未附带新的页面内容。",
            screenshotDataUrl: effectiveIncludeScreenshot ? state.turnAttachments?.screenshotDataUrl || "" : "",
            note: reusePlan ? buildAttachmentReuseMessage(reusePlan) : ""
          }
        : null
    };

    const payload = buildAnalyzeRequest({
      userInstruction: text,
      pageSnapshot: state.pageSnapshot,
      turnAttachments: state.turnAttachments,
      includePageContext: effectiveIncludePageContext,
      includeScreenshot: effectiveIncludeScreenshot,
      chatSession: state.chatSession
    });
    const activeChatSession = state.chatSession;
    const activePageSnapshot = state.pageSnapshot;
    activeChatSession.messages.push(userMessage);
    const assistantMessage = {
      role: "assistant",
      text: "",
      meta: "AI",
      streaming: true
    };
    activeChatSession.messages.push(assistantMessage);
    await persistSessionByKey(activePageKey, activeChatSession, activePageSnapshot);
    if (state.currentPageKey === activePageKey) {
      renderMessages(elements.messages, activeChatSession.messages, state.debugMode, state);
    }
    scrollMessagesToBottom(elements.messages);
    elements.instruction.value = "";

    const reuseMessage = reusePlan ? buildAttachmentReuseMessage(reusePlan) : "";
    setStatus(
      elements.status,
      reuseMessage ? `${reuseMessage} 正在生成回复...` : "正在生成回复...",
      { autoHide: false }
    );
    const streamResult = await streamAssistantResponse(
      payload,
      assistantMessage,
      activeChatSession,
      activePageKey,
      activePageSnapshot,
      state,
      elements
    );
    if (reusePlan) {
      commitAttachmentCache(activeChatSession, reusePlan.nextAttachmentCache, {
        includePageContext: effectiveIncludePageContext,
        includeScreenshot: effectiveIncludeScreenshot
      });
    }
    await persistSessionByKey(activePageKey, activeChatSession, activePageSnapshot);
    await refreshStorageSummary(elements);
    await recordDiagnostic({
      source: "sidepanel",
      event: "analyze_completed",
      details: {
        model: streamResult.model || "",
        hasUsage: !!streamResult.usage
      }
    });
    setStatus(
      elements.status,
      streamResult.usage
        ? `模型 ${streamResult.model} 已返回。tokens: input ${streamResult.usage.input_tokens || 0}, output ${streamResult.usage.output_tokens || 0}.`
        : `模型 ${streamResult.model} 已返回。`
    );
  } catch (error) {
    if (isCancelledError(error)) {
      await recordDiagnostic({
        source: "sidepanel",
        event: "analyze_cancelled",
        details: error.message
      });
      if (!error.silent) {
        setStatus(elements.status, error.message);
      }
      return;
    }
    await recordDiagnostic({
      source: "sidepanel",
      event: "analyze_failed",
      details: error instanceof Error ? error.message : String(error)
    });
    setStatus(elements.status, `发送失败：${error instanceof Error ? error.message : String(error)}`);
  } finally {
    syncBusyState(elements, state);
  }
}

function streamAssistantResponse(payload, assistantMessage, chatSession, pageKey, pageSnapshot, state, elements) {
  return new Promise((resolve, reject) => {
    cancelActiveStream(state, { silent: true, reason: "新的流式请求已开始。" });
    let settled = false;
    let disconnect = null;
    let firstTokenReceived = false;
    let streamWatchdogTimer = null;

    state.activeStream = {
      pageKey,
      cancel: (options = {}) => {
        if (settled) {
          return;
        }
        const error = createCancelledError(options.reason, options.silent);
        if (typeof disconnect === "function") {
          disconnect();
        }
        removeAssistantPlaceholder();
        finalizeReject(error);
      }
    };
    syncBusyState(elements, state);

    const finalizeReject = (error) => {
      if (settled) {
        return;
      }
      settled = true;
      clearWatchdog();
      clearActiveStream(state, pageKey);
      syncBusyState(elements, state);
      reject(error);
    };

    const finalizeResolve = (result) => {
      if (settled) {
        return;
      }
      settled = true;
      clearWatchdog();
      clearActiveStream(state, pageKey);
      syncBusyState(elements, state);
      resolve(result);
    };

    const clearWatchdog = () => {
      if (streamWatchdogTimer) {
        window.clearTimeout(streamWatchdogTimer);
        streamWatchdogTimer = null;
      }
    };

    const armWatchdog = () => {
      clearWatchdog();
      const timeoutMs = firstTokenReceived ? STREAM_IDLE_TIMEOUT_MS : STREAM_FIRST_TOKEN_TIMEOUT_MS;
      streamWatchdogTimer = window.setTimeout(async () => {
        if (settled) {
          return;
        }
        removeAssistantPlaceholder();
        await persistSessionByKey(pageKey, chatSession, pageSnapshot);
        if (typeof disconnect === "function") {
          disconnect();
        }
        finalizeReject(
          new Error(
            firstTokenReceived
              ? "生成过程中长时间没有继续返回内容，已自动停止。"
              : "模型长时间没有开始回复，已自动停止。"
          )
        );
      }, timeoutMs);
    };

    const removeAssistantPlaceholder = () => {
      const lastIndex = chatSession.messages.lastIndexOf(assistantMessage);
      if (lastIndex >= 0) {
        chatSession.messages.splice(lastIndex, 1);
      }
      if (state.currentPageKey === pageKey) {
        renderMessages(elements.messages, chatSession.messages, state.debugMode, state);
      }
    };

    disconnect = analyzeStream(payload, {
      async onDelta(delta) {
        if (settled) {
          return;
        }
        firstTokenReceived = true;
        armWatchdog();
        assistantMessage.text += delta;
        assistantMessage.streaming = false;
        await persistSessionByKey(pageKey, chatSession, pageSnapshot);
        if (state.currentPageKey === pageKey) {
          renderMessages(elements.messages, chatSession.messages, state.debugMode, state);
          scrollMessagesToBottom(elements.messages);
        }
      },
      async onDone(result) {
        if (settled) {
          return;
        }
        firstTokenReceived = true;
        assistantMessage.text = result.text || assistantMessage.text;
        assistantMessage.meta = result.usage
          ? `tokens: input ${result.usage.input_tokens || 0}, output ${result.usage.output_tokens || 0}`
          : result.model || "AI";
        assistantMessage.streaming = false;
        await persistSessionByKey(pageKey, chatSession, pageSnapshot);
        if (state.currentPageKey === pageKey) {
          renderMessages(elements.messages, chatSession.messages, state.debugMode, state);
          scrollMessagesToBottom(elements.messages);
        }
        finalizeResolve(result);
      },
      async onError(error) {
        if (settled) {
          return;
        }
        removeAssistantPlaceholder();
        await persistSessionByKey(pageKey, chatSession, pageSnapshot);
        finalizeReject(new Error(error));
      }
    });

    if (typeof disconnect !== "function") {
      finalizeReject(new Error("无法建立流式连接。"));
      return;
    }

    armWatchdog();
  });
}

function downloadJsonFile(filename, data) {
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  anchor.click();
  window.setTimeout(() => URL.revokeObjectURL(url), 0);
}

async function buildDiagnosticExport(logs, state) {
  const manifest = chrome.runtime.getManifest();
  const settingsResponse = await getSettings();
  const stats = await getStorageStats();
  const inventory = await getStorageInventory();
  const now = new Date().toISOString();
  const settings = settingsResponse?.ok ? settingsResponse.settings || DEFAULT_SETTINGS : DEFAULT_SETTINGS;

  return {
    exportedAt: now,
    app: {
      name: manifest.name || "Sensa基础版",
      version: manifest.version || "unknown",
      manifestVersion: manifest.manifest_version || 3
    },
    environment: {
      userAgent: navigator.userAgent,
      language: navigator.language,
      platform: navigator.platform || "unknown",
      online: navigator.onLine,
      currentPage: {
        key: state.currentPageKey,
        title: state.currentPageTitle,
        url: state.currentPageUrl
      }
    },
    settings: sanitizeSettingsForExport(settings),
    storage: {
      summary: stats,
      inventory
    },
    currentSession: summarizeSessionForExport(state),
    diagnostics: {
      count: Array.isArray(logs) ? logs.length : 0,
      summary: summarizeDiagnostics(logs),
      recentErrors: extractRecentDiagnosticEvents(logs, 12, /failed|error|cancel/i),
      recentEvents: Array.isArray(logs) ? logs.slice(-50) : []
    }
  };
}

function sanitizeSettingsForExport(settings) {
  const value = settings || DEFAULT_SETTINGS;
  return {
    hasApiKey: !!String(value.openaiApiKey || "").trim(),
    openaiBaseUrl: value.openaiBaseUrl || DEFAULT_BASE_URL,
    openaiModel: value.openaiModel || DEFAULT_MODEL,
    fontScale: value.fontScale || DEFAULT_SETTINGS.fontScale,
    screenshotMode: value.screenshotMode || DEFAULT_SETTINGS.screenshotMode,
    includePageContext: value.includePageContext !== false,
    includeScreenshot: value.includeScreenshot !== false,
    debugMode: value.debugMode === true,
    systemPromptPreview: String(value.systemPrompt || DEFAULT_SYSTEM_PROMPT).slice(0, 240)
  };
}

function summarizeSessionForExport(state) {
  const messages = Array.isArray(state?.chatSession?.messages) ? state.chatSession.messages : [];
  return {
    messageCount: messages.length,
    attachmentCache: state?.chatSession?.attachmentCache || null,
    recentMessages: messages.slice(-8).map((message, index) => ({
      index: messages.length - Math.min(messages.length, 8) + index,
      role: message.role,
      textPreview: String(message.text || "").slice(0, 400),
      meta: String(message.meta || ""),
      hasDetails: !!message.details
    })),
    pageSnapshot: summarizePageSnapshot(state?.pageSnapshot),
    hasTurnScreenshot: !!state?.turnAttachments?.screenshotDataUrl,
    turnScreenshotMeta: state?.turnAttachments?.screenshotMeta || null
  };
}

function summarizePageSnapshot(pageSnapshot) {
  if (!pageSnapshot || typeof pageSnapshot !== "object") {
    return null;
  }

  return {
    page: pageSnapshot.page || null,
    selectionPreview: String(pageSnapshot.selection || "").slice(0, 200),
    activeElement: pageSnapshot.activeElement || null,
    headingsCount: Array.isArray(pageSnapshot.headings) ? pageSnapshot.headings.length : 0,
    buttonsCount: Array.isArray(pageSnapshot.buttons) ? pageSnapshot.buttons.length : 0,
    tablesCount: Array.isArray(pageSnapshot.tables) ? pageSnapshot.tables.length : 0,
    listsCount: Array.isArray(pageSnapshot.lists) ? pageSnapshot.lists.length : 0,
    visibleTextPreview: String(pageSnapshot.visibleTextExcerpt || "").slice(0, 1200),
    interaction: pageSnapshot.interaction || null,
    timestamp: pageSnapshot.timestamp || null
  };
}

function summarizeDiagnostics(logs) {
  const events = Array.isArray(logs) ? logs : [];
  const byEvent = {};
  const bySource = {};

  for (const item of events) {
    const event = String(item?.event || "unknown");
    const source = String(item?.source || "unknown");
    byEvent[event] = (byEvent[event] || 0) + 1;
    bySource[source] = (bySource[source] || 0) + 1;
  }

  return {
    firstEventAt: events[0]?.time || null,
    lastEventAt: events.at(-1)?.time || null,
    byEvent,
    bySource
  };
}

function extractRecentDiagnosticEvents(logs, limit, pattern) {
  const events = (Array.isArray(logs) ? logs : []).filter((item) =>
    pattern.test(`${item?.event || ""} ${typeof item?.details === "string" ? item.details : JSON.stringify(item?.details || {})}`)
  );
  return events.slice(-limit);
}

async function captureFreshPageSummary(state) {
  if (!state.currentPageUrl) {
    return;
  }
  try {
    state.pageSnapshot = stripTransportFields(await captureContextFromActiveTab(false, "viewport"));
    state.turnAttachments = null;
  } catch (error) {
    state.pageSnapshot = null;
    state.turnAttachments = null;
  }
}

function cancelActiveStream(state, options) {
  if (typeof state.activeStream?.cancel === "function") {
    state.activeStream.cancel(options);
  }
  state.activeStream = null;
}

function createCancelledError(reason, silent) {
  const error = new Error(reason || "已取消当前生成。");
  error.name = "SensaStreamCancelledError";
  error.isCancelled = true;
  error.silent = silent === true;
  return error;
}

function isCancelledError(error) {
  return !!error && typeof error === "object" && error.isCancelled === true;
}

function applyFontScale(scale) {
  document.body.dataset.fontScale = scale || DEFAULT_SETTINGS.fontScale;
}

function syncBusyState(elements, state) {
  const currentPageStreaming = isCurrentPageStreaming(state);
  const otherPageStreaming = hasActiveStream(state) && !currentPageStreaming;

  elements.sendButton.disabled = otherPageStreaming;
  elements.sendButton.textContent = currentPageStreaming ? "停止" : otherPageStreaming ? "处理中" : "发送";
  elements.sendButton.classList.toggle("is-stop", currentPageStreaming);
}

function hasActiveStream(state) {
  return !!state.activeStream?.pageKey;
}

function isCurrentPageStreaming(state) {
  return hasActiveStream(state) && state.activeStream.pageKey === state.currentPageKey;
}

function clearActiveStream(state, pageKey) {
  if (state.activeStream?.pageKey === pageKey) {
    state.activeStream = null;
  }
}

function updateContextSummary(state, elements) {
  elements.contextSummary.textContent = "";
  elements.contextSummary.classList.add("panel-context-summary-hidden");
}

function buildContextPreview(pageSnapshot, turnAttachments) {
  const preview = {
    ...(pageSnapshot || {}),
    screenshotDataUrl: turnAttachments?.screenshotDataUrl ? "[screenshot attached]" : "",
    screenshotMeta: turnAttachments?.screenshotMeta || null
  };
  return JSON.stringify(preview, null, 2);
}

function stripTransportFields(context) {
  if (!context) {
    return {};
  }
  const clone = { ...context };
  delete clone.screenshotDataUrl;
  delete clone.screenshotMeta;
  return clone;
}

function buildTurnAttachments(context) {
  if (!context?.screenshotDataUrl) {
    return null;
  }
  return {
    screenshotDataUrl: context.screenshotDataUrl,
    screenshotMeta: context.screenshotMeta || null
  };
}

function scrollMessagesToBottom(container) {
  container.scrollTop = container.scrollHeight;
}

function setStatus(element, text, options = {}) {
  const autoHide = options.autoHide !== false;
  const durationMs = Number.isFinite(options.durationMs) ? options.durationMs : 2000;

  if (statusHideTimer) {
    window.clearTimeout(statusHideTimer);
    statusHideTimer = null;
  }

  element.textContent = text;
  element.classList.toggle("panel-status-hidden", !text);

  if (!text || !autoHide) {
    return;
  }

  statusHideTimer = window.setTimeout(() => {
    element.textContent = "";
    element.classList.add("panel-status-hidden");
    statusHideTimer = null;
  }, durationMs);
}

function setSettingsTab(elements, activeTab) {
  elements.settingsTabConfig.classList.toggle("is-active", activeTab === "config");
  elements.settingsTabStorage.classList.toggle("is-active", activeTab === "storage");
  elements.settingsTabAbout.classList.toggle("is-active", activeTab === "about");
  elements.settingsPanelConfig.hidden = activeTab !== "config";
  elements.settingsPanelStorage.hidden = activeTab !== "storage";
  elements.settingsPanelAbout.hidden = activeTab !== "about";
  elements.saveSettings.hidden = activeTab !== "config";
}

async function refreshStorageSummary(elements) {
  const stats = await getStorageStats();
  elements.storageSummary.textContent =
    `共 ${stats.historyCount} 个页面会话，当前本地占用 ${formatBytes(stats.totalBytes)}。`;
  elements.storageBreakdown.innerHTML = buildStorageBreakdownHtml(stats);
}
