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

/**
 * Root node of the AST representing the entire DSL program
 */
export class ProgramNode extends BaseNode {
  constructor(location = null) {
    super('Program', location);
    this.definitions = [];
    this.facts = [];
    this.evidence = [];
    this.measures = [];
  }

  /**
   * Add a definition to the program
   * @param {DefinitionNode} definition - Definition to add
   */
  addDefinition(definition) {
    this.definitions.push(definition);
    this.addChild(definition);
  }

  /**
   * Add a fact to the program
   * @param {FactNode} fact - Fact to add
   */
  addFact(fact) {
    this.facts.push(fact);
    this.addChild(fact);
  }

  /**
   * Add evidence to the program
   * @param {EvidenceNode} evidence - Evidence to add
   */
  addEvidence(evidence) {
    this.evidence.push(evidence);
    this.addChild(evidence);
  }

  /**
   * Add a measure to the program
   * @param {MeasureNode} measure - Measure to add
   */
  addMeasure(measure) {
    this.measures.push(measure);
    this.addChild(measure);
  }

  /**
   * Get all definitions of a specific type
   * @param {string} type - Definition type to filter by
   * @returns {DefinitionNode[]} Filtered definitions
   */
  getDefinitionsOfType(type) {
    return this.definitions.filter(def => def.definitionType === type);
  }

  /**
   * Find a definition by name
   * @param {string} name - Name to search for
   * @returns {DefinitionNode|null} Found definition or null
   */
  getDefinitionByName(name) {
    return this.definitions.find(def => def.name === name) || null;
  }

  /**
   * Find evidence by name
   * @param {string} name - Name to search for
   * @returns {EvidenceNode|null} Found evidence or null
   */
  getEvidenceByName(name) {
    return this.evidence.find(ev => ev.name === name) || null;
  }

  /**
   * Find a fact by name
   * @param {string} name - Name to search for
   * @returns {FactNode|null} Found fact or null
   */
  getFactByName(name) {
    return this.facts.find(fact => fact.name === name) || null;
  }

  /**
   * Find a measure by name
   * @param {string} name - Name to search for
   * @returns {MeasureNode|null} Found measure or null
   */
  getMeasureByName(name) {
    return this.measures.find(measure => measure.name === name) || null;
  }

  /**
   * Get all symbols (definitions, facts, evidence, measures) by name
   * @param {string} name - Name to search for
   * @returns {BaseNode[]} All matching symbols
   */
  getSymbolsByName(name) {
    return [
      ...this.definitions.filter(def => def.name === name),
      ...this.facts.filter(fact => fact.name === name),
      ...this.evidence.filter(ev => ev.name === name),
      ...this.measures.filter(measure => measure.name === name)
    ];
  }

  /**
   * Validate the program structure
   * @returns {Object} Validation result with errors and warnings
   */
  validate() {
    const errors = [];
    const warnings = [];

    // Check for duplicate names
    const allNames = new Map();
    [...this.definitions, ...this.facts, ...this.evidence, ...this.measures].forEach(symbol => {
      if (allNames.has(symbol.name)) {
        errors.push(`Duplicate symbol name: ${symbol.name}`);
      } else {
        allNames.set(symbol.name, symbol);
      }
    });

    // Validate each definition
    this.definitions.forEach(def => {
      const defErrors = def.validate ? def.validate() : [];
      errors.push(...defErrors);
    });

    // Validate each fact
    this.facts.forEach(fact => {
      const factErrors = fact.validate ? fact.validate() : [];
      errors.push(...factErrors);
    });

    // Validate each evidence
    this.evidence.forEach(ev => {
      const evErrors = ev.validate ? ev.validate() : [];
      errors.push(...evErrors);
    });

    // Validate each measure
    this.measures.forEach(measure => {
      const measureErrors = measure.validate ? measure.validate() : [];
      errors.push(...measureErrors);
    });

    return { errors, warnings, isValid: errors.length === 0 };
  }

  toString() {
    return `Program(${this.definitions.length} definitions, ${this.facts.length} facts, ${this.evidence.length} evidence, ${this.measures.length} measures)`;
  }
}
