import { UIElement } from '../../core/element.js';
// NOTE: the A2UI renderer (<a2ui-root>) lives in @adia-ai/web-modules and is
// loaded lazily + guarded inside #ensureRenderer() (kicked off from connected()).
// Two constraints shape this import:
//   • NOT static (FEEDBACK-81): a static cross-package import hard-fails the build
//     for primitives-only consumers who never use <canvas-ui>. Hence lazy import().
//   • BARE specifier, not a relative path (FEEDBACK-85): a relative
//     '../../../web-modules/…' breaks Vite dep pre-bundling — once web-components is
//     flattened into node_modules/.vite/deps/, the relative path no longer resolves
//     and import-analysis aborts (even with @vite-ignore). A bare specifier resolves
//     identically whether pre-bundled or not. @adia-ai/web-modules is declared as an
//     optionalDependency so it resolves; the .catch() below degrades gracefully if
//     it is genuinely absent at runtime.

/**
 * <canvas-ui> — A2UI rendering surface.
 *
 * Wrapper mode. Wraps an a2ui-root for rendering generated components.
 * Consumer-owns-LLM: fires interaction events, never calls fetch.
 *
 * Events:
 *   canvas-interaction — user interacts with rendered components.
 *     Detail: { type, targetTag, targetId, value }
 */
export class UICanvas extends UIElement {
  static properties = {
    theme: { type: String, default: '', reflect: true },
  };

  static template = () => null;

  #bound = false;
  #rootEl = null;
  #rootContainer = null;
  #history = [];     // P7: version stack (A2UI message arrays)
  #historyIndex = -1;
  static MAX_HISTORY = 10;

  // FB-81: lazy/guarded <a2ui-root> loader state.
  #rendererReady = null;                 // Promise<boolean> | null
  static #warnedMissingRenderer = false;

