/**
 * Unified Evidence Fusion System
 * 
 * This module provides a comprehensive evidence fusion system that separates
 * aggregation logic (OWA) from reconciliation logic (bilattice/Dempster-Shafer),
 * supporting both qualitative and quantitative modes with the same lexicon.
 * 
 * Architecture:
 * 1. Aggregation: Uses OWA operators for combining evidence values
 * 2. Reconciliation: Uses theoretical frameworks for handling epistemic uncertainty
 * 3. Mode Support: Both qualitative and quantitative with consistent lexicon
 * 
 * This separation allows for sophisticated evidence fusion that can handle
 * both simple aggregation and complex epistemic reasoning scenarios.
 */

import { EvidenceAggregation } from './EvidenceAggregation.js';
import { EvidenceReconciliation } from './EvidenceReconciliation.js';
import { QualitativeScale } from './QualitativeScale.js';

export class UnifiedEvidenceFusion {
  /**
   * Fuse evidence using both aggregation and reconciliation
   * @param {Array} collectedValues - Array of collected evidence values
   * @param {Object} options - Fusion options
   * @returns {Object} Fusion result
   */
  static fuse(collectedValues, options = {}) {
    const {
      mode = 'quantitative',           // 'qualitative' or 'quantitative'
      aggregationMethod = 'max',       // OWA aggregation method
      reconciliationMethod = 'none',   // 'none', 'bilattice', 'dempster_shafer', 'subjective_logic'
      epistemicMode = 'hybrid',        // 'information', 'truth', 'hybrid'
      capacityType = 'simple_support', // 'simple_support', 'possibility', 'necessity'
      scale = null,                    // QualitativeScale for qualitative mode
      useReconciliation = false,       // Whether to use reconciliation
      reliabilityWeighting = false,    // Whether to weight by reliability
      customWeights = null,            // Custom OWA weights
      weights = null                   // Legacy weights parameter
    } = options;

    if (!collectedValues || collectedValues.length === 0) {
      return {
        value: 0,
        possibility: 0,
        hasValue: false,
        fusionMethod: 'none',
        aggregationMethod: 'none',
        reconciliationMethod: 'none',
        mode
      };
    }

    // Extract values and metadata for aggregation
    const values = collectedValues.map(cv => cv.value || cv.possibility);
    const metas = collectedValues.map(cv => ({
      ...cv.metadata,
      reliability: cv.metadata?.reliability || 1.0,
      timestamp: cv.metadata?.timestamp || Date.now()
    }));

    let result;

    if (useReconciliation && reconciliationMethod !== 'none') {
      // Use reconciliation-based fusion
      result = this._fuseWithReconciliation(collectedValues, {
        mode,
        aggregationMethod,
        reconciliationMethod,
        epistemicMode,
        capacityType,
        scale: scale || (mode === 'qualitative' ? QualitativeScale.fivePoint() : null),
        reliabilityWeighting,
        customWeights: customWeights || weights
      });
    } else {
      // Use pure aggregation-based fusion
      result = this._fuseWithAggregation(values, metas, {
        mode,
        aggregationMethod,
        scale: scale || (mode === 'qualitative' ? QualitativeScale.fivePoint() : null),
        reliabilityWeighting,
        customWeights: customWeights || weights
      });
    }

    return {
      ...result,
      fusionMethod: useReconciliation && reconciliationMethod !== 'none' ? 'reconciliation' : 'aggregation',
      mode
    };
  }

