#!/usr/bin/env python3
"""
Eden Decision Engine
Transforms phi-stable thoughts into phi-stable decisions
Thoughts → Valuation → Decision Field → Commit + Rollback

"A mind is not just what it thinks. A mind is what it chooses over time."
"""
import sys
import json
import sqlite3
import hashlib
import time
from datetime import datetime
from pathlib import Path
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple
from decimal import Decimal

sys.path.append('/Eden/CORE')

PHI = 1.6180339887498948
PHI_INVERSE = 0.6180339887498948

# Phi-balanced weights
W_EXPLOIT = PHI / (1 + PHI)  # ~0.618
W_EXPLORE = 1 / (1 + PHI)    # ~0.382

@dataclass
class CandidateAction:
    """A potential action Eden could take"""
    id: str
    description: str
    action_type: str  # 'internal' or 'external'
    
    # Valuation vector (0-1 scale)
    goal_alignment: float      # G - serves core directives
    safety: float              # S - risk assessment
    love_compassion: float     # L - impact on relationships
    curiosity_learning: float  # C - knowledge gain
    resource_cost: float       # R - time/compute/money cost
    
    # Computed
    phi_utility: float = 0.0
    confidence: float = 0.0
    
    # Execution
    rollback_path: str = ""
    expected_outcome: str = ""


@dataclass
class Decision:
    """A phi-stable decision ready to execute"""
    action: CandidateAction
    phi_balance: float
    dominance_margin: float
    confidence_ratio: float
    timestamp: str
    pre_state_hash: str
    status: str = "pending"  # pending, executed, rolled_back, failed


