/**
 * Evidence Reconciliation System
 * 
 * This module provides a unified system for reconciling conflicting evidence
 * using different theoretical frameworks:
 * 
 * 1. Qualitative Mode: Bilattice orderings for qualitative scales
 * 2. Quantitative Mode: Dempster-Shafer/Subjective Logic for numeric evidence
 * 
 * The system separates aggregation logic (OWA) from reconciliation logic,
 * allowing for sophisticated evidence fusion that handles epistemic uncertainty
 * and conflicting information appropriately.
 */

import { BilatticeOrderings } from './BilatticeOrderings.js';
import { NumericBilatticeOrderings } from './NumericBilatticeOrderings.js';
import { QualitativeCapacity } from './QualitativeCapacity.js';
import { QualitativeScale } from './QualitativeScale.js';

export class EvidenceReconciliation {
  /**
   * Reconcile evidence using appropriate theoretical framework
   * @param {Array} collectedValues - Array of collected evidence values
   * @param {Object} options - Reconciliation options
   * @returns {Object} Reconciliation result
   */
  static reconcile(collectedValues, options = {}) {
    const {
      mode = 'qualitative',           // 'qualitative' or 'quantitative'
      reconciliationMethod = 'bilattice', // 'bilattice', 'dempster_shafer', 'subjective_logic'
      epistemicMode = 'hybrid',       // 'information', 'truth', 'hybrid'
      capacityType = 'simple_support', // 'simple_support', 'possibility', 'necessity'
      scale = null,                   // QualitativeScale for qualitative mode
      aggregationMethod = 'max'       // OWA aggregation method
    } = options;

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

    if (mode === 'qualitative') {
      return this._reconcileQualitative(collectedValues, {
        reconciliationMethod,
        epistemicMode,
        capacityType,
        scale: scale || QualitativeScale.fivePoint(),
        aggregationMethod
      });
    } else {
      return this._reconcileQuantitative(collectedValues, {
        reconciliationMethod,
        epistemicMode,
        capacityType,
        aggregationMethod
      });
    }
  }

