(() => {
  "use strict";

  const slides = [...document.querySelectorAll(".slide")];
  const total = slides.length;
  const storageKey = "visual-courseware-assistant-config";
  const sessionKey = "visual-courseware-assistant-session-key";
  const providers = {
    openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-mini" },
    deepseek: { baseUrl: "https://api.deepseek.com/v1", model: "deepseek-chat" },
    moonshot: { baseUrl: "https://api.moonshot.cn/v1", model: "moonshot-v1-8k" },
    custom: { baseUrl: "", model: "" },
  };
  const sessionMessages = [];
  let current = parseHash();
  let lastFocused = null;

  const byId = (id) => document.getElementById(id);
  const currentPage = byId("currentPage");
  const totalPages = byId("totalPages");
  const drawer = byId("assistantDrawer");
  const assistantToggle = byId("assistantToggle");
  const conversation = byId("conversation");
  const assistantInput = byId("assistantInput");
  const sendButton = byId("assistantSend");

  function parseHash() {
    const value = Number.parseInt(location.hash.replace("#slide-", ""), 10);
    return Number.isFinite(value) ? Math.min(Math.max(value - 1, 0), slides.length - 1) : 0;
  }

  function twoDigits(number) {
    return String(number).padStart(2, "0");
  }

  function renderSlide(next, updateHash = true) {
    current = Math.min(Math.max(next, 0), total - 1);
    slides.forEach((slide, index) => {
      const active = index === current;
      slide.hidden = !active;
      slide.classList.toggle("is-active", active);
      slide.setAttribute("aria-hidden", String(!active));
    });
    currentPage.textContent = twoDigits(current + 1);
    totalPages.textContent = twoDigits(total);
    byId("prevSlide").disabled = current === 0;
    byId("nextSlide").disabled = current === total - 1;
    if (updateHash) history.replaceState(null, "", `#slide-${current + 1}`);
  }

  function move(delta) {
    renderSlide(current + delta);
  }

  function toggleFullscreen() {
    if (!document.fullscreenElement) {
      byId("deckFrame").requestFullscreen().catch(() => showToast("浏览器未允许进入全屏。"));
    } else {
      document.exitFullscreen();
    }
  }

  function openAssistant() {
    lastFocused = document.activeElement;
    drawer.classList.add("is-open");
    drawer.setAttribute("aria-hidden", "false");
    assistantToggle.setAttribute("aria-expanded", "true");
    byId("assistantClose").focus();
  }

  function closeAssistant() {
    drawer.classList.remove("is-open");
    drawer.setAttribute("aria-hidden", "true");
    assistantToggle.setAttribute("aria-expanded", "false");
    if (lastFocused) lastFocused.focus();
  }

  function switchAssistantPanel(panel) {
    const chat = panel === "chat";
    byId("chatPanel").hidden = !chat;
    byId("settingsPanel").hidden = chat;
    byId("chatTab").classList.toggle("is-active", chat);
    byId("settingsTab").classList.toggle("is-active", !chat);
    byId("chatTab").setAttribute("aria-selected", String(chat));
    byId("settingsTab").setAttribute("aria-selected", String(!chat));
  }

  function showToast(message) {
    const toast = byId("toast");
    toast.textContent = message;
    toast.hidden = false;
    window.clearTimeout(showToast.timer);
    showToast.timer = window.setTimeout(() => { toast.hidden = true; }, 3200);
  }

  function loadConfig() {
    try {
      const saved = JSON.parse(localStorage.getItem(storageKey)) || {};
      return { ...saved, apiKey: sessionStorage.getItem(sessionKey) || saved.apiKey || "" };
    } catch {
      return {};
    }
  }

  function fillConfig() {
    const config = loadConfig();
    byId("assistantBaseUrl").value = config.baseUrl || "";
    byId("assistantApiKey").value = config.apiKey || "";
    byId("assistantModel").value = config.model || "";
    byId("assistantProvider").value = config.provider || "custom";
    byId("assistantRemember").checked = Boolean(config.remember);
  }

  function saveConfig(event) {
    event.preventDefault();
    const config = {
      provider: byId("assistantProvider").value,
      baseUrl: byId("assistantBaseUrl").value.trim(),
      apiKey: byId("assistantApiKey").value.trim(),
      model: byId("assistantModel").value.trim(),
      remember: byId("assistantRemember").checked,
    };
    sessionStorage.setItem(sessionKey, config.apiKey);
    const persisted = { ...config };
    if (!config.remember) delete persisted.apiKey;
    localStorage.setItem(storageKey, JSON.stringify(persisted));
    showToast(config.remember ? "配置已保存在这台设备。" : "配置仅在当前会话有效。");
    switchAssistantPanel("chat");
  }

  function clearConfig() {
    localStorage.removeItem(storageKey);
    sessionStorage.removeItem(sessionKey);
    fillConfig();
    showToast("模型配置已清除。 ");
  }

  function getCurrentSlideContext() {
    const slide = slides[current];
    const title = slide.querySelector("h1, h2")?.textContent.trim() || `第 ${current + 1} 页`;
    const visibleText = slide.innerText.replace(/\s+/g, " ").trim();
    return `页码：${current + 1}/${total}\n标题：${title}\n页面内容：${visibleText}`;
  }

  function renderSafeRichText(text) {
    const escaped = text.replace(/[&<>"']/g, char => ({"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"})[char]);
    return escaped
      .replace(/```([\s\S]*?)```/g, "<pre><code>$1</code></pre>")
      .replace(/`([^`]+)`/g, "<code>$1</code>")
      .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
      .replace(/^[-•]\s+(.+)$/gm, "<div class=\"rich-item\">• $1</div>")
      .replace(/\n/g, "<br>");
  }

  function addMessage(role, text) {
    const message = document.createElement("div");
    message.className = `message ${role === "user" ? "user-message" : "assistant-message"}`;
    if (role === "assistant") message.innerHTML = renderSafeRichText(text);
    else message.textContent = text;
    conversation.append(message);
    conversation.scrollTop = conversation.scrollHeight;
  }

  async function askAssistant(question) {
    const config = loadConfig();
    if (!config.baseUrl || !config.apiKey || !config.model) {
      showToast("请先填写 API 地址、Key 和模型名称。 ");
      switchAssistantPanel("settings");
      return;
    }

    addMessage("user", question);
    sessionMessages.push({ role: "user", content: question });
    sendButton.disabled = true;
    sendButton.textContent = "思考中…";

    const systemMessage = {
      role: "system",
      content: "你是课程页面助教。仅依据当前页面上下文回答；不知道时直接说明，不补造事实。回答简洁、清楚。",
    };
    const contextMessage = {
      role: "user",
      content: `${getCurrentSlideContext()}\n\n学员问题：${question}`,
    };
    const endpoint = `${config.baseUrl.replace(/\/$/, "")}/chat/completions`;
    const controller = new AbortController();
    const timeout = window.setTimeout(() => controller.abort(), 30000);

    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${config.apiKey}`,
        },
        body: JSON.stringify({
          model: config.model,
          messages: [systemMessage, ...sessionMessages.slice(-6, -1), contextMessage],
          temperature: 0.3,
        }),
        signal: controller.signal,
      });
      if (response.status === 401 || response.status === 403) throw new Error("认证失败，请检查 API Key 和服务地址");
      if (response.status === 429) throw new Error("请求过于频繁，请稍后重试");
      if (!response.ok) throw new Error(`服务请求失败（${response.status}）`);
      const data = await response.json();
      const answer = data?.choices?.[0]?.message?.content?.trim();
      if (!answer) throw new Error("模型没有返回可显示的内容");
      addMessage("assistant", answer);
      sessionMessages.push({ role: "assistant", content: answer });
    } catch (error) {
      const message = error.name === "AbortError" ? "请求超时，请检查网络或缩短问题" : (error.message || "网络请求失败，浏览器可能受 CORS 限制");
      addMessage("assistant", `${message}。如果从 file:// 打开失败，请改用本地 HTTP 服务。`);
    } finally {
      window.clearTimeout(timeout);
      sendButton.disabled = false;
      sendButton.textContent = "发送";
    }
  }

  function clearConversation() {
    sessionMessages.length = 0;
    conversation.replaceChildren();
    addMessage("assistant", "对话已清空。我仍然只读取当前页面。 ");
  }

  byId("prevSlide").addEventListener("click", () => move(-1));
  byId("nextSlide").addEventListener("click", () => move(1));
  byId("fullscreenToggle").addEventListener("click", toggleFullscreen);
  assistantToggle.addEventListener("click", openAssistant);
  byId("assistantClose").addEventListener("click", closeAssistant);
  byId("chatTab").addEventListener("click", () => switchAssistantPanel("chat"));
  byId("settingsTab").addEventListener("click", () => switchAssistantPanel("settings"));
  byId("assistantSettings").addEventListener("submit", saveConfig);
  byId("clearSettings").addEventListener("click", clearConfig);
  byId("clearConversation").addEventListener("click", clearConversation);
  byId("assistantProvider").addEventListener("change", (event) => {
    const preset = providers[event.target.value];
    if (!preset || event.target.value === "custom") return;
    byId("assistantBaseUrl").value = preset.baseUrl;
    byId("assistantModel").value = preset.model;
  });
  byId("assistantForm").addEventListener("submit", (event) => {
    event.preventDefault();
    const question = assistantInput.value.trim();
    if (!question) return;
    assistantInput.value = "";
    askAssistant(question);
  });
  document.querySelectorAll("[data-quick]").forEach((button) => {
    button.addEventListener("click", () => askAssistant(button.dataset.quick));
  });

  window.addEventListener("hashchange", () => renderSlide(parseHash(), false));
  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape" && drawer.classList.contains("is-open")) return closeAssistant();
    if (/INPUT|TEXTAREA/.test(document.activeElement?.tagName)) return;
    if (["ArrowRight", "PageDown", " "].includes(event.key)) { event.preventDefault(); move(1); }
    if (["ArrowLeft", "PageUp"].includes(event.key)) { event.preventDefault(); move(-1); }
    if (event.key === "Home") { event.preventDefault(); renderSlide(0); }
    if (event.key === "End") { event.preventDefault(); renderSlide(total - 1); }
  });
  document.addEventListener("fullscreenchange", () => {
    byId("fullscreenToggle").textContent = document.fullscreenElement ? "退出全屏" : "全屏";
  });

  fillConfig();
  renderSlide(current, !location.hash);
})();
