/**
 * Qualitative Capacity Implementation
 * 
 * Implements qualitative capacities (q-capacities) as described in the research paper.
 * A qualitative capacity γ: 2^W → L is a monotonic set-function where:
 * - γ(∅) = 0, γ(W) = 1
 * - If A ⊆ B, then γ(A) ≤ γ(B)
 * 
 * The core design principle is to use the Qualitative Möbius Transform (QMT) γ#
 * as the canonical internal representation for any q-capacity γ.
 */

import { QualitativeScale, DEFAULT_QUALITATIVE_SCALE } from './QualitativeScale.js';
import { getSetKey, setFromKey, setsEqual } from './SetUtils.js';

export class QualitativeCapacity {
  constructor(stateSpace, scale = DEFAULT_QUALITATIVE_SCALE, qmt = null) {
    if (!Array.isArray(stateSpace) || stateSpace.length === 0) {
      throw new Error('State space must be a non-empty array');
    }
    
    this.stateSpace = [...new Set(stateSpace)]; // Ensure unique states
    this.scale = scale;
    
    // Internal representation: QMT as a Map from canonical string keys to scale values
    // Only store non-zero entries
    this.qmt = new Map();
    
    if (qmt) {
      this._initializeFromQMT(qmt);
    } else {
      // Initialize as vacuous capacity (ignorance)
      this.qmt.set(getSetKey(new Set(this.stateSpace)), scale.top);
    }
  }
  
  /**
   * Initialize from a QMT representation
   * @param {Map|Object} qmt - QMT as Map or object with subset keys
   */
  _initializeFromQMT(qmt) {
    this.qmt.clear();
    
    if (qmt instanceof Map) {
      for (const [subset, value] of qmt) {
        if (value !== this.scale.bottom) {
          // If subset is already a Set, use it directly. Canonical string
          // keys ('a,b,c') come from getSetKey and must be split back into
          // elements — new Set('abc') would iterate characters.
          const subsetSet = subset instanceof Set
            ? subset
            : (typeof subset === 'string' ? setFromKey(subset) : new Set(subset));
          this.qmt.set(getSetKey(subsetSet), value);
        }
      }
    } else if (typeof qmt === 'object') {
      for (const [key, value] of Object.entries(qmt)) {
        if (value !== this.scale.bottom) {
          const subset = new Set(JSON.parse(key));
          this.qmt.set(getSetKey(subset), value);
        }
      }
    }
  }
  
  /**
   * Get the capacity value for a subset A ⊆ W
   * γ(A) = max_{B ⊆ A, B ∈ dom(γ#)} γ#(B)
   */
  getCapacity(subset) {
    const A = new Set(subset);
    
    // Find all focal sets (keys in QMT) that are subsets of A
    let maxValue = this.scale.bottom;
    
    for (const [key, value] of this.qmt) {
      const focalSet = setFromKey(key, this.stateSpace);
      if (this._isSubset(focalSet, A)) {
        maxValue = this.scale.max(maxValue, value);
      }
    }
    
    return maxValue;
  }
  
  /**
   * Set the capacity value for a subset by updating the QMT
   * This is a complex operation that may require recomputing the entire QMT
   */
  setCapacity(subset, value) {
    const A = new Set(subset);
    const key = getSetKey(A);
    
    // For now, we'll implement a simple approach:
    // Add this subset as a focal set with the given value
    // In a full implementation, we'd need to recompute the QMT properly
    if (value !== this.scale.bottom) {
      this.qmt.set(key, value);
    } else {
      this.qmt.delete(key);
    }
  }
  
  /**
   * Get the Qualitative Möbius Transform value for a subset
   */
  getQMT(subset) {
    const A = new Set(subset);
    const key = getSetKey(A);
    
    // Direct lookup using canonical key
    return this.qmt.get(key) || this.scale.bottom;
  }
  
  /**
   * Get all focal sets (subsets with non-zero QMT values)
   */
  getFocalSets() {
    return Array.from(this.qmt.keys()).map(key => setFromKey(key, this.stateSpace));
  }
  
  /**
   * Get the QMT as a Map
   */
  getQMTMap() {
    const result = new Map();
    for (const [key, value] of this.qmt) {
      result.set(setFromKey(key, this.stateSpace), value);
    }
    return result;
  }
  