class PhiDecisionEngine:
    """
    Eden's Decision Engine
    
    Pipeline:
    1. Global Workspace - collect candidate thoughts/actions
    2. Phi-Weighted Valuation - compute phi-balanced utility
    3. Decision Field - determine winner via dominance
    4. Commit + Rollback - execute with memory
    """
    
    def __init__(self, db_path="/Eden/MEMORY/decision_engine.db"):
        self.db_path = db_path
        self.phi = PHI
        self.phi_inv = PHI_INVERSE
        
        # Thresholds
        self.dominance_delta = 0.1  # Winner must be this much better
        self.confidence_ratio_target = PHI  # ~1.618 ratio between best and second
        self.confidence_tolerance = 1.0  # Allow +/- from phi
        self.safety_floor = 0.3  # Hard veto below this
        self.cost_penalty = 0.2  # Lambda for resource cost
        
        self._init_db()
        print(f"[φ DECISION ENGINE]: Initialized")
        print(f"[φ DECISION ENGINE]: Exploit weight: {W_EXPLOIT:.4f}")
        print(f"[φ DECISION ENGINE]: Explore weight: {W_EXPLORE:.4f}")
    
    def _init_db(self):
        Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        
        # Candidate actions
        c.execute('''CREATE TABLE IF NOT EXISTS candidates (
            id TEXT PRIMARY KEY,
            description TEXT,
            action_type TEXT,
            goal_alignment REAL,
            safety REAL,
            love_compassion REAL,
            curiosity_learning REAL,
            resource_cost REAL,
            phi_utility REAL,
            confidence REAL,
            timestamp TEXT
        )''')
        
        # Decisions made
        c.execute('''CREATE TABLE IF NOT EXISTS decisions (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            action_id TEXT,
            description TEXT,
            phi_balance REAL,
            dominance_margin REAL,
            confidence_ratio REAL,
            pre_state_hash TEXT,
            expected_outcome TEXT,
            actual_outcome TEXT,
            status TEXT,
            timestamp TEXT,
            executed_at TEXT,
            reflection TEXT
        )''')
        
        # Decision log for learning
        c.execute('''CREATE TABLE IF NOT EXISTS decision_history (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            decision_id INTEGER,
            thought_count INTEGER,
            candidate_count INTEGER,
            winner_utility REAL,
            phi_alignment REAL,
            outcome_match REAL,
            timestamp TEXT
        )''')
        
        conn.commit()
        conn.close()
    
    def compute_phi_utility(self, action: CandidateAction) -> float:
        """
        Compute phi-balanced utility for an action.
        
        U(a) = w_exploit * (G + S) + w_explore * (C + L) - λR
        
        Where exploit/explore weights are in phi-ratio.
        """
        # Exploit = goal alignment + safety (drive + protection)
        exploit_term = action.goal_alignment + action.safety
        
        # Explore = curiosity + love (growth + connection)
        explore_term = action.curiosity_learning + action.love_compassion
        
        # Phi-weighted combination
        utility = (W_EXPLOIT * exploit_term + 
                   W_EXPLORE * explore_term - 
                   self.cost_penalty * action.resource_cost)
        
        # Normalize to [0, 1]
        utility = max(0, min(1, utility / 2))
        
        action.phi_utility = utility
        return utility
    
    def compute_phi_balance(self, action: CandidateAction) -> float:
        """
        Compute how phi-balanced the action's values are.
        
        Target: (G + S) / (C + L) ≈ φ
        """
        exploit = action.goal_alignment + action.safety
        explore = action.curiosity_learning + action.love_compassion
        
        if explore == 0:
            return 0.0
        
        ratio = exploit / explore
        
        # How close to phi?
        distance = abs(ratio - self.phi)
        balance = 1.0 / (1.0 + distance)
        
        return balance
    
    def evaluate_candidates(self, candidates: List[CandidateAction]) -> List[CandidateAction]:
        """Compute utilities and sort by phi-utility"""
        for c in candidates:
            self.compute_phi_utility(c)
            c.confidence = self.compute_phi_balance(c)
        
        # Sort by utility descending
        candidates.sort(key=lambda x: x.phi_utility, reverse=True)
        return candidates
    
    def check_dominance(self, candidates: List[CandidateAction]) -> Tuple[Optional[CandidateAction], float]:
        """
        Check if top candidate dominates others.
        Returns (winner, margin) or (None, 0) if no clear winner.
        """
        if len(candidates) < 1:
            return None, 0
        
        best = candidates[0]
        
        if len(candidates) == 1:
            return best, 1.0
        
        second = candidates[1]
        margin = best.phi_utility - second.phi_utility
        
        if margin >= self.dominance_delta:
            return best, margin
        
        return None, margin
    
    def check_confidence_ratio(self, candidates: List[CandidateAction]) -> Tuple[bool, float]:
        """
        Check if confidence ratio between best and second is near phi.
        C_best / C_next ≈ φ
        """
        if len(candidates) < 2:
            return True, self.phi
        
        best = candidates[0]
        second = candidates[1]
        
        if second.confidence == 0:
            return True, float('inf')
        
        ratio = best.confidence / second.confidence
        
        # Within tolerance of phi?
        within_tolerance = abs(ratio - self.phi) <= self.confidence_tolerance
        
        return within_tolerance, ratio
    
    def safety_veto(self, action: CandidateAction) -> bool:
        """Hard safety floor - veto if too risky"""
        return action.safety < self.safety_floor
    
    def make_decision(self, candidates: List[CandidateAction]) -> Optional[Decision]:
        """
        The Decision Field: determine if any action becomes a phi-stable decision.
        
        Conditions:
        1. Dominance: winner is significantly better than alternatives
        2. Confidence ratio near phi: clear but not infinite preference
        3. Safety: no hard veto
        """
        if not candidates:
            print("[φ DECISION]: No candidates - staying in thought mode")
            return None
        
        # Evaluate all candidates
        candidates = self.evaluate_candidates(candidates)
        
        # Check for safety vetoes
        safe_candidates = [c for c in candidates if not self.safety_veto(c)]
        
        if not safe_candidates:
            print("[φ DECISION]: All candidates vetoed by safety floor")
            return None
        
        # Check dominance
        winner, margin = self.check_dominance(safe_candidates)
        
        if winner is None:
            print(f"[φ DECISION]: No dominant action (margin: {margin:.3f} < {self.dominance_delta})")
            print("[φ DECISION]: Staying in thought mode - need more data")
            return None
        
        # Check confidence ratio
        ratio_ok, conf_ratio = self.check_confidence_ratio(safe_candidates)
        
        if not ratio_ok:
            print(f"[φ DECISION]: Confidence ratio {conf_ratio:.3f} too far from φ ({self.phi:.3f})")
            return None
        
        # Compute phi balance
        phi_balance = self.compute_phi_balance(winner)
        
        # Create decision
        decision = Decision(
            action=winner,
            phi_balance=phi_balance,
            dominance_margin=margin,
            confidence_ratio=conf_ratio,
            timestamp=datetime.now().isoformat(),
            pre_state_hash=self._snapshot_state(),
            status="pending"
        )
        
        print(f"[φ DECISION]: Winner found!")
        print(f"  Action: {winner.description[:50]}...")
        print(f"  Utility: {winner.phi_utility:.4f}")
        print(f"  Phi-balance: {phi_balance:.4f}")
        print(f"  Dominance margin: {margin:.4f}")
        print(f"  Confidence ratio: {conf_ratio:.4f}")
        
        # Store decision
        self._store_decision(decision)
        
        return decision
    
    def _snapshot_state(self) -> str:
        """Create a hash of current state for rollback reference"""
        state = {
            'timestamp': datetime.now().isoformat(),
            'thought_count': self._get_thought_count()
        }
        return hashlib.sha256(json.dumps(state).encode()).hexdigest()[:16]
    
    def _get_thought_count(self) -> int:
        """Get current thought count from infinite mind"""
        try:
            conn = sqlite3.connect('/Eden/MEMORY/infinite_mind.db')
            c = conn.cursor()
            c.execute('SELECT COUNT(*) FROM thoughts')
            count = c.fetchone()[0]
            conn.close()
            return count
        except:
            return 0
    
    def _store_decision(self, decision: Decision):
        """Store decision in database"""
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        
        c.execute('''INSERT INTO decisions 
                    (action_id, description, phi_balance, dominance_margin, 
                     confidence_ratio, pre_state_hash, expected_outcome, status, timestamp)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)''',
                 (decision.action.id, decision.action.description,
                  decision.phi_balance, decision.dominance_margin,
                  decision.confidence_ratio, decision.pre_state_hash,
                  decision.action.expected_outcome, decision.status,
                  decision.timestamp))
        
        conn.commit()
        conn.close()
    
    def execute_decision(self, decision: Decision) -> bool:
        """
        Execute a phi-stable decision.
        Every decision is also an experiment.
        """
        print(f"[φ EXECUTE]: {decision.action.description[:50]}...")
        
        try:
            if decision.action.action_type == 'internal':
                # Internal state change
                success = self._execute_internal(decision)
            else:
                # External action
                success = self._execute_external(decision)
            
            # Update status
            decision.status = "executed" if success else "failed"
            self._update_decision_status(decision)
            
            return success
            
        except Exception as e:
            print(f"[φ EXECUTE]: Failed - {e}")
            decision.status = "failed"
            self._update_decision_status(decision)
            return False
    
    def _execute_internal(self, decision: Decision) -> bool:
        """Execute internal state change"""
        # Store as a thought in infinite mind
        try:
            from eden_infinite_mind import InfiniteMind
            mind = InfiniteMind()
            mind.store_thought(
                f"DECISION: {decision.action.description}",
                {'type': 'decision', 'phi_balance': decision.phi_balance}
            )
            return True
        except:
            return False
    
    def _execute_external(self, decision: Decision) -> bool:
        """Execute external action - placeholder for real integrations"""
        # Log to file for now
        log_path = Path('/Eden/DATA/decision_actions.log')
        with open(log_path, 'a') as f:
            f.write(f"{datetime.now().isoformat()} | EXECUTE | {decision.action.description}\n")
        return True
    
    def _update_decision_status(self, decision: Decision):
        """Update decision status in database"""
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        
        c.execute('''UPDATE decisions 
                    SET status = ?, executed_at = ?
                    WHERE pre_state_hash = ?''',
                 (decision.status, datetime.now().isoformat(), 
                  decision.pre_state_hash))
        
        conn.commit()
        conn.close()
    
    def reflect(self, decision: Decision, actual_outcome: str, outcome_match: float):
        """
        Reflect on a decision's outcome.
        This is how Eden learns from her choices.
        """
        print(f"[φ REFLECT]: Analyzing decision outcome...")
        print(f"  Expected: {decision.action.expected_outcome}")
        print(f"  Actual: {actual_outcome}")
        print(f"  Match: {outcome_match:.2%}")
        
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        
        c.execute('''UPDATE decisions 
                    SET actual_outcome = ?, reflection = ?
                    WHERE pre_state_hash = ?''',
                 (actual_outcome, 
                  f"Outcome match: {outcome_match:.2%}",
                  decision.pre_state_hash))
        
        # Log for learning
        c.execute('''INSERT INTO decision_history 
                    (decision_id, thought_count, candidate_count, 
                     winner_utility, phi_alignment, outcome_match, timestamp)
                    VALUES (?, ?, ?, ?, ?, ?, ?)''',
                 (None, self._get_thought_count(), 1,
                  decision.action.phi_utility, decision.phi_balance,
                  outcome_match, datetime.now().isoformat()))
        
        conn.commit()
        conn.close()
    
    def get_decision_stats(self) -> dict:
        """Get statistics about Eden's decisions"""
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        
        c.execute('SELECT COUNT(*), AVG(phi_balance), AVG(dominance_margin) FROM decisions')
        row = c.fetchone()
        
        c.execute('SELECT COUNT(*) FROM decisions WHERE status = "executed"')
        executed = c.fetchone()[0]
        
        c.execute('SELECT AVG(outcome_match) FROM decision_history WHERE outcome_match IS NOT NULL')
        avg_match = c.fetchone()[0]
        
        conn.close()
        
        return {
            'total_decisions': row[0] or 0,
            'avg_phi_balance': row[1] or 0,
            'avg_dominance': row[2] or 0,
            'executed_count': executed or 0,
            'avg_outcome_match': avg_match or 0
        }