  /**
   * Reconcile qualitative evidence using bilattice orderings
   * @private
   */
  static _reconcileQualitative(collectedValues, options) {
    const {
      reconciliationMethod,
      epistemicMode,
      capacityType,
      scale,
      aggregationMethod
    } = options;

    // Create qualitative capacity from collected values
    const capacity = this._createQualitativeCapacity(collectedValues, scale, capacityType);
    
    if (!capacity) {
      return {
        value: scale.bottom,
        possibility: scale.bottom,
        hasValue: false,
        reconciliationMethod: 'none',
        epistemicAnalysis: null
      };
    }

    // Create propositions for bilattice analysis
    const propositions = collectedValues.map((cv, index) => [`evidence_${index}`]);

    let selectedValue;
    let epistemicAnalysis;

    switch (reconciliationMethod) {
      case 'bilattice':
        epistemicAnalysis = this._performBilatticeReconciliation(
          propositions, capacity, epistemicMode, scale
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      case 'dempster_shafer':
        epistemicAnalysis = this._performDempsterShaferReconciliation(
          propositions, capacity, epistemicMode, scale
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      case 'subjective_logic':
        epistemicAnalysis = this._performSubjectiveLogicReconciliation(
          propositions, capacity, epistemicMode, scale
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      default:
        // Fallback to simple aggregation
        selectedValue = this._simpleQualitativeAggregation(collectedValues, aggregationMethod, scale);
        epistemicAnalysis = {
          method: 'simple_aggregation',
          selectedValue,
          reasoning: 'Fallback to simple aggregation'
        };
    }

    return {
      value: selectedValue,
      possibility: selectedValue, // In qualitative mode, value and possibility are the same
      hasValue: true,
      reconciliationMethod,
      epistemicAnalysis
    };
  }

  /**
   * Reconcile quantitative evidence using Dempster-Shafer/Subjective Logic
   * @private
   */
  static _reconcileQuantitative(collectedValues, options) {
    const {
      reconciliationMethod,
      epistemicMode,
      capacityType,
      aggregationMethod
    } = options;

    // Create numeric capacity from collected values
    const capacity = this._createNumericCapacity(collectedValues, capacityType);
    
    if (!capacity) {
      return {
        value: 0,
        possibility: 0,
        hasValue: false,
        reconciliationMethod: 'none',
        epistemicAnalysis: null
      };
    }

    // Create propositions for analysis
    const propositions = collectedValues.map((cv, index) => [`evidence_${index}`]);

    let selectedValue;
    let epistemicAnalysis;

    switch (reconciliationMethod) {
      case 'dempster_shafer':
        epistemicAnalysis = this._performNumericDempsterShaferReconciliation(
          propositions, capacity, epistemicMode
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      case 'subjective_logic':
        epistemicAnalysis = this._performNumericSubjectiveLogicReconciliation(
          propositions, capacity, epistemicMode
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      case 'bilattice':
        epistemicAnalysis = this._performNumericBilatticeReconciliation(
          propositions, capacity, epistemicMode
        );
        selectedValue = epistemicAnalysis.selectedValue;
        break;

      default:
        // Fallback to simple aggregation
        selectedValue = this._simpleNumericAggregation(collectedValues, aggregationMethod);
        epistemicAnalysis = {
          method: 'simple_aggregation',
          selectedValue,
          reasoning: 'Fallback to simple aggregation'
        };
    }

    return {
      value: selectedValue,
      possibility: selectedValue, // In quantitative mode, we use the reconciled value
      hasValue: true,
      reconciliationMethod,
      epistemicAnalysis
    };
  }

  /**
   * Perform bilattice reconciliation for qualitative evidence
   * @private
   */
  static _performBilatticeReconciliation(propositions, capacity, epistemicMode, scale) {
    let selectedProposition;
    let ranking;

    switch (epistemicMode) {
      case 'information':
        selectedProposition = BilatticeOrderings.findMostInformative(propositions, capacity);
        ranking = BilatticeOrderings.rankByInformation(propositions, capacity);
        break;

      case 'truth':
        selectedProposition = BilatticeOrderings.findMostTrue(propositions, capacity);
        ranking = BilatticeOrderings.rankByTruth(propositions, capacity);
        break;

      case 'hybrid':
      default:
        // Use information ordering as primary, truth as tie-breaker
        const infoRanking = BilatticeOrderings.rankByInformation(propositions, capacity);
        const truthRanking = BilatticeOrderings.rankByTruth(propositions, capacity);
        
        // Find best proposition considering both orderings
        let bestScore = -1;
        let bestProposition = null;
        
        for (let i = 0; i < propositions.length; i++) {
          const infoRank = infoRanking.find(r => r.proposition === propositions[i])?.rank || propositions.length;
          const truthRank = truthRanking.find(r => r.proposition === propositions[i])?.rank || propositions.length;
          
          // Combined score (lower rank is better)
          const score = 1 / (infoRank + truthRank);
          
          if (score > bestScore) {
            bestScore = score;
            bestProposition = propositions[i];
          }
        }
        
        selectedProposition = {
          proposition: bestProposition,
          epistemic: BilatticeOrderings._getEpistemicPair(new Set(bestProposition), capacity),
          rank: 1
        };
        ranking = infoRanking;
        break;
    }

    // Extract value from selected proposition
    const selectedIndex = propositions.findIndex(p => p === selectedProposition.proposition);
    const selectedValue = selectedIndex >= 0 ? 
      this._extractValueFromProposition(selectedProposition.proposition, capacity, scale) : 
      scale.bottom;

    return {
      method: 'bilattice',
      epistemicMode,
      selectedValue,
      selectedProposition,
      ranking,
      reasoning: `Selected based on ${epistemicMode} ordering`
    };
  }

  /**
   * Perform Dempster-Shafer reconciliation for qualitative evidence
   * @private
   */
  static _performDempsterShaferReconciliation(propositions, capacity, epistemicMode, scale) {
    // Calculate Dempster-Shafer measures for each proposition
    const dsMeasures = propositions.map(prop => {
      const set = new Set(prop);
      const belief = BilatticeOrderings._getEpistemicPair(set, capacity).belief;
      const plausibility = 1 - BilatticeOrderings._getEpistemicPair(set, capacity).disbelief;
      const uncertainty = plausibility - belief;
      
      return {
        proposition: prop,
        belief,
        plausibility,
        uncertainty,
        expectation: belief + uncertainty / 2
      };
    });

    // Select based on epistemic mode
    let selectedMeasure;
    switch (epistemicMode) {
      case 'information':
        // Select based on uncertainty (lower uncertainty = more informative)
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.uncertainty < best.uncertainty ? current : best
        );
        break;

      case 'truth':
        // Select based on belief (higher belief = more true)
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.belief > best.belief ? current : best
        );
        break;

      case 'hybrid':
      default:
        // Select based on expectation value
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.expectation > best.expectation ? current : best
        );
        break;
    }

    const selectedValue = this._extractValueFromProposition(selectedMeasure.proposition, capacity, scale);

    return {
      method: 'dempster_shafer',
      epistemicMode,
      selectedValue,
      selectedMeasure,
      allMeasures: dsMeasures,
      reasoning: `Selected based on ${epistemicMode} Dempster-Shafer analysis`
    };
  }

  /**
   * Perform Subjective Logic reconciliation for qualitative evidence
   * @private
   */
  static _performSubjectiveLogicReconciliation(propositions, capacity, epistemicMode, scale) {
    // Calculate Subjective Logic opinions for each proposition
    const opinions = propositions.map(prop => {
      const set = new Set(prop);
      const epistemic = BilatticeOrderings._getEpistemicPair(set, capacity);
      const opinion = {
        b: epistemic.belief,        // Belief
        d: epistemic.disbelief,     // Disbelief
        u: Math.max(0, 1 - epistemic.belief - epistemic.disbelief) // Uncertainty
      };
      const expectation = opinion.b + opinion.u / 2;
      
      return {
        proposition: prop,
        opinion,
        expectation
      };
    });

    // Select based on epistemic mode
    let selectedOpinion;
    switch (epistemicMode) {
      case 'information':
        // Select based on uncertainty (lower uncertainty = more informative)
        selectedOpinion = opinions.reduce((best, current) => 
          current.opinion.u < best.opinion.u ? current : best
        );
        break;

      case 'truth':
        // Select based on belief (higher belief = more true)
        selectedOpinion = opinions.reduce((best, current) => 
          current.opinion.b > best.opinion.b ? current : best
        );
        break;

      case 'hybrid':
      default:
        // Select based on expectation value
        selectedOpinion = opinions.reduce((best, current) => 
          current.expectation > best.expectation ? current : best
        );
        break;
    }

    const selectedValue = this._extractValueFromProposition(selectedOpinion.proposition, capacity, scale);

    return {
      method: 'subjective_logic',
      epistemicMode,
      selectedValue,
      selectedOpinion,
      allOpinions: opinions,
      reasoning: `Selected based on ${epistemicMode} Subjective Logic analysis`
    };
  }

  /**
   * Perform numeric Dempster-Shafer reconciliation
   * @private
   */
  static _performNumericDempsterShaferReconciliation(propositions, capacity, epistemicMode) {
    // Similar to qualitative but with numeric values
    const dsMeasures = propositions.map(prop => {
      const set = new Set(prop);
      const belief = NumericBilatticeOrderings.dempsterShaferBelief(set, capacity);
      const plausibility = NumericBilatticeOrderings.dempsterShaferPlausibility(set, capacity);
      const uncertainty = NumericBilatticeOrderings.dempsterShaferUncertainty(set, capacity);
      
      return {
        proposition: prop,
        belief,
        plausibility,
        uncertainty,
        expectation: belief + uncertainty / 2
      };
    });

    // Select based on epistemic mode (same logic as qualitative)
    let selectedMeasure;
    switch (epistemicMode) {
      case 'information':
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.uncertainty < best.uncertainty ? current : best
        );
        break;

      case 'truth':
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.belief > best.belief ? current : best
        );
        break;

      case 'hybrid':
      default:
        selectedMeasure = dsMeasures.reduce((best, current) => 
          current.expectation > best.expectation ? current : best
        );
        break;
    }

    const selectedValue = this._extractNumericValueFromProposition(selectedMeasure.proposition, capacity);

    return {
      method: 'dempster_shafer',
      epistemicMode,
      selectedValue,
      selectedMeasure,
      allMeasures: dsMeasures,
      reasoning: `Selected based on ${epistemicMode} numeric Dempster-Shafer analysis`
    };
  }