  /**
   * Check if this is a possibility measure
   * A capacity is a possibility measure if all focal sets are singletons
   */
  isPossibilityMeasure() {
    for (const key of this.qmt.keys()) {
      const focalSet = setFromKey(key, this.stateSpace);
      if (focalSet.size !== 1) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * Check if this is a necessity measure
   * A capacity is a necessity measure if all focal sets form a nested chain
   */
  isNecessityMeasure() {
    const focalSets = this.getFocalSets();
    if (focalSets.length === 0) return true;
    
    // Check if all focal sets are nested (form a chain)
    for (let i = 0; i < focalSets.length; i++) {
      for (let j = i + 1; j < focalSets.length; j++) {
        const A = focalSets[i];
        const B = focalSets[j];
        if (!this._isSubset(A, B) && !this._isSubset(B, A)) {
          return false;
        }
      }
    }
    return true;
  }
  
  /**
   * Compute the contour function π_γ
   * π_γ(w) = max_{B: w ∈ B} γ#(B)
   */
  getContourFunction() {
    const contour = new Map();
    
    for (const state of this.stateSpace) {
      let maxValue = this.scale.bottom;
      
      for (const [key, value] of this.qmt) {
        const focalSet = setFromKey(key, this.stateSpace);
        if (focalSet.has(state)) {
          maxValue = this.scale.max(maxValue, value);
        }
      }
      
      contour.set(state, maxValue);
    }
    
    return contour;
  }
  
  /**
   * Compute the upper capacity (possibility measure) Pl_γ
   * Pl_γ(A) = max_{w ∈ A} π_γ(w)
   */
  getUpperCapacity() {
    const contour = this.getContourFunction();
    
    // Create a new capacity that is a possibility measure
    const upperQMT = new Map();
    
    for (const [state, value] of contour) {
      if (value !== this.scale.bottom) {
        upperQMT.set(new Set([state]), value);
      }
    }
    
    return new QualitativeCapacity(this.stateSpace, this.scale, upperQMT);
  }
  
  /**
   * Compute the conjugate capacity γ^c
   * γ^c(A) = ν(γ(A^c))
   */
  getConjugate() {
    const conjugateQMT = new Map();
    
    // For each subset A, compute γ^c(A) = ν(γ(A^c))
    const allSubsets = this._generateAllSubsets();
    
    for (const A of allSubsets) {
      const complement = new Set(this.stateSpace.filter(s => !A.has(s)));
      const complementValue = this.getCapacity(complement);
      const conjugateValue = this.scale.negate(complementValue);
      
      if (conjugateValue !== this.scale.bottom) {
        conjugateQMT.set(A, conjugateValue);
      }
    }
    
    return new QualitativeCapacity(this.stateSpace, this.scale, conjugateQMT);
  }
  
  /**
   * Check if A is a subset of B
   */
  _isSubset(A, B) {
    for (const element of A) {
      if (!B.has(element)) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * Generate all possible subsets of the state space
   */
  _generateAllSubsets() {
    const subsets = [];
    const n = this.stateSpace.length;
    
    // Generate all 2^n subsets
    for (let i = 0; i < (1 << n); i++) {
      const subset = new Set();
      for (let j = 0; j < n; j++) {
        if (i & (1 << j)) {
          subset.add(this.stateSpace[j]);
        }
      }
      subsets.push(subset);
    }
    
    return subsets;
  }
  
  /**
   * Create a Simple Support Capacity (SSC)
   * A necessity measure with focal sets A and W
   */
  static createSimpleSupport(stateSpace, supportSet, supportValue, scale = DEFAULT_QUALITATIVE_SCALE) {
    const A = new Set(supportSet);
    const W = new Set(stateSpace);
    
    const qmt = new Map();
    qmt.set(A, supportValue);
    qmt.set(W, scale.top);
    
    return new QualitativeCapacity(stateSpace, scale, qmt);
  }
  
  /**
   * Create a possibility measure from a possibility distribution
   */
  static createPossibilityMeasure(stateSpace, possibilityDistribution, scale = DEFAULT_QUALITATIVE_SCALE) {
    const qmt = new Map();
    
    for (const [state, value] of Object.entries(possibilityDistribution)) {
      if (value !== scale.bottom) {
        qmt.set(new Set([state]), value);
      }
    }
    
    return new QualitativeCapacity(stateSpace, scale, qmt);
  }
  
  /**
   * Create a necessity measure from a possibility distribution
   */
  static createNecessityMeasure(stateSpace, possibilityDistribution, scale = DEFAULT_QUALITATIVE_SCALE) {
    const capacity = this.createPossibilityMeasure(stateSpace, possibilityDistribution, scale);
    return capacity.getConjugate();
  }
  
  /**
   * Get a string representation of the capacity
   */
  toString() {
    const focalSets = Array.from(this.qmt.entries())
      .map(([key, value]) => `{${key || '∅'}}:${value}`)
      .join(', ');
    
    return `QualitativeCapacity(${this.stateSpace.length} states, ${this.qmt.size} focal sets): {${focalSets}}`;
  }
  
  /**
   * Check if this capacity equals another
   */
  equals(other) {
    if (!(other instanceof QualitativeCapacity)) return false;
    if (!this.scale.equals(other.scale)) return false;
    if (this.stateSpace.length !== other.stateSpace.length) return false;
    
    // Check if all focal sets and values match
    if (this.qmt.size !== other.qmt.size) return false;
    
    for (const [key, value] of this.qmt) {
      if (other.qmt.get(key) !== value) {
        return false;
      }
    }
    
    return true;
  }
  
}