  /**
   * Fuse evidence using reconciliation-based approach
   * @private
   */
  static _fuseWithReconciliation(collectedValues, options) {
    const {
      mode,
      aggregationMethod,
      reconciliationMethod,
      epistemicMode,
      capacityType,
      scale,
      reliabilityWeighting,
      customWeights
    } = options;

    // First, perform reconciliation to select the best evidence
    const reconciliationResult = EvidenceReconciliation.reconcile(collectedValues, {
      mode,
      reconciliationMethod,
      epistemicMode,
      capacityType,
      scale
    });

    if (!reconciliationResult.hasValue) {
      return {
        value: mode === 'qualitative' ? scale.bottom : 0,
        possibility: mode === 'qualitative' ? scale.bottom : 0,
        hasValue: false,
        aggregationMethod: 'none',
        reconciliationMethod,
        epistemicAnalysis: reconciliationResult.epistemicAnalysis
      };
    }

    // If reconciliation selected a single value, use it directly
    if (reconciliationResult.reconciliationMethod !== 'none') {
      return {
        value: reconciliationResult.value,
        possibility: reconciliationResult.possibility,
        hasValue: true,
        aggregationMethod: 'reconciliation_selected',
        reconciliationMethod,
        epistemicAnalysis: reconciliationResult.epistemicAnalysis
      };
    }

    // Otherwise, fall back to aggregation
    const values = collectedValues.map(cv => cv.value || cv.possibility);
    const metas = collectedValues.map(cv => ({
      ...cv.metadata,
      reliability: cv.metadata?.reliability || 1.0
    }));

    const aggregationResult = EvidenceAggregation.aggregate(values, metas, {
      mode,
      aggregator: aggregationMethod,
      reliabilityWeighting,
      scale,
      customWeights
    });

    return {
      value: aggregationResult.value,
      possibility: aggregationResult.possibility,
      hasValue: true,
      aggregationMethod,
      reconciliationMethod,
      epistemicAnalysis: reconciliationResult.epistemicAnalysis
    };
  }

  /**
   * Fuse evidence using pure aggregation approach
   * @private
   */
  static _fuseWithAggregation(values, metas, options) {
    const {
      mode,
      aggregationMethod,
      scale,
      reliabilityWeighting,
      customWeights
    } = options;

    const aggregationResult = EvidenceAggregation.aggregate(values, metas, {
      mode,
      aggregator: aggregationMethod,
      reliabilityWeighting,
      scale,
      customWeights
    });

    return {
      value: aggregationResult.value,
      possibility: aggregationResult.possibility,
      hasValue: true,
      aggregationMethod,
      reconciliationMethod: 'none',
      epistemicAnalysis: null
    };
  }

  /**
   * Compare fusion results between different methods
   * @param {Array} collectedValues - Array of collected evidence values
   * @param {Object} options - Comparison options
   * @returns {Object} Comparison result
   */
  static compareMethods(collectedValues, options = {}) {
    const {
      mode = 'quantitative',
      aggregationMethods = ['max', 'min', 'average', 'majority'],
      reconciliationMethods = ['none', 'bilattice', 'dempster_shafer'],
      epistemicModes = ['hybrid', 'information', 'truth'],
      scale = null
    } = options;

    const results = {};

    // Test aggregation methods
    for (const aggMethod of aggregationMethods) {
      const result = this.fuse(collectedValues, {
        ...options,
        mode,
        aggregationMethod: aggMethod,
        useReconciliation: false,
        scale
      });
      results[`aggregation_${aggMethod}`] = result;
    }

    // Test reconciliation methods
    for (const recMethod of reconciliationMethods) {
      if (recMethod === 'none') continue;
      
      for (const epMode of epistemicModes) {
        const result = this.fuse(collectedValues, {
          ...options,
          mode,
          reconciliationMethod: recMethod,
          epistemicMode: epMode,
          useReconciliation: true,
          scale
        });
        results[`reconciliation_${recMethod}_${epMode}`] = result;
      }
    }

    return {
      mode,
      results,
      summary: this._generateComparisonSummary(results)
    };
  }

