/**
 * Marked Help Assistant — search-only "chat" over search.json (no LLM).
 */
(function () {
  "use strict";

  function isInternalHelp() {
    return document.body && document.body.classList.contains("internal");
  }

  function helpPageHref(page) {
    if (isInternalHelp()) {
      return page + ".html";
    }
    return page;
  }

  function helpResultHref(loc) {
    if (!loc || /^https?:\/\//i.test(loc)) {
      return loc;
    }
    if (!isInternalHelp()) {
      return loc;
    }
    return loc.replace(/^([^#?]+)(#.*)?$/, function (_match, page, fragment) {
      var href = /\.html$/i.test(page) ? page : page + ".html";
      return href + (fragment || "");
    });
  }

  // Common English stop words — ignored when parsing natural-language questions.
  var STOP_WORDS = {
    a: 1,
    about: 1,
    an: 1,
    and: 1,
    are: 1,
    as: 1,
    at: 1,
    be: 1,
    been: 1,
    being: 1,
    best: 1,
    but: 1,
    by: 1,
    can: 1,
    could: 1,
    did: 1,
    do: 1,
    does: 1,
    doing: 1,
    for: 1,
    from: 1,
    had: 1,
    has: 1,
    have: 1,
    having: 1,
    he: 1,
    her: 1,
    here: 1,
    him: 1,
    his: 1,
    how: 1,
    i: 1,
    if: 1,
    in: 1,
    into: 1,
    is: 1,
    it: 1,
    its: 1,
    just: 1,
    know: 1,
    me: 1,
    might: 1,
    more: 1,
    most: 1,
    much: 1,
    must: 1,
    my: 1,
    need: 1,
    no: 1,
    nor: 1,
    not: 1,
    of: 1,
    off: 1,
    on: 1,
    or: 1,
    other: 1,
    our: 1,
    out: 1,
    over: 1,
    own: 1,
    please: 1,
    same: 1,
    she: 1,
    should: 1,
    so: 1,
    some: 1,
    such: 1,
    tell: 1,
    than: 1,
    that: 1,
    the: 1,
    their: 1,
    them: 1,
    then: 1,
    there: 1,
    these: 1,
    they: 1,
    this: 1,
    those: 1,
    through: 1,
    to: 1,
    too: 1,
    under: 1,
    up: 1,
    us: 1,
    very: 1,
    want: 1,
    was: 1,
    way: 1,
    we: 1,
    were: 1,
    what: 1,
    when: 1,
    where: 1,
    which: 1,
    who: 1,
    whom: 1,
    whose: 1,
    why: 1,
    will: 1,
    with: 1,
    would: 1,
    you: 1,
    your: 1
  };

  var REPLACE = {
    scriviner: "scrivener",
    javscript: "javascript",
    js: "javascript",
    cmark: "commonmark"
  };

  var STEMS = [
    { word: "e-mail", stem: "email" },
    { word: "javascript", stem: "script" },
    { word: "javascript", stem: "js" }
  ];

  var SUGGESTIONS = [
    "How do I export to PDF?",
    "How much does Marked cost?",
    "Where do I submit a bug report?",
    "What Markdown processors are available?"
  ];

  function isExternalURL(url) {
    return /^https?:\/\//i.test(String(url || ""));
  }

  function externalLinkAttrs(url) {
    return isExternalURL(url)
      ? ' target="_blank" rel="noopener noreferrer"'
      : "";
  }

  function externalHost(url) {
    if (!isExternalURL(url)) {
      return "";
    }
    try {
      return new URL(url).hostname.replace(/^www\./, "");
    } catch (e) {
      return "markedapp.com";
    }
  }

  function escapeHtml(text) {
    return String(text)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  function escapeRegExp(text) {
    return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }

  function stemWord(word) {
    var out = [word];
    STEMS.forEach(function (entry) {
      if (entry.word === word) {
        out.push(entry.stem);
      }
    });
    return out;
  }

  function queryTerms(raw) {
    var q = String(raw || "")
      .toLowerCase()
      .replace(/[^\w\s\u00C0-\u00FF-]/g, " ");
    var parts = q.split(/\s+/).filter(Boolean);
    var terms = [];

    parts.forEach(function (part) {
      if (part.length < 2 || STOP_WORDS[part]) {
        return;
      }
      var word = REPLACE[part] || part;
      stemWord(word).forEach(function (term) {
        if (terms.indexOf(term) === -1) {
          terms.push(term);
        }
      });
    });

    return terms;
  }

  function fieldMatches(field, term) {
    if (!field) {
      return false;
    }
    return new RegExp("\\b" + escapeRegExp(term), "i").test(field);
  }

  function scoreMatch(base, index) {
    return base - index;
  }

  function collectHits(pages, terms, maxResults) {
    var hits = [];
    var i;
    var page;
    var score;
    var t;
    var term;
    var child;
    var c;

    for (i = 0; i < pages.length; i++) {
      page = pages[i];
      score = 1000000000;

      for (t = 0; t < terms.length; t++) {
        term = terms[t];

        if (fieldMatches(page.title, term)) {
          score = Math.min(score, scoreMatch(400000, i));
        }
        if (fieldMatches(page.text, term)) {
          score = Math.min(score, scoreMatch(150000, i));
        }
        if (fieldMatches(page.tags, term)) {
          score = Math.min(score, scoreMatch(100000, i));
        }

        if (page.children && page.children.length) {
          for (c = 0; c < page.children.length; c++) {
            child = page.children[c];
            if (fieldMatches(child.title, term)) {
              score = Math.min(score, scoreMatch(380000, i) - c);
            }
            if (fieldMatches(child.text, term)) {
              score = Math.min(score, scoreMatch(320000, i) - c);
            }
          }
        }
      }

      if (score < 1000000000) {
        hits.push({ score: score, page: page });
      }
    }

    hits.sort(function (a, b) {
      return a.score - b.score;
    });

    return hits.slice(0, maxResults);
  }

  function bestChild(page, terms) {
    var best = null;
    var bestScore = 1000000000;
    var c;
    var child;
    var score;
    var t;
    var term;

    if (!page.children || !page.children.length) {
      return null;
    }

    for (c = 0; c < page.children.length; c++) {
      child = page.children[c];
      score = 1000000000;

      for (t = 0; t < terms.length; t++) {
        term = terms[t];
        if (fieldMatches(child.title, term)) {
          score = Math.min(score, 1000 - c);
        }
        if (fieldMatches(child.text, term)) {
          score = Math.min(score, 2000 - c);
        }
      }

      if (score < bestScore) {
        bestScore = score;
        best = child;
      }
    }

    return best;
  }

  function highlightTerms(text, terms) {
    var out = escapeHtml(text);
    terms.forEach(function (term) {
      var pat = new RegExp("\\b(" + escapeRegExp(term) + ")", "gi");
      out = out.replace(pat, "<strong>$1</strong>");
    });
    return out;
  }

  function snippet(text, terms, wordLimit) {
    var source = String(text || "")
      .replace(/\s+/g, " ")
      .trim();
    var lower = source.toLowerCase();
    var pos = -1;
    var t;
    var term;
    var start;
    var words;
    var snippetText;

    if (!source) {
      return "";
    }

    for (t = 0; t < terms.length; t++) {
      term = terms[t];
      pos = lower.search(new RegExp("\\b" + escapeRegExp(term), "i"));
      if (pos !== -1) {
        break;
      }
    }

    if (pos === -1) {
      start = 0;
    } else {
      start = Math.max(0, pos - 40);
    }

    snippetText = source.slice(start);
    if (start > 0) {
      snippetText = "…" + snippetText;
    }

    words = snippetText.split(/\s+/);
    if (words.length > wordLimit) {
      snippetText = words.slice(0, wordLimit).join(" ") + " …";
    }

    return highlightTerms(snippetText, terms);
  }

  function formatHit(hit, terms) {
    var page = hit.page;
    var child = bestChild(page, terms);
    var title = child && child.title ? child.title : page.title;
    var loc = child && child.loc ? child.loc : page.loc;
    var body = child && child.text ? child.text : page.text;

    return {
      title: title,
      loc: loc,
      snippet: snippet(body, terms, 28)
    };
  }

  function searchJsonURL() {
    var link = document.querySelector('link[href*="help-assistant.css"]');
    if (link) {
      return link
        .getAttribute("href")
        .replace(/help-assistant\.css.*$/, "search.json");
    }
    return "search.json";
  }

  function askPageURL(query) {
    var q = String(query || "").trim();
    var url = helpPageHref("ask");
    if (!q) {
      return url;
    }
    return url + "?q=" + encodeURIComponent(q);
  }

  function fetchJson(url) {
    function parseXhr(xhr) {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
        return JSON.parse(xhr.responseText);
      }
      throw new Error("Could not load search index");
    }

    function loadViaXhr() {
      return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.onload = function () {
          try {
            resolve(parseXhr(xhr));
          } catch (err) {
            reject(err);
          }
        };
        xhr.onerror = function () {
          reject(new Error("Could not load search index"));
        };
        xhr.send();
      });
    }

    if (typeof fetch !== "function") {
      return loadViaXhr();
    }

    return fetch(url)
      .then(function (response) {
        if (!response.ok) {
          throw new Error("Could not load search index");
        }
        return response.json();
      })
      .catch(function () {
        return loadViaXhr();
      });
  }

  var HelpAssistant = {
    index: null,
    loadPromise: null,
    maxResults: 5,
    _panelState: null,

    loadIndex: function () {
      var self = this;

      if (self.index) {
        return Promise.resolve(self.index);
      }
      if (self.loadPromise) {
        return self.loadPromise;
      }

      self.loadPromise = fetchJson(searchJsonURL())
        .then(function (data) {
          self.index = data.pages || [];
          return self.index;
        });

      return self.loadPromise;
    },

    answer: function (query) {
      var self = this;
      var terms = queryTerms(query);

      if (!String(query || "").trim()) {
        return Promise.resolve({
          type: "prompt",
          message:
            "Ask a question about Marked and I will search the help docs and markedapp.com."
        });
      }

      if (!terms.length) {
        return Promise.resolve({
          type: "empty",
          message:
            "Try a more specific question (at least one meaningful word)."
        });
      }

      return self.loadIndex().then(function (pages) {
        var hits = collectHits(pages, terms, self.maxResults);

        if (!hits.length) {
          return {
            type: "none",
            message:
              'I could not find anything matching that in the Marked help or website. Try different keywords or browse the <a href="' +
              helpPageHref("contents") +
              '">full index</a>.',
            query: query
          };
        }

        return {
          type: "results",
          query: query,
          results: hits.map(function (hit) {
            return formatHit(hit, terms);
          })
        };
      });
    },

    renderUserBubble: function (text) {
      return (
        '<div class="mha-message mha-message-user"><div class="mha-bubble">' +
        escapeHtml(text) +
        "</div></div>"
      );
    },

    renderBotBubble: function (html) {
      return (
        '<div class="mha-message mha-message-bot"><div class="mha-bubble">' +
        html +
        "</div></div>"
      );
    },

    renderResponse: function (response) {
      if (response.type === "results") {
        var html =
          '<p>Here are the most relevant topics I found:</p><ul class="mha-results">';
        response.results.forEach(function (result) {
          var host = externalHost(result.loc);
          html +=
            '<li><a href="' +
            escapeHtml(helpResultHref(result.loc)) +
            '"' +
            externalLinkAttrs(result.loc) +
            "><strong>" +
            escapeHtml(result.title) +
            "</strong></a>";
          if (host) {
            html +=
              ' <span class="mha-external-badge">' +
              escapeHtml(host) +
              "</span>";
          }
          if (result.snippet) {
            html += '<p class="mha-snippet">' + result.snippet + "</p>";
          }
          html += "</li>";
        });
        html +=
          '</ul><p class="mha-footnote">These answers come from the Marked help index and markedapp.com, not an AI model. <a href="' +
          helpPageHref("search") +
          "?q=" +
          encodeURIComponent(response.query) +
          '">View all search results</a></p>';
        return html;
      }

      return "<p>" + response.message + "</p>";
    },

    welcomeHTML: function () {
      var html =
        "<p>Hi! I search the Marked help documentation and markedapp.com. Ask a question below.</p>";
      html +=
        '<p class="mha-suggestions-label">Try:</p><ul class="mha-suggestions">';
      SUGGESTIONS.forEach(function (q) {
        html +=
          '<li><button type="button" class="mha-suggestion" data-query="' +
          escapeHtml(q) +
          '">' +
          escapeHtml(q) +
          "</button></li>";
      });
      html += "</ul>";
      return html;
    },

    bindSuggestions: function (root, onAsk) {
      root.querySelectorAll(".mha-suggestion").forEach(function (btn) {
        btn.addEventListener("click", function () {
          onAsk(btn.getAttribute("data-query"));
        });
      });
    },

    createChatUI: function (container, options) {
      var self = this;
      var messages = document.createElement("div");
      messages.className = "mha-messages";
      messages.innerHTML = self.renderBotBubble(self.welcomeHTML());

      var form = document.createElement("form");
      form.className = "mha-form";
      form.innerHTML =
        '<input type="text" class="mha-input" placeholder="Ask about Marked…" autocomplete="off" aria-label="Question">' +
        '<button type="submit" class="mha-send">Ask</button>';

      var input = form.querySelector(".mha-input");
      var busy = false;

      function focusInput() {
        if (typeof input.focus === "function") {
          try {
            input.focus({ preventScroll: true });
          } catch (err) {
            input.focus();
          }
        }
      }

      function scrollMessageToTop(messageEl) {
        if (!messageEl || !messages) {
          return;
        }

        function applyScroll() {
          var containerTop = messages.getBoundingClientRect().top;
          var messageTop = messageEl.getBoundingClientRect().top;
          messages.scrollTop += messageTop - containerTop;
        }

        window.requestAnimationFrame(function () {
          applyScroll();
          window.requestAnimationFrame(applyScroll);
        });
      }

      function ask(question) {
        var q = String(question || "").trim();
        if (!q || busy) {
          return;
        }

        busy = true;
        input.value = "";
        messages.insertAdjacentHTML("beforeend", self.renderUserBubble(q));
        var userMessageEl = messages.lastElementChild;
        messages.insertAdjacentHTML(
          "beforeend",
          self.renderBotBubble('<p class="mha-thinking">Searching…</p>')
        );
        scrollMessageToTop(userMessageEl);

        self
          .answer(q)
          .then(function (response) {
            messages.removeChild(messages.lastElementChild);
            messages.insertAdjacentHTML(
              "beforeend",
              self.renderBotBubble(self.renderResponse(response))
            );
            self.bindSuggestions(messages, ask);
            scrollMessageToTop(userMessageEl);
          })
          .catch(function () {
            messages.removeChild(messages.lastElementChild);
            messages.insertAdjacentHTML(
              "beforeend",
              self.renderBotBubble(
                '<p>Sorry, the help index could not be loaded. Try the <a href="' +
                helpPageHref("search") +
                '">search page</a>.</p>'
              )
            );
            scrollMessageToTop(userMessageEl);
          })
          .then(function () {
            busy = false;
            focusInput();
          });
      }

      form.addEventListener("submit", function (event) {
        event.preventDefault();
        ask(input.value);
      });

      container.appendChild(messages);
      container.appendChild(form);
      self.bindSuggestions(messages, ask);

      if (options && options.prepopulateQuery) {
        input.value = options.prepopulateQuery;
      }

      if (options && options.initialQuery) {
        ask(options.initialQuery);
      }

      return { ask: ask, focus: focusInput, input: input };
    },

    initWidget: function () {
      var self = this;
      var chevronSvg =
        '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="18 15 12 9 6 15"></polyline></svg>';

      var dock = document.createElement("div");
      dock.className = "mha-dock";
      dock.id = "mha-dock";
      dock.innerHTML =
        '<button type="button" class="mha-dock-ask" aria-expanded="false" aria-controls="mha-panel">Ask</button>' +
        '<span class="mha-dock-divider" aria-hidden="true"></span>' +
        '<button type="button" class="mha-dock-top" title="Back to top" aria-label="Back to top">' +
        chevronSvg +
        "</button>";

      var askBtn = dock.querySelector(".mha-dock-ask");
      var topBtn = dock.querySelector(".mha-dock-top");

      function onScroll() {
        if (window.scrollY > 300) {
          dock.classList.add("mha-dock-scrolled");
        } else {
          dock.classList.remove("mha-dock-scrolled");
        }
      }

      window.addEventListener("scroll", onScroll, { passive: true });
      onScroll();

      topBtn.addEventListener("click", function (event) {
        event.preventDefault();
        if (window.scrollTo) {
          try {
            window.scrollTo({ top: 0, behavior: "smooth" });
          } catch (err) {
            window.scrollTo(0, 0);
          }
        } else {
          document.documentElement.scrollTop = 0;
          document.body.scrollTop = 0;
        }
      });

      var panel = document.createElement("div");
      panel.id = "mha-panel";
      panel.className = "mha-panel";
      panel.hidden = true;
      panel.setAttribute("aria-hidden", "true");
      panel.innerHTML =
        '<header class="mha-header">' +
        "<span>Marked Help</span>" +
        '<div class="mha-header-actions">' +
        '<button type="button" class="mha-expand" aria-label="Open full Ask page" title="Full page">' +
        "&#x26F6;" +
        "</button>" +
        '<button type="button" class="mha-close" aria-label="Close">×</button>' +
        "</div>" +
        '</header><div class="mha-chat"></div>';

      var chatRoot = panel.querySelector(".mha-chat");
      var chat;

      function openPanel(initialQuery) {
        var q = String(initialQuery || "").trim();
        panel.hidden = false;
        panel.setAttribute("aria-hidden", "false");
        askBtn.setAttribute("aria-expanded", "true");
        dock.classList.add("mha-dock-open");
        if (!chat) {
          chat = self.createChatUI(
            chatRoot,
            q ? { initialQuery: q } : undefined
          );
        } else if (q) {
          chat.ask(q);
        }
        chat.focus();
      }

      function closePanel() {
        panel.hidden = true;
        panel.setAttribute("aria-hidden", "true");
        askBtn.setAttribute("aria-expanded", "false");
        dock.classList.remove("mha-dock-open");
        askBtn.focus();
      }

      self._panelState = {
        open: openPanel,
        close: closePanel,
        isOpen: function () {
          return !panel.hidden;
        }
      };

      askBtn.addEventListener("click", function () {
        if (panel.hidden) {
          openPanel();
        } else {
          closePanel();
        }
      });

      panel.querySelector(".mha-close").addEventListener("click", function () {
        closePanel();
      });

      panel.querySelector(".mha-expand").addEventListener("click", function () {
        var draft = "";
        if (chat && chat.input) {
          draft = chat.input.value;
        } else {
          var pending = panel.querySelector(".mha-input");
          draft = pending ? pending.value : "";
        }
        window.location.href = askPageURL(draft);
      });

      panel.addEventListener("keydown", function (event) {
        if (event.key === "Escape" && !panel.hidden) {
          event.preventDefault();
          closePanel();
        }
      });

      document.body.appendChild(dock);
      document.body.appendChild(panel);
    },

    openWidget: function (initialQuery) {
      if (this._panelState) {
        this._panelState.open(initialQuery);
      }
    },

    closeWidget: function () {
      if (this._panelState) {
        this._panelState.close();
      }
    },

    bindPageHooks: function () {
      var self = this;

      document.querySelectorAll(".mha-open-widget").forEach(function (link) {
        link.addEventListener("click", function (event) {
          event.preventDefault();
          self.openWidget();
        });
      });
    },

    initPage: function () {
      var root = document.getElementById("mha-page-chat");
      if (!root) {
        return;
      }

      var params = new URLSearchParams(window.location.search);
      var initial = params.get("q") || "";
      var chat = this.createChatUI(root, { prepopulateQuery: initial });
      if (initial) {
        chat.focus();
      }
    }
  };

  window.MarkedHelpAssistant = HelpAssistant;

  document.addEventListener("DOMContentLoaded", function () {
    var page = document.body.getAttribute("data-page");
    if (page === "ask") {
      HelpAssistant.initPage();
    } else if (page !== "search") {
      HelpAssistant.initWidget();
    }
    HelpAssistant.bindPageHooks();
  });
})();
