/**
 * Qualitative Scale System for Qualitative Capacities
 * 
 * Implements finite totally ordered scales with order-reversing negation
 * as described in the research paper on qualitative capacities.
 * 
 * A qualitative scale L is a finite, totally ordered set of values where:
 * - Only min, max, and comparison operators are used (no arithmetic)
 * - 0 and 1 are the bottom and top elements
 * - An order-reversing negation map ν: L → L exists where ν(ν(λ)) = λ
 */

export class QualitativeScale {
  constructor(values, name = 'default') {
    if (!Array.isArray(values) || values.length === 0) {
      throw new Error('QualitativeScale requires a non-empty array of values');
    }
    
    // Ensure values are sorted and unique
    this.values = [...new Set(values)].sort((a, b) => a - b);
    this.name = name;
    this.bottom = this.values[0];
    this.top = this.values[this.values.length - 1];
    
    // Validate that bottom is 0 and top is 1 (or equivalent)
    if (this.bottom !== 0 && this.bottom !== 0.0) {
      console.warn(`QualitativeScale: bottom value should be 0, got ${this.bottom}`);
    }
    if (this.top !== 1 && this.top !== 1.0) {
      console.warn(`QualitativeScale: top value should be 1, got ${this.top}`);
    }
    
    // Create order-reversing negation map
    this._createNegationMap();
    
    // Create Set for O(1) contains() lookups
    this.valueSet = new Set(this.values);
  }
  
  /**
   * Create the order-reversing negation map ν: L → L
   * For a scale [0, a, b, ..., 1], the negation maps:
   * 0 → 1, a → ..., b → ..., 1 → 0
   */
  _createNegationMap() {
    this.negationMap = new Map();
    const n = this.values.length;
    
    for (let i = 0; i < n; i++) {
      const value = this.values[i];
      const negatedValue = this.values[n - 1 - i];
      this.negationMap.set(value, negatedValue);
    }
  }
  
  /**
   * Get the negation of a value: ν(λ)
   */
  negate(value) {
    if (!this.negationMap.has(value)) {
      throw new Error(`Value ${value} not found in scale ${this.name}`);
    }
    return this.negationMap.get(value);
  }
  
  /**
   * Check if a value is in the scale
   * OPTIMIZED: Uses Set for O(1) average time complexity
   */
  contains(value) {
    return this.valueSet.has(value);
  }
  
  /**
   * Get the minimum of two values
   */
  min(a, b) {
    if (!this.contains(a) || !this.contains(b)) {
      throw new Error(`Values must be in scale ${this.name}`);
    }
    return a <= b ? a : b;
  }
  
  /**
   * Get the maximum of two values
   */
  max(a, b) {
    if (!this.contains(a) || !this.contains(b)) {
      throw new Error(`Values must be in scale ${this.name}`);
    }
    return a >= b ? a : b;
  }
  
  /**
   * Get the minimum of multiple values
   */
  minAll(values) {
    if (!values.length) return this.bottom;
    return values.reduce((acc, val) => this.min(acc, val), this.top);
  }
  
  /**
   * Get the maximum of multiple values
   */
  maxAll(values) {
    if (!values.length) return this.bottom;
    return values.reduce((acc, val) => this.max(acc, val), this.bottom);
  }
  
  /**
   * Compare two values: returns -1, 0, or 1
   */
  compare(a, b) {
    if (!this.contains(a) || !this.contains(b)) {
      throw new Error(`Values must be in scale ${this.name}`);
    }
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
  }
  
  /**
   * Get the index of a value in the scale
   * OPTIMIZED: Uses binary search for O(log n) time complexity
   */
  indexOf(value) {
    if (!this.contains(value)) {
      return -1;
    }
    
    // Binary search since values are sorted
    let left = 0;
    let right = this.values.length - 1;
    
    while (left <= right) {
      const mid = Math.floor((left + right) / 2);
      if (this.values[mid] === value) {
        return mid;
      } else if (this.values[mid] < value) {
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
    
    return -1; // Should not reach here if contains() is correct
  }
  
  /**
   * Get the value at a given index
   */
  at(index) {
    return this.values[index];
  }
  
  /**
   * Get the size of the scale
   */
  get size() {
    return this.values.length;
  }
  
  /**
   * Check if this scale is equivalent to another
   */
  equals(other) {
    if (!(other instanceof QualitativeScale)) return false;
    if (this.values.length !== other.values.length) return false;
    return this.values.every((val, i) => val === other.values[i]);
  }
  
  /**
   * Create a string representation
   */
  toString() {
    return `QualitativeScale(${this.name}): [${this.values.join(', ')}]`;
  }
  
  /**
   * Create common qualitative scales
   */
  static binary() {
    return new QualitativeScale([0, 1], 'binary');
  }
  
  static ternary() {
    return new QualitativeScale([0, 0.5, 1], 'ternary');
  }
  
  static fivePoint() {
    return new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'five-point');
  }
  
  static tenPoint() {
    return new QualitativeScale([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], 'ten-point');
  }
  
  static custom(values, name = 'custom') {
    return new QualitativeScale(values, name);
  }
}

/**
 * Default qualitative scale for the system
 */
export const DEFAULT_QUALITATIVE_SCALE = QualitativeScale.fivePoint();