  /**
   * Perform numeric Subjective Logic reconciliation
   * @private
   */
  static _performNumericSubjectiveLogicReconciliation(propositions, capacity, epistemicMode) {
    // Similar to qualitative but with numeric values
    const opinions = propositions.map(prop => {
      const set = new Set(prop);
      const epistemic = NumericBilatticeOrderings._getEpistemicPair(set, capacity);
      const opinion = NumericBilatticeOrderings.subjectiveLogicOpinion(epistemic);
      const expectation = NumericBilatticeOrderings.subjectiveLogicExpectation(opinion);
      
      return {
        proposition: prop,
        opinion,
        expectation
      };
    });

    // Select based on epistemic mode (same logic as qualitative)
    let selectedOpinion;
    switch (epistemicMode) {
      case 'information':
        selectedOpinion = opinions.reduce((best, current) => 
          current.opinion.u < best.opinion.u ? current : best
        );
        break;

      case 'truth':
        selectedOpinion = opinions.reduce((best, current) => 
          current.opinion.b > best.opinion.b ? current : best
        );
        break;

      case 'hybrid':
      default:
        selectedOpinion = opinions.reduce((best, current) => 
          current.expectation > best.expectation ? current : best
        );
        break;
    }

    const selectedValue = this._extractNumericValueFromProposition(selectedOpinion.proposition, capacity);

    return {
      method: 'subjective_logic',
      epistemicMode,
      selectedValue,
      selectedOpinion,
      allOpinions: opinions,
      reasoning: `Selected based on ${epistemicMode} numeric Subjective Logic analysis`
    };
  }

