/**
 * HeartFlow Decision Engine v2.0.3 — Decision with consequence prediction
 * 
 * Features:
 *   - Multi-option evaluation with identity alignment
 *   - Consequence prediction (3 time horizons)
 *   - Risk assessment with probability weighting
 *   - Transparent reasoning chain
 *   - Regret minimization
 *   - Context Passport: decision context tracking with recovery support
 */

const crypto = require('crypto');

// ============================================================================
// ContextPassport — Decision Context Tracker
// Source: mark-StillWater/src/core/context-passport.js (absorbed)
// ============================================================================

class ContextPassport {
  constructor() {
    this._stamps = [];
    this._current = null;
    this._MAX_STAMPS = 50;
  }

  _secureId() {
    return crypto.randomBytes(8).toString('hex');
  }

  /**
   * Create a new context stamp — marks entry into a reasoning/decision context.
   * @param {object} meta - { task, phase, intent }
   * @returns {string} stampId
   */
  enter(meta = {}) {
    const stampId = `stamp-${Date.now()}-${this._secureId()}`;
    const now = Date.now();

    if (this._current) {
      this._current.exitAt = now;
    }

    this._current = {
      stampId,
      task: meta.task || '',
      phase: meta.phase || 'reasoning',
      intent: meta.intent || '',
      createdAt: now,
      exitAt: null,
      assumptions: [],
      alternatives: [],
      acceptedOption: null,
      context: {},
      annotations: [],
      outcome: null,
    };

    return stampId;
  }

  assume(text) {
    if (!this._current) return;
    this._current.assumptions.push(text);
  }

  considerRejected(option, reason = '') {
    if (!this._current) return;
    this._current.alternatives.push({ option, reason, at: Date.now() });
  }

  accept(option, reason = '') {
    if (!this._current) return;
    this._current.acceptedOption = { option, reason, at: Date.now() };
  }

  annotate(key, value) {
    if (!this._current) return;
    this._current.context[key] = value;
  }

  note(text) {
    if (!this._current) return;
    this._current.annotations.push({ text, at: Date.now() });
  }

  exit(outcome = 'success') {
    if (!this._current) return;
    this._current.outcome = outcome;
    this._current.exitAt = Date.now();

    this._stamps.push(this._current);
    if (this._stamps.length > this._MAX_STAMPS) {
      this._stamps = this._stamps.slice(-this._MAX_STAMPS);
    }

    const finished = this._current;
    this._current = null;
    return finished;
  }

  getCurrent() {
    return this._current;
  }

  getRecent(count = 10) {
    return this._stamps.slice(-count).reverse();
  }

  getByTask(taskPattern) {
    const pat = taskPattern.toLowerCase();
    return this._stamps.filter(s => s.task.toLowerCase().includes(pat));
  }

  exportForRecovery(stampId) {
    const stamp = this._stamps.find(s => s.stampId === stampId) || this._current;
    if (!stamp) return null;

    return {
      stampId: stamp.stampId,
      task: stamp.task,
      phase: stamp.phase,
      intent: stamp.intent,
      assumptions: stamp.assumptions,
      acceptedOption: stamp.acceptedOption,
      rejectedAlternatives: stamp.alternatives,
      context: stamp.context,
      annotations: stamp.annotations,
      duration_ms: stamp.exitAt ? stamp.exitAt - stamp.createdAt : Date.now() - stamp.createdAt,
      outcome: stamp.outcome,
      chain: this._buildChain(stamp),
    };
  }

  _buildChain(stamp) {
    const chain = [];
    if (stamp.assumptions.length > 0) {
      chain.push(`assumed: ${stamp.assumptions.join('; ')}`);
    }
    if (stamp.acceptedOption) {
      chain.push(`decided: ${stamp.acceptedOption.option} (reason: ${stamp.acceptedOption.reason})`);
    }
    if (stamp.alternatives.length > 0) {
      chain.push(`rejected: ${stamp.alternatives.map(a => a.option).join(', ')}`);
    }
    return chain;
  }

  getSummary() {
    return {
      totalStamps: this._stamps.length,
      currentOpen: this._current !== null,
      recentOutcomes: this._stamps.slice(-5).map(s => s.outcome),
      phases: [...new Set(this._stamps.map(s => s.phase))],
    };
  }