  #onKeydown = (e) => {
    if (e.target.closest('input, textarea, [contenteditable]')) return;
    if (!this.contains(document.activeElement) && document.activeElement !== document.body) return;
  };

  #onClick = (e) => {
    if (!this.#rootEl?.contains(e.target)) return;
    this.#emitInteraction('click', e.target);
  };

  #onChange = (e) => {
    if (!this.#rootEl?.contains(e.target)) return;
    this.#emitInteraction('change', e.target);
  };

  #onInput = (e) => {
    if (!this.#rootEl?.contains(e.target)) return;
    this.#emitInteraction('input', e.target);
  };

  #onRetry = (e) => {
    // Re-dispatch as canvas-interaction with type='retry' for uniform handling
    this.dispatchEvent(new CustomEvent('canvas-interaction', {
      bubbles: true,
      detail: {
        type: 'retry',
        targetTag: 'button-ui',
        targetId: 'retry',
        value: '',
        ...e.detail,
      },
    }));
  };

  connected() {
    document.addEventListener('keydown', this.#onKeydown);
    this.addEventListener('click', this.#onClick);
    this.addEventListener('change', this.#onChange);
    this.addEventListener('input', this.#onInput);
    // Forward a2ui-retry from wired error cards so consumers can retry generation
    this.addEventListener('a2ui-retry', this.#onRetry);
    // Kick off the lazy renderer load now that a canvas is in the DOM.
    this.#ensureRenderer();
  }

  disconnected() {
    document.removeEventListener('keydown', this.#onKeydown);
    this.removeEventListener('click', this.#onClick);
    this.removeEventListener('change', this.#onChange);
    this.removeEventListener('input', this.#onInput);
    this.removeEventListener('a2ui-retry', this.#onRetry);
    this.#rootEl = null;
    this.#rootContainer = null;
    this.#bound = false;
  }

  /**
   * Lazily load the A2UI renderer (<a2ui-root>) from @adia-ai/web-modules.
   * Resolves true once the element is defined, false if web-modules is not
   * installed (primitives-only consumers). Idempotent — the import runs once.
   * Guarded so a missing sibling package degrades to a one-time warning
   * instead of a hard build/runtime failure (FEEDBACK-81).
   */
  #ensureRenderer() {
    if (this.#rendererReady) return this.#rendererReady;
    if (customElements.get('a2ui-root')) {
      this.#rendererReady = Promise.resolve(true);
      return this.#rendererReady;
    }
    this.#rendererReady = import('@adia-ai/web-modules/runtime/a2ui-root')
      .then(() => customElements.whenDefined('a2ui-root'))
      .then(() => true)
      .catch((err) => {
        if (!UICanvas.#warnedMissingRenderer) {
          UICanvas.#warnedMissingRenderer = true;
          console.warn(
            '[canvas-ui] A2UI renderer <a2ui-root> could not be loaded — install ' +
            '@adia-ai/web-modules to render generated UI in <canvas-ui>. Apps that ' +
            'use only primitives (and never <canvas-ui>) can ignore this.',
            err,
          );
        }
        return false;
      });
    return this.#rendererReady;
  }

  /**
   * Run `fn(rootEl)` against the <a2ui-root> once it is available. Synchronous
   * fast-path when the renderer is already upgraded (preserves prior timing for
   * consumers that have @adia-ai/web-modules); otherwise defers until the lazy
   * import resolves, preserving call order. No-op when web-modules is absent.
   */
  #whenRoot(fn) {
    if (this.#rootEl && typeof this.#rootEl.process === 'function') {
      fn(this.#rootEl);
      return;
    }
    this.#ensureRenderer().then((ok) => {
      if (ok && this.#rootEl && typeof this.#rootEl.process === 'function') fn(this.#rootEl);
    });
  }

  render() {
    if (this.#bound) return;
    this.#bound = true;

    this.#rootContainer = document.createElement('div');
    this.#rootContainer.setAttribute('data-canvas-surface', '');

    this.#rootEl = document.createElement('a2ui-root');

    if (this.theme) {
      const themeEl = document.createElement('theme-ui');
      themeEl.setAttribute('preset', this.theme);
      themeEl.appendChild(this.#rootEl);
      this.#rootContainer.appendChild(themeEl);
    } else {
      this.#rootContainer.appendChild(this.#rootEl);
    }

    this.appendChild(this.#rootContainer);
  }

  // ── Public API ──

  /** Process a single A2UI message. */
  process(message) {
    this.#whenRoot((root) => root.process(message));
  }

  /** Process an array of A2UI messages. */
  processAll(messages) {
    this.#whenRoot((root) => {
      for (const msg of messages) root.process(msg);
    });
  }

  /** Reset the canvas. */
  reset() {
    this.#whenRoot((root) => root.reset());
  }

  /** Return formatted innerHTML of the a2ui-root. */
  getHTML() {
    return this.#rootEl?.innerHTML ?? '';
  }

  /** Snapshot current state to history stack. */
  pushVersion(messages) {
    // Truncate forward history if we're not at the end
    if (this.#historyIndex < this.#history.length - 1) {
      this.#history = this.#history.slice(0, this.#historyIndex + 1);
    }
    this.#history.push(messages ? [...messages] : []);
    if (this.#history.length > UICanvas.MAX_HISTORY) this.#history.shift();
    this.#historyIndex = this.#history.length - 1;
  }

  /** Go back one version. Returns false if already at oldest. */
  back() {
    if (this.#historyIndex <= 0) return false;
    this.#historyIndex--;
    this.#restoreVersion();
    return true;
  }

  /** Go forward one version. Returns false if already at newest. */
  forward() {
    if (this.#historyIndex >= this.#history.length - 1) return false;
    this.#historyIndex++;
    this.#restoreVersion();
    return true;
  }

  get historyLength() { return this.#history.length; }
  get historyIndex() { return this.#historyIndex; }

  #restoreVersion() {
    const messages = this.#history[this.#historyIndex];
    this.#whenRoot((root) => {
      root.reset();
      if (messages) {
        for (const msg of messages) root.process(msg);
      }
    });
  }

  // ── Private ──

  #emitInteraction(type, target) {
    this.dispatchEvent(new CustomEvent('canvas-interaction', {
      bubbles: true,
      detail: {
        type,
        targetTag: target.localName,
        targetId: target.id || '',
        value: target.value ?? target.getAttribute('text') ?? '',
      },
    }));
  }
}

customElements.define('canvas-ui', UICanvas);