  /**
   * Perform numeric bilattice reconciliation
   * @private
   */
  static _performNumericBilatticeReconciliation(propositions, capacity, epistemicMode) {
    let selectedProposition;
    let ranking;

    switch (epistemicMode) {
      case 'information':
        selectedProposition = NumericBilatticeOrderings.findMostInformative(propositions, capacity);
        ranking = NumericBilatticeOrderings.rankByInformation(propositions, capacity);
        break;

      case 'truth':
        selectedProposition = NumericBilatticeOrderings.findMostTrue(propositions, capacity);
        ranking = NumericBilatticeOrderings.rankByTruth(propositions, capacity);
        break;

      case 'hybrid':
      default:
        // Use information ordering as primary, truth as tie-breaker
        const infoRanking = NumericBilatticeOrderings.rankByInformation(propositions, capacity);
        const truthRanking = NumericBilatticeOrderings.rankByTruth(propositions, capacity);
        
        // Find best proposition considering both orderings
        let bestScore = -1;
        let bestProposition = null;
        
        for (let i = 0; i < propositions.length; i++) {
          const infoRank = infoRanking.find(r => r.proposition === propositions[i])?.rank || propositions.length;
          const truthRank = truthRanking.find(r => r.proposition === propositions[i])?.rank || propositions.length;
          
          // Combined score (lower rank is better)
          const score = 1 / (infoRank + truthRank);
          
          if (score > bestScore) {
            bestScore = score;
            bestProposition = propositions[i];
          }
        }
        
        selectedProposition = {
          proposition: bestProposition,
          epistemic: NumericBilatticeOrderings._getEpistemicPair(new Set(bestProposition), capacity),
          rank: 1
        };
        ranking = infoRanking;
        break;
    }

    const selectedValue = this._extractNumericValueFromProposition(selectedProposition.proposition, capacity);

    return {
      method: 'bilattice',
      epistemicMode,
      selectedValue,
      selectedProposition,
      ranking,
      reasoning: `Selected based on ${epistemicMode} numeric bilattice ordering`
    };
  }

