/**
 * D3 Selection Polyfill - A lightweight DOM selection and manipulation utility.
 *
 * This is a drop-in replacement for d3-selection, implementing only the methods
 * used in the Vega chart rendering system.
 *
 * Supported methods: select, selectAll, append, attr, property, text, datum,
 * call, node, nodes, each, remove, clone, transition, duration.
 *
 * @see VD-5022 - [Zero Dependencies] remove d3 dependency
 */
/**
 * A lightweight selection class mimicking d3-selection's chainable API.
 * Wraps an array of DOM elements and provides attribute/property/child manipulation.
 */
export class D3SelectionPolyfill {
  /**
   * @param {Element[]} elements - Array of DOM elements to wrap.
   * @param {*} [datum] - Optional bound datum for the selection.
   */
  constructor(elements, datum) {
    this._groups = elements;
    this._datum = datum;
  }
  /**
   * Selects the first descendant element matching the selector.
   *
   * @template DescElement
   * @template Datum
   * @template GElement
   * @param {string} selector - CSS selector string.
   * @returns {D3SelectionPolyfill<DescElement, Datum, GElement, Datum>} A new selection wrapping the matched element.
   */
  select(selector) {
    const el = this._groups[0];
    if (!el) {
      return new D3SelectionPolyfill([]);
    }
    const found = el.querySelector(selector);
    return new D3SelectionPolyfill(found ? [found] : []);
  }
  /**
   * Selects all descendant elements matching the selector.
   *
   * @template DescElement
   * @template NewDatum
   * @template GElement
   * @template Datum
   * @param {string} selector - CSS selector string.
   * @returns {D3SelectionPolyfill<DescElement, NewDatum, GElement, Datum>} A new selection wrapping all matched elements.
   */
  selectAll(selector) {
    const el = this._groups[0];
    if (!el) {
      return new D3SelectionPolyfill([]);
    }
    const found = Array.from(el.querySelectorAll(selector));
    return new D3SelectionPolyfill(found);
  }
  /**
   * Appends a new child element to each element in the selection.
   * Accepts either a tag name string or a factory function that returns a DOM element.
   *
   * @template ChildElement
   * @template Datum
   * @template GElement
   * @param {string | Function} tagOrFactory - Tag name (e.g. 'g', 'path') or a factory function.
   * @returns {D3SelectionPolyfill<ChildElement, Datum, GElement, Datum>} A new selection wrapping the appended elements.
   */
  append(tagOrFactory) {
    const results = [];
    for (const parent of this._groups) {
      let child;
      if (typeof tagOrFactory === 'function') {
        child = tagOrFactory();
      }
      else {
        child = parent.ownerDocument.createElementNS('http://www.w3.org/2000/svg', tagOrFactory);
      }
      parent.appendChild(child);
      results.push(child);
    }
    return new D3SelectionPolyfill(results, this._datum);
  }
  /**
   * Sets or gets an attribute on each element.
   * If value is a function, it is called with the bound datum.
   *
   * @param {...*} args - Attribute name, and optionally a value or function returning a value.
   * @returns {any} This selection for chaining, or the attribute value if getting.
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  attr(...args) {
    const name = args[0];
    if (args.length === 1) {
      const el = this._groups[0];
      return el ? el.getAttribute(name) : null;
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const value = args[1];
    for (let i = 0; i < this._groups.length; i++) {
      const el = this._groups[i];
      const resolvedValue = typeof value === 'function' ? value.call(el, this._datum, i, this._groups) : value;
      if (resolvedValue === null || resolvedValue === undefined) {
        el.removeAttribute(name);
      }
      else {
        el.setAttribute(name, String(resolvedValue));
      }
    }
    return this;
  }
  /**
   * Sets a JavaScript property on each element.
   *
   * @param {string} name - Property name.
   * @param {unknown} value - Property value.
   * @returns {this} This selection for chaining.
   */
  property(name, value) {
    for (const el of this._groups) {
      el[name] = value;
    }
    return this;
  }
  /**
   * Sets the text content of each element.
   *
   * @param {string} value - Text content string.
   * @returns {this} This selection for chaining.
   */
  text(value) {
    for (const el of this._groups) {
      el.textContent = value;
    }
    return this;
  }
  /**
   * Binds a datum to the selection.
   *
   * @template NewDatum
   * @template GElement
   * @template PElement
   * @template PDatum
   * @param {*} data - The datum to bind.
   * @returns {D3SelectionPolyfill<GElement, NewDatum, PElement, PDatum>} A new selection with the bound datum.
   */
  datum(data) {
    return new D3SelectionPolyfill(this._groups, data);
  }
  /**
   * Calls a function with this selection as argument. Used for grouping operations.
   *
   * @param {Function} fn - Function to call.
   * @param {...*} args - Additional arguments.
   * @returns {this} This selection for chaining.
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  call(fn, ...args) {
    fn(this, ...args);
    return this;
  }
  /**
   * Returns the first DOM node in the selection, or null.
   *
   * @template GElement
   * @returns {GElement | null} The first element or null.
   */
  node() {
    return this._groups[0] || null;
  }
  /**
   * Returns all DOM nodes in the selection as an array.
   *
   * @template GElement
   * @returns {GElement[]} Array of elements.
   */
  nodes() {
    return [...this._groups];
  }
  /**
   * Iterates over each element, calling the provided function.
   * The function is called with `this` set to the current DOM element.
   *
   * @param {Function} fn - Callback function.
   * @returns {this} This selection for chaining.
   */
  each(fn) {
    for (let i = 0; i < this._groups.length; i++) {
      fn.call(this._groups[i], this._datum, i, this._groups);
    }
    return this;
  }
  /**
   * Removes all elements in the selection from the DOM.
   *
   * @returns {this} This selection for chaining.
   */
  remove() {
    var _a;
    for (const el of this._groups) {
      (_a = el.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(el);
    }
    return this;
  }
  /**
   * Clones each element in the selection (shallow clone) and inserts after the original.
   *
   * @template GElement
   * @template Datum
   * @template PElement
   * @template PDatum
   * @returns {D3SelectionPolyfill<GElement, Datum, PElement, PDatum>} A new selection wrapping the cloned elements.
   */
  clone() {
    var _a;
    const clones = [];
    for (const el of this._groups) {
      const cloned = el.cloneNode(false);
      (_a = el.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(cloned, el.nextSibling);
      clones.push(cloned);
    }
    return new D3SelectionPolyfill(clones, this._datum);
  }
  /**
   * Creates a transition-like object. This is a simplified polyfill that
   * applies attribute changes immediately (no actual animation), which is
   * sufficient for Vega's usage of hover path swapping.
   *
   * @param {*} [_name] - Transition name (ignored in polyfill).
   * @returns {this} This selection for chaining (acts as a no-op).
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  transition(_name) {
    return this;
  }
  /**
   * Sets the transition duration. No-op in this polyfill since
   * transitions are applied immediately.
   *
   * @param {number} _ms - Duration in milliseconds (ignored).
   * @returns {this} This selection for chaining.
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  duration(_ms) {
    return this;
  }
}
/**
 * Selects a DOM element by wrapping it in a D3SelectionPolyfill.
 * Mimics d3.select().
 *
 * @template GElement
 * @param {Element | EventTarget} element - A DOM element or EventTarget to wrap.
 * @returns {D3SelectionPolyfill<GElement>} A new D3SelectionPolyfill wrapping the element.
 */
export function select(element) {
  return new D3SelectionPolyfill([element]);
}
/**
 * Creates a detached SVG element (e.g., 'svg:text', 'svg:circle', 'svg:path').
 * Mimics d3.create().
 *
 * @template GElement
 * @param {string} qualifiedName - A qualified name like 'svg:text' or 'svg:circle'.
 * @returns {D3SelectionPolyfill<GElement>} A new D3SelectionPolyfill wrapping the created element.
 */
export function create(qualifiedName) {
  const parts = qualifiedName.split(':');
  const tagName = parts.length > 1 ? parts[1] : parts[0];
  // eslint-disable-next-line no-restricted-globals
  const el = document.createElementNS('http://www.w3.org/2000/svg', tagName);
  return new D3SelectionPolyfill([el]);
}