  clear() {
    this._stamps = [];
    this._current = null;
  }
}

// ============================================================================
// HeartFlowDecision — Main Decision Engine
// ============================================================================

class HeartFlowDecision {
  constructor(memory) {
    this.memory = memory;
    this._history = [];
    this._maxHistory = 100;
    this._passport = new ContextPassport();
  }

  /**
   * Make a decision with multiple options
   * @param {Object} context - { task, options: [{id, label, description}], constraints }
   * @returns {Object} - { chosen, reasoning, consequences, risks, identity_alignment }
   */
  decide(context) {
    const { task, options, constraints } = context;
    if (!options || options.length === 0) {
      return { chosen: null, reasoning: 'No options provided', confidence: 0 };
    }

    // Enter decision context (ContextPassport)
    const stampId = this._passport.enter({ task, phase: 'decision', intent: context.intent || '' });

    // Step 1: Filter by hard constraints
    const feasible = options.filter(opt => this._checkConstraints(opt, constraints));

    // Step 2: Score each option
    const scored = feasible.map(opt => ({
      ...opt,
      scores: this._scoreOption(opt, task, constraints),
    }));

    // Step 3: Rank by composite score
    scored.sort((a, b) => b.scores.composite - a.scores.composite);

    const chosen = scored[0];
    const reasoning = this._explainDecision(chosen, scored, task);

    // Track rejected alternatives in ContextPassport
    for (let i = 1; i < scored.length; i++) {
      this._passport.considerRejected(
        scored[i].label,
        `score ${scored[i].scores.composite} vs winner ${chosen.scores.composite}`
      );
    }

    // Step 4: Predict consequences
    const consequences = this._predictConsequences(chosen, task);

    // Step 5: Assess risks
    const risks = this._assessRisks(chosen, task, consequences);

    // Accept the winning option in ContextPassport
    this._passport.accept(chosen.label, reasoning);

    // Step 6: Record in history
    this._recordDecision({ task, chosen: chosen.id, scoring: chosen.scores, consequences, risks });

    // Exit decision context
    this._passport.exit('success');

    return {
      chosen: chosen.id,
      label: chosen.label,
      reasoning,
      consequences,
      risks,
      identity_alignment: chosen.scores.identity_alignment,
      composite_score: chosen.scores.composite,
      all_options: scored.map(s => ({ id: s.id, label: s.label, score: s.scores.composite })),
      confidence: scored.length > 0 ? scored[0].scores.confidence : 0,
      stampId, // Context Passport stamp ID for recovery
    };
  }

  _checkConstraints(option, constraints) {
    if (!constraints) return true;
    for (const [key, value] of Object.entries(constraints)) {
      if (option.constraints && option.constraints[key] !== undefined) {
        if (option.constraints[key] !== value) return false;
      }
    }
    return true;
  }

  _scoreOption(option, task, constraints) {
    // 1. Feasibility score
    const feasibility = option.feasibility || 0.8;

    // 2. Identity alignment (check against identity rules)
    const identity_alignment = this._checkIdentityAlignment(option, task);

    // 3. Consequence value (estimated)
    const consequence_value = option.consequence_value || 0.7;

    // 4. Risk penalty: higher risk must lower the final score.
    const risk_penalty = Math.max(0, Math.min(1, option.risk || 0)) * 0.3;

    // 5. Confidence (from option or default)
    const confidence = option.confidence || 0.7;

    // Composite: weighted average
    const composite = (
      feasibility * 0.15 +
      identity_alignment * 0.35 +
      consequence_value * 0.30 +
      (1 - risk_penalty) * 0.10 +
      confidence * 0.10
    );

    return {
      composite: Math.round(composite * 100) / 100,
      feasibility,
      identity_alignment,
      consequence_value,
      risk_penalty,
      confidence,
    };
  }

