/**
 * Vanna chat embed bootstrap — fonte única do JS de wiring exigido pelo
 * <vanna-chat>: theme pierce (adoptedStyleSheets), tradutor PT-BR
 * (MutationObserver), markdown processor pros balões, fonts, e load do
 * bundle.
 *
 * Servido pelo Vanna server em /vanna-embed-bootstrap.js. Consumido por:
 *   - static/embed-demo.html (smoke-test page, mesmo origin)
 *   - clubpetro-frontend/.../vannaChatLoader.ts (React app, cross-origin)
 *
 * API pública:
 *   window.VannaEmbed.ensureLoaded({ baseUrl, extraCss? }) -> Promise<void>
 *     - baseUrl: "" pra mesma origem (demo); URL completa do Vanna server
 *       quando embedado em outra origem (React app).
 *     - extraCss: CSS opcional appended ao theme sheet (ex.: override do
 *       avatar logo quando o SVG mora na origem da app, não na do Vanna).
 *     - Idempotente: chamadas subsequentes retornam a mesma Promise.
 */
(function () {
  if (window.VannaEmbed) return;

  let loaderPromise = null;

  const escapeHtml = (s) =>
    s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

  const mdToHtml = (md) => {
    if (!md) return "";
    let s = escapeHtml(md);
    s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
    s = s.replace(/\*\*\*([^*]+)\*\*\*/g, "<strong><em>$1</em></strong>");
    s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
    s = s.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, "$1<em>$2</em>");
    s = s.replace(/(^|[^\w])_([^_\n]+)_(?!\w)/g, "$1<em>$2</em>");
    s = s.replace(
      /\[([^\]]+)\]\(([^)]+)\)/g,
      '<a href="$2" target="_blank" rel="noopener">$1</a>'
    );
    // Headers ANTES das listas. A regex de lista consome o \n final do
    // último item; rodar headers depois deixaria `### Conclusão` logo
    // após um bloco de lista precedido só de `</ul>` (sem \n) e a regex
    // de header (que exige ^ ou \n antes do #) falharia.
    s = s.replace(
      /(^|\n)(#{1,6})\s+([^\n]+)/g,
      (_, lead, hashes, text) =>
        `${lead}<h${hashes.length}>${text.trim()}</h${hashes.length}>`
    );
    s = s.replace(/(?:^|\n)((?:[-*]\s+.+(?:\n|$))+)/g, (_, block) => {
      const items = block
        .trim()
        .split(/\n/)
        .map((l) => l.replace(/^[-*]\s+/, "").trim())
        .map((t) => `<li>${t}</li>`)
        .join("");
      return `\n<ul>${items}</ul>\n`;
    });
    s = s.replace(/\n/g, "<br>");
    s = s.replace(/<br>\s*(<\/?(?:ul|li|h[1-6])>)/g, "$1");
    s = s.replace(/(<\/?(?:ul|li|h[1-6])>)\s*<br>/g, "$1");
    return s;
  };

  const EXACT = {
    "Search...": "Buscar...",
    "Export": "Baixar",
    "📥 Export": "📥 Baixar",
    "Export to CSV": "Baixar para CSV",
  };
  const REGEX = [
    [/^(\d+)\s+rows?$/i, (_, n) => `${n} ${n === "1" ? "linha" : "linhas"}`],
    [/^(\d+)\s+columns?$/i, (_, n) => `${n} ${n === "1" ? "coluna" : "colunas"}`],
    [
      /^Showing\s+(\d+)\s+of\s+(\d+)\s+rows?$/i,
      (_, a, b) => `Mostrando ${a} de ${b} ${b === "1" ? "linha" : "linhas"}`,
    ],
  ];
  const translate = (s) => {
    if (typeof s !== "string") return s;
    const t = s.trim();
    if (!t) return s;
    if (EXACT[t]) return s.replace(t, EXACT[t]);
    for (const [re, fn] of REGEX) {
      const m = t.match(re);
      if (m) return s.replace(t, fn(...m));
    }
    return s;
  };

  const setupThemePierce = (baseUrl, extraCss) => {
    if (window.__vannaThemeSheet) return;
    const themeSheet = new CSSStyleSheet();
    window.__vannaThemeSheet = themeSheet;

    const desc = Object.getOwnPropertyDescriptor(
      ShadowRoot.prototype,
      "adoptedStyleSheets"
    );
    if (desc && desc.set) {
      Object.defineProperty(ShadowRoot.prototype, "adoptedStyleSheets", {
        configurable: true,
        enumerable: true,
        get() {
          return desc.get.call(this);
        },
        set(value) {
          const without = (value || []).filter((s) => s !== themeSheet);
          desc.set.call(this, [...without, themeSheet]);
        },
      });
    }

    const merge = (css) => (extraCss ? css + "\n" + extraCss : css);
    fetch(baseUrl + "/vanna-theme.css?t=" + Date.now())
      .then((r) => r.text())
      .then((css) => themeSheet.replaceSync(merge(css)))
      .catch(() => {
        if (extraCss) themeSheet.replaceSync(extraCss);
      });
  };

  const setupTranslator = () => {
    if (window.__vannaTranslatorArmed) return;
    window.__vannaTranslatorArmed = true;

    const isInsideMessageContent = (n) =>
      !!n &&
      (n.classList?.contains("message-content") ||
        n.closest?.(".message-content"));

    const processMessageContent = (el) => {
      const text = el.textContent || "";
      if (el.dataset.bare === text) return;
      el.innerHTML = mdToHtml(text);
      el.dataset.bare = el.textContent || "";
    };

    const walkAndTranslate = (root) => {
      const tw = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
      let node;
      while ((node = tw.nextNode())) {
        const next = translate(node.nodeValue);
        if (next !== node.nodeValue) node.nodeValue = next;
      }
      root.querySelectorAll?.("[placeholder], [title]")?.forEach((el) => {
        for (const attr of ["placeholder", "title"]) {
          const v = el.getAttribute(attr);
          if (v == null) continue;
          const next = translate(v);
          if (next !== v) el.setAttribute(attr, next);
        }
      });
      root
        .querySelectorAll?.(".message-content")
        ?.forEach(processMessageContent);
    };

    const observers = new WeakSet();
    const attachObserver = (root) => {
      if (observers.has(root)) return;
      observers.add(root);
      walkAndTranslate(root);
      new MutationObserver((muts) => {
        for (const m of muts) {
          const target = m.target;
          const inMd = isInsideMessageContent(target);
          if (m.type === "characterData") {
            if (inMd) {
              const host = target.parentElement?.closest?.(".message-content");
              if (host) processMessageContent(host);
            } else {
              const next = translate(target.nodeValue);
              if (next !== target.nodeValue) target.nodeValue = next;
            }
          } else if (m.type === "attributes") {
            const v = target.getAttribute(m.attributeName);
            const next = translate(v);
            if (next !== v) target.setAttribute(m.attributeName, next);
          } else {
            m.addedNodes.forEach((n) => {
              if (n.nodeType === 3) {
                if (inMd) {
                  const host = target.closest?.(".message-content");
                  if (host) processMessageContent(host);
                } else {
                  const next = translate(n.nodeValue);
                  if (next !== n.nodeValue) n.nodeValue = next;
                }
              } else if (n.nodeType === 1) {
                walkAndTranslate(n);
                if (n.shadowRoot) attachObserver(n.shadowRoot);
              }
            });
          }
        }
      }).observe(root, {
        childList: true,
        subtree: true,
        characterData: true,
        attributes: true,
        attributeFilter: ["placeholder", "title"],
      });
    };

    const origAttach = Element.prototype.attachShadow;
    Element.prototype.attachShadow = function (init) {
      const root = origAttach.call(this, init);
      queueMicrotask(() => attachObserver(root));
      return root;
    };
  };

  // Wrap window.ResizeObserver pra capturar errors NA ORIGEM. Plotly tem
  // um RO interno (vanna-components.js:113878) que dispara `_t [as relayout]`
  // (kt at 22888) num node desmontado durante minimize/maximize do chat,
  // e o crash propaga como `TypeError: Cannot read properties of undefined
  // (reading 'width' / 'height')`. Listeners em window.error registrados
  // depois do webpack-dev-server (caso CRA) não conseguem barrar o overlay
  // porque o handler do dev server roda antes em capture phase. Try/catch
  // dentro da própria callback garante que o error nunca escapa pra window.
  // Só engole o erro específico (filename + message bate); qualquer outro
  // erro continua propagando normal.
  const wrapResizeObserver = () => {
    if (window.__vannaResizeObserverWrapped) return;
    window.__vannaResizeObserverWrapped = true;
    const Original = window.ResizeObserver;
    if (!Original) return;

    const isPlotlyCrash = (err) => {
      if (!err) return false;
      const msg = (err && err.message) || String(err);
      const stack = (err && err.stack) || "";
      const widthHeight =
        msg.indexOf("reading 'width'") !== -1 ||
        msg.indexOf("reading 'height'") !== -1 ||
        msg.indexOf("ResizeObserver loop") !== -1;
      const fromBundle = stack.indexOf("vanna-components") !== -1;
      return widthHeight && fromBundle;
    };

    const Wrapped = function (callback) {
      const safeCallback = function (entries, observer) {
        try {
          callback.call(this, entries, observer);
        } catch (err) {
          if (!isPlotlyCrash(err)) throw err;
        }
      };
      return new Original(safeCallback);
    };
    Wrapped.prototype = Original.prototype;
    window.ResizeObserver = Wrapped;
  };

  const loadFontsOnce = () => {
    if (window.__vannaFontsLoaded) return;
    window.__vannaFontsLoaded = true;
    const links = [
      { rel: "preconnect", href: "https://fonts.googleapis.com" },
      {
        rel: "preconnect",
        href: "https://fonts.gstatic.com",
        crossorigin: "anonymous",
      },
      {
        rel: "stylesheet",
        href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap",
      },
    ];
    for (const cfg of links) {
      const link = document.createElement("link");
      Object.entries(cfg).forEach(([k, v]) => link.setAttribute(k, v));
      document.head.appendChild(link);
    }
  };

  const injectBundle = (baseUrl) =>
    new Promise((resolve, reject) => {
      const sel = 'script[data-vanna-bundle="' + baseUrl + '"]';
      const existing = document.querySelector(sel);
      if (existing) {
        if (existing.__loaded) return resolve();
        existing.addEventListener("load", () => resolve());
        existing.addEventListener("error", () =>
          reject(new Error("vanna bundle failed to load"))
        );
        return;
      }
      const script = document.createElement("script");
      script.type = "module";
      script.src = baseUrl + "/static/vanna-components.js";
      script.dataset.vannaBundle = baseUrl;
      script.onload = () => {
        script.__loaded = true;
        resolve();
      };
      script.onerror = () =>
        reject(new Error("vanna bundle failed to load"));
      document.head.appendChild(script);
    });

  window.VannaEmbed = {
    ensureLoaded(config) {
      if (loaderPromise) return loaderPromise;
      const baseUrl = (config && config.baseUrl) || "";
      const extraCss = config && config.extraCss;
      loaderPromise = (async () => {
        wrapResizeObserver();
        setupThemePierce(baseUrl, extraCss);
        setupTranslator();
        loadFontsOnce();
        await injectBundle(baseUrl);
        await customElements.whenDefined("vanna-chat");
      })();
      return loaderPromise;
    },
  };
})();
