import numpy as np
import hashlib
from typing import List, Dict, Callable

class CCT_Fluid_Algorithm:
    """
    The Fluid Algorithm:
    Adapts its own structure (SuperBoolean) to find the Underlying Mechanism
    with Probability -> 1 via Entropy Collapse.
    """
    def __init__(self, 
                 max_active_paths: int = 50, 
                 collapse_threshold: float = 1e-6,
                 fluidity_rate: float = 0.01):
        
        # 16-Element Semantic State (The "Fluid" Core)
        self.elements = np.ones(16) * 0.5  # Start in superposition
        self.MAX_ACTIVE = max_active_paths
        self.THRESHOLD = collapse_threshold
        self.ALPHA = fluidity_rate
        
        # Memory Bank (Infinite Data Handling)
        self.memory_bank = {}  # {id: {'weight': float, 'data': any}}
        self.active_set = set()
        
        # Metrics
        self.entropy_history = []
        self.work_invested = 0
        
    def calculate_entropy(self) -> float:
        """Calculate Semantic Entropy of the current Algorithm State"""
        p = np.clip(self.elements, 1e-10, 1.0)
        return -np.sum(p * np.log2(p))
    
    def inject_data(self, data_id: str, data_content: any, modality: str = 'text'):
        """Add data to the infinite memory bank"""
        self.memory_bank[data_id] = {
            'content': data_content,
            'modality': modality,
            'weight': 0.5,  # Initial neutral probability
            'age': 0
        }
        self.active_set.add(data_id)
        
    def fluid_update(self, prompt: str) -> bool:
        """
        The Core Fluid Step:
        1. Evaluate all active paths
        2. Update Algorithm State (ODE)
        3. Prune low-probability paths (Memory)
        4. Check for Mechanism Collapse
        """
        # 1. Calculate Current Entropy
        H_current = self.calculate_entropy()
        self.entropy_history.append(H_current)
        
        # 2. Evaluate Collapse Potential of Active Data
        paths_to_prune = []
        total_gradient = np.zeros(16)
        
        for data_id in list(self.active_set):
            mem = self.memory_bank[data_id]
            
            # Simulate "AI Think" (Semantic Gradient)
            # In real implementation: Embedding model
            gradient = self._compute_semantic_gradient(prompt, mem['content'])
            
            # Calculate Potential Entropy Reduction
            delta_H = np.dot(gradient, (1.0 - self.elements))
            
            # 3. Update Path Weight (ODE Dynamics)
            # dw = alpha * delta_H - decay * w
            mem['weight'] += self.ALPHA * delta_H - 0.005 * mem['weight']
            mem['age'] += 1
            
            # Accumulate Gradient for Algorithm Update
            total_gradient += mem['weight'] * gradient
            
            # 4. Pruning Condition (Maintain Fluidity)
            if mem['weight'] < 0.05 or mem['age'] > 100:
                paths_to_prune.append(data_id)
        
        # 5. Execute Pruning (Forget Noise)
        for pid in paths_to_prune:
            self.active_set.remove(pid)
            # Keep in bank but inactive (Archive)
            
        # 6. Enforce Max Active (Hard Constraint for Infinity)
        if len(self.active_set) > self.MAX_ACTIVE:
            # Keep top weighted paths
            sorted_paths = sorted(self.active_set, 
                                  key=lambda x: self.memory_bank[x]['weight'], 
                                  reverse=True)
            self.active_set = set(sorted_paths[:self.MAX_ACTIVE])
            
        # 7. Update Algorithm State (Flow toward Mechanism)
        self.elements += self.ALPHA * total_gradient
        self.elements = np.clip(self.elements, 0.0, 1.0)
        
        # 8. Normalize (Probability Sum = 1)
        self.elements /= np.sum(self.elements)
        
        # 9. Check Collapse (Mechanism Found?)
        H_new = self.calculate_entropy()
        self.work_invested += len(self.active_set)
        
        return H_new < self.THRESHOLD
    
    def _compute_semantic_gradient(self, prompt: str, content: any) -> np.ndarray:
        """Simulates the semantic pull of data on the 16 elements"""
        # Deterministic simulation for demo
        combined = prompt + str(content)
        hash_val = int(hashlib.md5(combined.encode()).hexdigest(), 16)
        np.random.seed(hash_val % (2**32))
        gradient = np.random.uniform(0.1, 1.0, 16)
        
        # Boost E16 (Stability) if content looks like a "Law"
        if 'law' in str(content).lower() or 'equation' in str(content).lower():
            gradient[15] *= 2.0 
        return gradient
    
    def get_mechanism_probability(self) -> float:
        """Returns the probability that the Underlying Mechanism is found"""
        # E16 is Proof_Stability
        return self.elements[15]

# --- Usage Example ---
fluid_ai = CCT_Fluid_Algorithm(max_active_paths=20)

# Simulate "Infinite" Data Stream (1000 files)
for i in range(1000):
    # 95% Noise, 5% Signal (Underlying Mechanism)
    content = "Noise data..." if i % 20 != 0 else "The underlying mechanism is F=ma..."
    fluid_ai.inject_data(f"file_{i}", content)
    
    # Run Fluid Step
    if i % 10 == 0:
        collapsed = fluid_ai.fluid_update(prompt="Find the physics law")
        prob = fluid_ai.get_mechanism_probability()
        print(f"Iter {i}: Active={len(fluid_ai.active_set)} | P(Mechanism)={prob:.4f} | Collapsed={collapsed}")

print(f"\nFinal Probability of Mechanism: {fluid_ai.get_mechanism_probability():.4f}")
print(f"Total Work Invested: {fluid_ai.work_invested} units")
print(f"Entropy Trajectory: {fluid_ai.entropy_history[0]:.4f} -> {fluid_ai.entropy_history[-1]:.4f}")