  /**
   * Create qualitative capacity from collected values
   * @private
   */
  static _createQualitativeCapacity(collectedValues, scale, capacityType) {
    if (!collectedValues || collectedValues.length === 0) {
      return null;
    }

    // Create state space from unique values
    const uniqueValues = [...new Set(collectedValues.map(cv => cv.value))];
    const stateSpace = uniqueValues.map((_, index) => `evidence_${index}`);

    // Create QMT based on capacity type
    const qmt = new Map();
    
    switch (capacityType) {
      case 'simple_support':
        collectedValues.forEach((cv, index) => {
          const evidenceSet = new Set([`evidence_${index}`]);
          qmt.set(evidenceSet, cv.possibility);
        });
        break;

      case 'possibility':
        collectedValues.forEach((cv, index) => {
          const singletonSet = new Set([`evidence_${index}`]);
          qmt.set(singletonSet, cv.possibility);
        });
        break;

      case 'necessity':
        const sortedValues = collectedValues
          .map((cv, index) => ({ value: cv.value, possibility: cv.possibility, index }))
          .sort((a, b) => b.possibility - a.possibility);

        sortedValues.forEach((item, rank) => {
          const nestedSet = new Set(sortedValues.slice(0, rank + 1).map(sv => `evidence_${sv.index}`));
          qmt.set(nestedSet, item.possibility);
        });
        break;

      default:
        throw new Error(`Unknown capacity type: ${capacityType}`);
    }

    return new QualitativeCapacity(stateSpace, scale, qmt);
  }

  /**
   * Create numeric capacity from collected values
   * @private
   */
  static _createNumericCapacity(collectedValues, capacityType) {
    if (!collectedValues || collectedValues.length === 0) {
      return null;
    }

    // Create a simple numeric capacity function
    const stateSpace = collectedValues.map((_, index) => `evidence_${index}`);
    
    return {
      stateSpace,
      getCapacity: (set) => {
        if (set.size === 0) return 0;
        
        // For numeric capacity, we use the maximum possibility of included evidence
        let maxPossibility = 0;
        for (const element of set) {
          const index = parseInt(element.replace('evidence_', ''));
          if (index >= 0 && index < collectedValues.length) {
            maxPossibility = Math.max(maxPossibility, collectedValues[index].possibility);
          }
        }
        return maxPossibility;
      }
    };
  }

  /**
   * Extract value from qualitative proposition
   * @private
   */
  static _extractValueFromProposition(proposition, capacity, scale) {
    const index = parseInt(proposition[0].replace('evidence_', ''));
    if (index >= 0 && index < capacity.stateSpace.length) {
      // Return the possibility value from the capacity
      const set = new Set([`evidence_${index}`]);
      return capacity.getCapacity(set);
    }
    return scale.bottom;
  }

  /**
   * Extract value from numeric proposition
   * @private
   */
  static _extractNumericValueFromProposition(proposition, capacity) {
    const index = parseInt(proposition[0].replace('evidence_', ''));
    if (index >= 0 && index < capacity.stateSpace.length) {
      // Return the possibility value from the capacity
      const set = new Set([`evidence_${index}`]);
      return capacity.getCapacity(set);
    }
    return 0;
  }

  /**
   * Simple qualitative aggregation fallback
   * @private
   */
  static _simpleQualitativeAggregation(collectedValues, aggregationMethod, scale) {
    const values = collectedValues.map(cv => cv.possibility);
    
    switch (aggregationMethod) {
      case 'max':
        return scale.maxAll(values);
      case 'min':
        return scale.minAll(values);
      case 'avg':
      case 'average':
        return scale.at(Math.floor(values.length / 2)); // Median
      default:
        return scale.maxAll(values);
    }
  }

  /**
   * Simple numeric aggregation fallback
   * @private
   */
  static _simpleNumericAggregation(collectedValues, aggregationMethod) {
    const values = collectedValues.map(cv => cv.possibility);
    
    switch (aggregationMethod) {
      case 'max':
        return Math.max(...values);
      case 'min':
        return Math.min(...values);
      case 'avg':
      case 'average':
        return values.reduce((sum, val) => sum + val, 0) / values.length;
      case 'sum':
        return values.reduce((sum, val) => sum + val, 0);
      default:
        return Math.max(...values);
    }
  }
}