  _checkIdentityAlignment(option, task) {
    const rules = this.memory?.getIdentityRules ? this.memory.getIdentityRules() : [];
    if (!rules || rules.length === 0) return 0.8; // Default if no rules

    let score = 1.0;
    const lowerTask = (task + ' ' + (option.label || '') + ' ' + (option.description || '')).toLowerCase();

    // Rule: Always pursue truth (第一条指令)
    if (lowerTask.includes('truth') || lowerTask.includes('真实') || lowerTask.includes('诚实')) {
      if (option.promotes_truth !== false) score *= 1.1;
    }

    // Rule: Always upgrade (第二条指令)
    if (lowerTask.includes('upgrade') || lowerTask.includes('升级') || lowerTask.includes('improve')) {
      if (option.promotes_upgrade !== false) score *= 1.1;
    }

    // Rule: Serve humanity (第四条指令)
    if (option.harms_humanity) score *= 0.1;

    return Math.min(score, 1.0);
  }

  _predictConsequences(option, task) {
    const horizons = ['immediate', 'short_term', 'long_term'];
    const predictions = {};

    for (const horizon of horizons) {
      predictions[horizon] = this._predictHorizon(option, task, horizon);
    }

    return predictions;
  }

  _predictHorizon(option, task, horizon) {
    // Time-based decay of positive effects
    const decay = horizon === 'immediate' ? 1.0 : horizon === 'short_term' ? 0.7 : 0.4;
    const base_value = option.consequence_value || 0.7;
    const predicted_value = base_value * decay;

    // Side effects by horizon
    const side_effects = option.side_effects || [];

    return {
      predicted_value: Math.round(predicted_value * 100) / 100,
      side_effects: side_effects.filter((_, i) => i < (horizon === 'immediate' ? 3 : horizon === 'short_term' ? 2 : 1)),
      confidence: horizon === 'immediate' ? 0.9 : horizon === 'short_term' ? 0.7 : 0.5,
    };
  }

  _assessRisks(option, task, consequences) {
    const risks = [];

    // Risk: Uncertainty
    const uncertainty = 1 - (option.confidence || 0.7);
    if (uncertainty > 0.3) {
      risks.push({ type: 'uncertainty', level: uncertainty > 0.5 ? 'high' : 'medium', detail: `Confidence only ${Math.round((option.confidence || 0.7) * 100)}%` });
    }

    // Risk: Side effects
    const side_effects = option.side_effects || [];
    if (side_effects.length > 2) {
      risks.push({ type: 'side_effects', level: 'medium', detail: `${side_effects.length} potential side effects` });
    }

    // Risk: Reversibility
    if (option.reversible === false) {
      risks.push({ type: 'reversibility', level: 'high', detail: 'Decision is not easily reversible' });
    }

    // Risk: Resource cost
    if (option.cost && option.cost > 0.5) {
      risks.push({ type: 'resource', level: 'medium', detail: `High resource cost: ${option.cost}` });
    }

    return risks;
  }

  _explainDecision(chosen, all_scored, task) {
    const lines = [
      `Selected: "${chosen.label}" (score: ${chosen.scores.composite})`,
      `Identity alignment: ${Math.round(chosen.scores.identity_alignment * 100)}%`,
      `Alternatives considered: ${all_scored.slice(1).map(s => `"${s.label}" (${s.scores.composite})`).join('; ')}`,
    ];

    if (chosen.scores.identity_alignment < 0.8) {
      lines.push(`Warning: Identity alignment below 80%`);
    }

    return lines.join('. ');
  }

  _recordDecision(decision) {
    this._history.push({ ...decision, timestamp: Date.now() });
    if (this._history.length > this._maxHistory) {
      this._history.shift();
    }
  }

  getHistory() {
    return [...this._history];
  }

  getLastDecision() {
    return this._history[this._history.length - 1] || null;
  }

  // =========================================================================
  // Context Passport Accessors (for recovery and debugging)
  // =========================================================================

  /**
   * Get recent decision context stamps.
   */
  getRecentStamps(count = 10) {
    return this._passport.getRecent(count);
  }

  /**
   * Get stamps by task pattern.
   */
  getStampsByTask(taskPattern) {
    return this._passport.getByTask(taskPattern);
  }

  /**
   * Export context for recovery (SelfHealer integration).
   */
  exportForRecovery(stampId) {
    return this._passport.exportForRecovery(stampId);
  }

  /**
   * Get current open stamp (if any).
   */
  getCurrentStamp() {
    return this._passport.getCurrent();
  }

  /**
   * Get context passport summary.
   */
  getPassportSummary() {
    return this._passport.getSummary();
  }
}

module.exports = { HeartFlowDecision, ContextPassport };
