/**
 * D3 Axis Polyfill - Lightweight SVG axis generators.
 *
 * This is a drop-in replacement for d3-axis, implementing only the methods
 * used in the Vega chart rendering system: axisBottom and axisLeft.
 *
 * @see VD-5022 - [Zero Dependencies] remove d3 dependency
 */
/**
 * Internal axis generator class.
 */
class AxisGenerator {
  /**
   * @param {AxisScale} scale - The scale to use for the axis.
   * @param {string} orientation - 'bottom' for x-axis, 'left' for y-axis.
   */
  constructor(scale, orientation) {
    this._tickCount = 10;
    this._tickFormatter = null;
    this._scale = scale;
    this._orientation = orientation;
  }
  /**
   * Sets the number of ticks.
   *
   * @param {number} count - Desired number of ticks.
   */
  setTickCount(count) {
    this._tickCount = count;
  }
  /**
   * Sets a custom tick format function.
   *
   * @param {Function} formatter - Function to format tick labels.
   */
  setTickFormatter(formatter) {
    this._tickFormatter = formatter;
  }
  /**
   * Renders the axis into the given SVG <g> selection.
   * Creates a domain path, tick lines, and tick text labels.
   *
   * @param {D3SelectionPolyfill} selection - A D3SelectionPolyfill wrapping an SVG <g> element.
   */
  render(selection) {
    const g = selection.node();
    if (!g)
      return;
    const doc = g.ownerDocument;
    const tickValues = this.getTickValues();
    const formatter = this.getFormatter();
    const offset = 0.5;
    // Compute band centering offset (d3's center() function)
    const bandOffset = typeof this._scale.bandwidth === 'function'
      ? Math.max(0, this._scale.bandwidth() - offset * 2) / 2
      : 0;
    const range = this._scale.range();
    const range0 = +range[0] + offset;
    const range1 = +range[range.length - 1] + offset;
    // Create domain path (with outer tick marks, matching d3's tickSizeOuter = 6)
    const domain = doc.createElementNS('http://www.w3.org/2000/svg', 'path');
    domain.setAttribute('class', 'domain');
    domain.setAttribute('stroke', 'currentColor');
    if (this._orientation === 'bottom') {
      domain.setAttribute('d', `M${range0},6V${offset}H${range1}V6`);
    }
    else {
      domain.setAttribute('d', `M-6,${range0}H${offset}V${range1}H-6`);
    }
    g.appendChild(domain);
    // Create ticks
    for (const tickValue of tickValues) {
      const position = this._scale(tickValue);
      if (position === undefined)
        continue;
      const finalPosition = position + bandOffset + offset;
      const tickG = doc.createElementNS('http://www.w3.org/2000/svg', 'g');
      tickG.setAttribute('class', 'tick');
      tickG.setAttribute('opacity', '1');
      const tickLine = doc.createElementNS('http://www.w3.org/2000/svg', 'line');
      tickLine.setAttribute('stroke', 'currentColor');
      const tickText = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
      tickText.setAttribute('fill', 'currentColor');
      tickText.textContent = formatter(tickValue);
      // eslint-disable-next-line spellcheck/spell-checker
      const verticalOffset = this._orientation === 'bottom' ? '0.71em' : '0.32em';
      if (this._orientation === 'bottom') {
        tickG.setAttribute('transform', `translate(${finalPosition},0)`);
        tickLine.setAttribute('y2', '6');
        tickText.setAttribute('y', '9');
        // eslint-disable-next-line spellcheck/spell-checker
        tickText.setAttribute('dy', verticalOffset);
      }
      else {
        tickG.setAttribute('transform', `translate(0,${finalPosition})`);
        tickLine.setAttribute('x2', '-6');
        tickText.setAttribute('x', '-9');
        // eslint-disable-next-line spellcheck/spell-checker
        tickText.setAttribute('dy', verticalOffset);
      }
      tickG.appendChild(tickLine);
      tickG.appendChild(tickText);
      g.appendChild(tickG);
    }
    // Set axis group attributes (matching d3)
    g.setAttribute('fill', 'none');
    g.setAttribute('font-size', '10');
    g.setAttribute('font-family', 'sans-serif');
    g.setAttribute('text-anchor', this._orientation === 'bottom' ? 'middle' : 'end');
  }
  /**
   * Gets tick values from the scale.
   *
   * @template Domain
   * @returns {Domain[]} Array of tick domain values.
   */
  getTickValues() {
    if (this._scale.ticks) {
      return this._scale.ticks(this._tickCount);
    }
    if (this._scale.domain) {
      return this._scale.domain();
    }
    return [];
  }
  /**
   * Gets the tick formatter function.
   *
   * @template Domain
   * @returns {(d: Domain) => string} A function that converts tick values to strings.
   */
  getFormatter() {
    if (this._tickFormatter) {
      return this._tickFormatter;
    }
    if (this._scale.tickFormat) {
      return this._scale.tickFormat(this._tickCount);
    }
    return (d) => String(d);
  }
}
/**
 * Creates a bottom-oriented axis generator. Mimics d3.axisBottom().
 *
 * @template Domain
 * @param {AxisScale} scale - The scale to use.
 * @returns {Axis<Domain>} An Axis callable that renders into a <g> element.
 */
export function axisBottom(scale) {
  const generator = new AxisGenerator(scale, 'bottom');
  /* eslint-disable jsdoc/require-jsdoc */
  const axisFn = Object.assign(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (selection) => {
    generator.render(selection);
  }, {
    ticks: (count) => {
      generator.setTickCount(count);
      return axisFn;
    },
    tickFormat: (formatter) => {
      generator.setTickFormatter(formatter);
      return axisFn;
    },
  });
  /* eslint-enable jsdoc/require-jsdoc */
  return axisFn;
}
/**
 * Creates a left-oriented axis generator. Mimics d3.axisLeft().
 *
 * @template Domain
 * @param {AxisScale} scale - The scale to use.
 * @returns {Axis<Domain>} An Axis callable that renders into a <g> element.
 */
export function axisLeft(scale) {
  const generator = new AxisGenerator(scale, 'left');
  /* eslint-disable jsdoc/require-jsdoc */
  const axisFn = Object.assign(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (selection) => {
    generator.render(selection);
  }, {
    ticks: (count) => {
      generator.setTickCount(count);
      return axisFn;
    },
    tickFormat: (formatter) => {
      generator.setTickFormatter(formatter);
      return axisFn;
    },
  });
  /* eslint-enable jsdoc/require-jsdoc */
  return axisFn;
}