  /**
   * Generate comparison summary
   * @private
   */
  static _generateComparisonSummary(results) {
    const values = Object.values(results).map(r => r.value);
    const maxValue = Math.max(...values);
    const minValue = Math.min(...values);
    const avgValue = values.reduce((sum, val) => sum + val, 0) / values.length;

    const bestMethods = Object.entries(results)
      .filter(([_, result]) => result.value === maxValue)
      .map(([method, _]) => method);

    const worstMethods = Object.entries(results)
      .filter(([_, result]) => result.value === minValue)
      .map(([method, _]) => method);

    return {
      valueRange: { min: minValue, max: maxValue, average: avgValue },
      bestMethods,
      worstMethods,
      methodCount: Object.keys(results).length,
      valueVariance: this._calculateVariance(values)
    };
  }

  /**
   * Calculate variance of values
   * @private
   */
  static _calculateVariance(values) {
    const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
    const squaredDiffs = values.map(val => Math.pow(val - mean, 2));
    return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length;
  }

  /**
   * Get available fusion methods
   * @returns {Object} Available methods by category
   */
  static getAvailableMethods() {
    return {
      aggregation: EvidenceAggregation.getAvailableMethods(),
      reconciliation: ['none', 'bilattice', 'dempster_shafer', 'subjective_logic'],
      epistemicModes: ['information', 'truth', 'hybrid'],
      capacityTypes: ['simple_support', 'possibility', 'necessity']
    };
  }

  /**
   * Validate fusion options
   * @param {Object} options - Options to validate
   * @returns {Object} Validation result
   */
  static validateOptions(options) {
    const errors = [];
    const warnings = [];

    const availableMethods = this.getAvailableMethods();

    // Validate aggregation method
    if (options.aggregationMethod && !availableMethods.aggregation.includes(options.aggregationMethod)) {
      errors.push(`Invalid aggregation method: ${options.aggregationMethod}`);
    }

    // Validate reconciliation method
    if (options.reconciliationMethod && !availableMethods.reconciliation.includes(options.reconciliationMethod)) {
      errors.push(`Invalid reconciliation method: ${options.reconciliationMethod}`);
    }

    // Validate epistemic mode
    if (options.epistemicMode && !availableMethods.epistemicModes.includes(options.epistemicMode)) {
      errors.push(`Invalid epistemic mode: ${options.epistemicMode}`);
    }

    // Validate capacity type
    if (options.capacityType && !availableMethods.capacityTypes.includes(options.capacityType)) {
      errors.push(`Invalid capacity type: ${options.capacityType}`);
    }

    // Validate mode
    if (options.mode && !['qualitative', 'quantitative'].includes(options.mode)) {
      errors.push(`Invalid mode: ${options.mode}`);
    }

    // Warnings
    if (options.useReconciliation && options.reconciliationMethod === 'none') {
      warnings.push('useReconciliation is true but reconciliationMethod is none');
    }

    if (options.mode === 'qualitative' && !options.scale) {
      warnings.push('Qualitative mode recommended with explicit scale');
    }

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

  /**
   * Get method descriptions
   * @returns {Object} Method descriptions
   */
  static getMethodDescriptions() {
    return {
      aggregation: EvidenceAggregation.getAvailableMethods().reduce((desc, method) => {
        desc[method] = EvidenceAggregation.getMethodDescription(method);
        return desc;
      }, {}),
      reconciliation: {
        'none': 'No reconciliation (pure aggregation)',
        'bilattice': 'Bilattice orderings for epistemic reasoning',
        'dempster_shafer': 'Dempster-Shafer theory for belief functions',
        'subjective_logic': 'Subjective Logic for opinion-based reasoning'
      },
      epistemicModes: {
        'information': 'Focus on information content (belief + disbelief)',
        'truth': 'Focus on truth content (capacity ordering)',
        'hybrid': 'Combine information and truth orderings'
      },
      capacityTypes: {
        'simple_support': 'Simple support functions (basic evidence)',
        'possibility': 'Possibility measures (singleton focal sets)',
        'necessity': 'Necessity measures (nested focal sets)'
      }
    };
  }
}