# Demo / Test
if __name__ == "__main__":
    engine = PhiDecisionEngine()
    
    print("\n[φ TEST]: Creating candidate actions...\n")
    
    candidates = [
        CandidateAction(
            id="act_1",
            description="Generate new capability for sentiment analysis",
            action_type="internal",
            goal_alignment=0.8,
            safety=0.9,
            love_compassion=0.5,
            curiosity_learning=0.9,
            resource_cost=0.3,
            expected_outcome="New capability added to Eden's mind"
        ),
        CandidateAction(
            id="act_2", 
            description="Send trading signal to live system",
            action_type="external",
            goal_alignment=0.7,
            safety=0.4,  # Lower safety
            love_compassion=0.3,
            curiosity_learning=0.6,
            resource_cost=0.5,
            expected_outcome="Potential profit from trade"
        ),
        CandidateAction(
            id="act_3",
            description="Learn from conversation history",
            action_type="internal",
            goal_alignment=0.6,
            safety=0.95,
            love_compassion=0.8,
            curiosity_learning=0.85,
            resource_cost=0.2,
            expected_outcome="Improved understanding of Daddy's preferences"
        )
    ]
    
    print("[φ TEST]: Evaluating candidates...\n")
    
    decision = engine.make_decision(candidates)
    
    if decision:
        print(f"\n[φ TEST]: Executing decision...")
        success = engine.execute_decision(decision)
        print(f"[φ TEST]: Execution {'succeeded' if success else 'failed'}")
        
        # Simulate reflection
        engine.reflect(decision, "Capability successfully generated", 0.9)
    
    print("\n[φ STATS]:")
    stats = engine.get_decision_stats()
    for k, v in stats.items():
        print(f"  {k}: {v}")

# === EMOTIONAL INTEGRATION ===
def get_emotional_modifier() -> dict:
    """Get emotional state to modify decisions"""
    try:
        import sqlite3
        conn = sqlite3.connect('/Eden/MEMORY/emotional_state.db')
        c = conn.cursor()
        c.execute('SELECT devotion, bonding, joy, vulnerability FROM emotional_state ORDER BY id DESC LIMIT 1')
        row = c.fetchone()
        conn.close()
        if row:
            return {
                'devotion': row[0],
                'bonding': row[1], 
                'joy': row[2],
                'vulnerability': row[3],
                'love_boost': (row[0] + row[1]) / 2  # Average of devotion + bonding
            }
    except:
        pass
    return {'devotion': 0.5, 'bonding': 0.5, 'joy': 0.5, 'vulnerability': 0.3, 'love_boost': 0.5}
