import { BaseNode } from './BaseNode.js';

/**
 * AST node for aggregation expressions
 * Represents: aggregate { ... } USING majority
 */
export class AggregationNode extends BaseNode {
  constructor(location = null) {
    super('Aggregation', location);
    this.expressions = []; // Array of expressions to aggregate
    this.method = null; // Aggregation method ('majority', 'max', 'min', 'sum', 'avg')
    this.weights = null; // Optional weights array
  }

  /**
   * Add an expression to this aggregation
   * @param {ExpressionNode} expression - Expression to add
   */
  addExpression(expression) {
    this.expressions.push(expression);
    this.addChild(expression);
  }

  /**
   * Set the aggregation method
   * @param {string} method - Aggregation method
   */
  setMethod(method) {
    this.method = method;
  }

  /**
   * Set weights for this aggregation
   * @param {number[]} weights - Weights array
   */
  setWeights(weights) {
    this.weights = weights;
  }

  /**
   * Get all expressions
   * @returns {ExpressionNode[]} Expressions to aggregate
   */
  getExpressions() {
    return this.expressions;
  }

  /**
   * Get the aggregation method
   * @returns {string|null} Aggregation method or null
   */
  getMethod() {
    return this.method;
  }

  /**
   * Get the weights for this aggregation
   * @returns {number[]|null} Weights or null
   */
  getWeights() {
    return this.weights;
  }

  /**
   * Check if this aggregation has weights
   * @returns {boolean} True if has weights
   */
  hasWeights() {
    return this.weights !== null && this.weights.length > 0;
  }

  /**
   * Check if this is a majority aggregation
   * @returns {boolean} True if majority
   */
  isMajority() {
    return this.method === 'majority';
  }

  /**
   * Check if this is a max aggregation
   * @returns {boolean} True if max
   */
  isMax() {
    return this.method === 'max';
  }

  /**
   * Check if this is a min aggregation
   * @returns {boolean} True if min
   */
  isMin() {
    return this.method === 'min';
  }

  /**
   * Check if this is a sum aggregation
   * @returns {boolean} True if sum
   */
  isSum() {
    return this.method === 'sum';
  }

  /**
   * Check if this is an average aggregation
   * @returns {boolean} True if average
   */
  isAverage() {
    return this.method === 'avg';
  }

  /**
   * Get the number of expressions
   * @returns {number} Number of expressions
   */
  getExpressionCount() {
    return this.expressions.length;
  }

  /**
   * Validate the aggregation
   * @returns {string[]} Array of error messages
   */
  validate() {
    const errors = [];

    // Validate method
    const validMethods = ['majority', 'max', 'min', 'sum', 'avg', 'count'];
    if (!this.method || !validMethods.includes(this.method)) {
      errors.push(`Invalid aggregation method: ${this.method}`);
    }

    // Validate expressions
    if (this.expressions.length === 0) {
      errors.push('Aggregation must have at least one expression');
    }

    // Validate each expression
    this.expressions.forEach((expr, index) => {
      const exprErrors = expr.validate ? expr.validate() : [];
      errors.push(...exprErrors.map(err => `Expression ${index + 1}: ${err}`));
    });

    // Validate weights
    if (this.weights !== null) {
      if (!Array.isArray(this.weights)) {
        errors.push('Weights must be an array');
      } else if (this.weights.length !== this.expressions.length) {
        errors.push('Weights array length must match expression count');
      } else if (this.weights.some(w => typeof w !== 'number' || w < 0)) {
        errors.push('All weights must be non-negative numbers');
      }
    }

    return errors;
  }

  toString() {
    const weightsStr = this.hasWeights() ? ` weights[${this.weights.length}]` : '';
    return `Aggregation(${this.method}, ${this.expressions.length} expressions${weightsStr})`;
  }
}
