// retoor <retoor@molodetz.nl>

import { assetUrl } from "../assetVersion.js";

const TAG_RE = /^ai-[a-z0-9-]+$/;

const ALLOWLIST = {
  "ai-interaction": "AiInteraction.js",
  "ai-confirm": "AiConfirm.js",
  "ai-choice": "AiChoice.js",
  "ai-choice-multi": "AiChoiceMulti.js",
  "ai-field": "AiField.js",
  "ai-select": "AiSelect.js",
  "ai-option": "AiOption.js",
  "ai-group": "AiGroup.js",
  "ai-help": "AiHelp.js",
  "ai-actions": "AiActions.js",
  "ai-status": "AiStatus.js",
  "ai-markdown-fallback": "AiMarkdownFallback.js",
};

export class AiAutoload {
  constructor(root = document) {
    this.root = root;
    this.loading = new Map();
    this.failed = new Set();
    this._observer = null;
  }

  start() {
    this.scan(this.root);
    if (this._observer) return;
    this._observer = new MutationObserver((records) => {
      for (const record of records) {
        for (const node of record.addedNodes) {
          if (node.nodeType !== 1) continue;
          this.scan(node);
        }
      }
    });
    this._observer.observe(this.root === document ? document.documentElement : this.root, {
      childList: true,
      subtree: true,
    });
  }

  stop() {
    if (this._observer) {
      this._observer.disconnect();
      this._observer = null;
    }
  }

  scan(root) {
    if (!root || root.nodeType !== 1) return;
    const nodes = [];
    if (TAG_RE.test(root.localName || "")) nodes.push(root);
    if (root.querySelectorAll) {
      nodes.push(...root.querySelectorAll("*"));
    }
    for (const el of nodes) {
      const name = el.localName || "";
      if (!TAG_RE.test(name)) continue;
      this.ensure(name, el).catch(() => null);
    }
  }

  ensure(tag, el = null) {
    if (customElements.get(tag)) return Promise.resolve();
    if (this.loading.has(tag)) {
      return this.loading.get(tag);
    }
    const file = ALLOWLIST[tag];
    if (!file) {
      this.failed.add(tag);
      this._markFailed(el, tag);
      return Promise.reject(new Error(`ai widget not allowlisted: ${tag}`));
    }
    const url = assetUrl(`/static/js/components/${file}`);
    const task = import(url)
      .then(() => {
        this.loading.delete(tag);
        this.failed.delete(tag);
        if (!customElements.get(tag)) {
          return customElements.whenDefined(tag);
        }
        return undefined;
      })
      .catch((error) => {
        this.loading.delete(tag);
        this.failed.add(tag);
        console.error("AiAutoload failed", tag, url, error);
        this._markFailed(el, tag);
        window.setTimeout(() => this.failed.delete(tag), 3000);
        throw error;
      });
    this.loading.set(tag, task);
    return task;
  }

  _markFailed(el, tag) {
    if (!el || !el.isConnected || !el.replaceWith) return;
    if (el.getAttribute("data-ai-load-failed") === tag) return;
    el.setAttribute("data-ai-load-failed", tag);
    const status = document.createElement("div");
    status.className = "ai-status";
    status.dataset.state = "error";
    status.setAttribute("aria-live", "polite");
    status.textContent = `Widget failed to load (${tag})`;
    el.replaceWith(status);
  }
}

export default AiAutoload;
