/**
 * Base AST Node class for all DSL AST nodes
 * Provides common functionality for all AST nodes
 */
export class BaseNode {
  constructor(type, location = null) {
    this.type = type;
    this.location = location; // { start, end, line, column }
    this.parent = null;
    this.children = [];
  }

  /**
   * Add a child node to this node
   * @param {BaseNode} child - Child node to add
   */
  addChild(child) {
    if (child) {
      child.parent = this;
      this.children.push(child);
    }
    return this;
  }

  /**
   * Add multiple child nodes
   * @param {BaseNode[]} children - Array of child nodes
   */
  addChildren(children) {
    children.forEach(child => this.addChild(child));
    return this;
  }

  /**
   * Get all children of a specific type
   * @param {string} type - Node type to filter by
   * @returns {BaseNode[]} Filtered children
   */
  getChildrenOfType(type) {
    return this.children.filter(child => child.type === type);
  }

  /**
   * Find the first child of a specific type
   * @param {string} type - Node type to find
   * @returns {BaseNode|null} First matching child or null
   */
  getChildOfType(type) {
    return this.children.find(child => child.type === type) || null;
  }

  /**
   * Get all descendants of a specific type
   * @param {string} type - Node type to find
   * @returns {BaseNode[]} All matching descendants
   */
  getDescendantsOfType(type) {
    const results = [];
    this.children.forEach(child => {
      if (child.type === type) {
        results.push(child);
      }
      results.push(...child.getDescendantsOfType(type));
    });
    return results;
  }

  /**
   * Accept a visitor (visitor pattern)
   * @param {Object} visitor - Visitor object with visit methods
   * @returns {*} Result of visitor.visit{NodeType}(this)
   */
  accept(visitor) {
    const methodName = `visit${this.type}`;
    if (visitor[methodName]) {
      return visitor[methodName](this);
    }
    if (visitor.visit) {
      return visitor.visit(this);
    }
    return null;
  }

  /**
   * Get a string representation of this node
   * @returns {string} String representation
   */
  toString() {
    return `${this.type}(${this.children.length} children)`;
  }

  /**
   * Get a detailed string representation for debugging
   * @returns {string} Detailed string representation
   */
  toDebugString() {
    const childrenStr = this.children.map(child => 
      child.toDebugString ? child.toDebugString() : child.toString()
    ).join(', ');
    return `${this.type}(${childrenStr})`;
  }

  /**
   * Clone this node and all its children
   * @returns {BaseNode} Cloned node
   */
  clone() {
    const cloned = new this.constructor();
    cloned.type = this.type;
    cloned.location = this.location ? { ...this.location } : null;
    cloned.children = this.children.map(child => child.clone());
    cloned.children.forEach(child => child.parent = cloned);
    return cloned;
  }

  /**
   * Get the root node of the AST
   * @returns {BaseNode} Root node
   */
  getRoot() {
    let current = this;
    while (current.parent) {
      current = current.parent;
    }
    return current;
  }

  /**
   * Get the depth of this node in the AST
   * @returns {number} Depth from root
   */
  getDepth() {
    let depth = 0;
    let current = this.parent;
    while (current) {
      depth++;
      current = current.parent;
    }
    return depth;
  }

  /**
   * Check if this node is a descendant of another node
   * @param {BaseNode} ancestor - Potential ancestor node
   * @returns {boolean} True if ancestor is an ancestor of this node
   */
  isDescendantOf(ancestor) {
    let current = this.parent;
    while (current) {
      if (current === ancestor) {
        return true;
      }
      current = current.parent;
    }
    return false;
  }
}
