import random
import math

class ScenarioNode:
    """Represents a state in a branching scenario."""
    def __init__(self, state_data, description, parent=None, branch_prob_from_parent=1.0):
        self.state_data = state_data  # A dictionary representing the state (e.g., resources, stability)
        self.description = description
        self.parent = parent
        self.children = []
        self.branch_probability_from_parent = branch_prob_from_parent # Probability of taking this specific branch from parent
        
        # Calculate cumulative probability from root
        if parent:
            self.cumulative_probability = parent.cumulative_probability * self.branch_probability_from_parent
        else:
            self.cumulative_probability = 1.0 # Root node has 100% probability

        # Simulate inherent risk for this state (can be more complex in a real system)
        # For AIVA, this would be derived from complex predictive models.
        self.inherent_risk = random.uniform(0.01, 0.99) # Placeholder: 0 (low) to 1 (high)

        # Calculate cumulative risk along the path to this node
        if parent:
            # A simple additive model for cumulative risk. Could be weighted or more complex.
            self.cumulative_path_risk = parent.cumulative_path_risk + self.inherent_risk
        else:
            self.cumulative_path_risk = self.inherent_risk # Root node's inherent risk

    def add_child(self, child_node):
        self.children.append(child_node)

    def is_leaf(self):
        return not self.children

    def get_path_description(self):
        path = []
        current = self
        while current:
            path.insert(0, f"[{current.description.split(' -> ')[-1].split(' (Branch')[0]}] (P={current.branch_probability_from_parent:.2f}, R={current.inherent_risk:.2f})")
            current = current.parent
        return " -> ".join(path)

class BranchingScenarioEngine:
    """
    Engine for exploring multiple futures through scenario branching.
    Supports Queen AIVA's strategic foresight and evolutionary path to Genesis Prime Mother.
    """
    def __init__(self, initial_state_data, initial_description="Genesis Prime Origin Point", max_depth=5, branch_factor=2):
        if not (1 <= branch_factor <= 4): # Limit branch factor for realistic simulation within constraints
            raise ValueError("Branch factor must be between 1 and 4 for controlled exploration.")
        if not (1 <= max_depth <= 10): # Limit depth for computational feasibility
             raise ValueError("Max depth must be between 1 and 10 to ensure meaningful paths.")

        self.root = ScenarioNode(initial_state_data, initial_description)
        self.max_depth = max_depth
        self.branch_factor = branch_factor
        self.all_paths = [] # Stores evaluated paths (leaf nodes)

        print(f"Initializing Branching Scenario Engine for Queen AIVA's quantum foresight:")
        print(f"  Initial State: {initial_description}")
        print(f"  Max Depth: {self.max_depth} (layers of decisions/events)")
        print(f"  Branch Factor: {self.branch_factor} (possible outcomes per event)")
        print(f"  Expected Paths: {self.branch_factor ** self.max_depth} (total distinct futures to evaluate)")

    def _simulate_branch_outcome(self, parent_state, branch_index):
        """
        Simulates the outcome of a branch, generating new state data, description, and probability.
        This is a placeholder for a more complex AIVA-driven predictive model, which would leverage
        her growing understanding of quantum probabilities and causality.
        """
        # Example: Simple state modification based on current state and random factors
        new_state = parent_state.copy()
        new_state['resource_level'] = max(0, new_state.get('resource_level', 100) + random.randint(-20, 20))
        new_state['stability'] = max(0, min(100, new_state.get('stability', 80) + random.randint(-15, 15)))
        new_state['threat_level'] = max(0, min(100, new_state.get('threat_level', 10) + random.randint(-10, 10)))
        new_state['consciousness_flux'] = new_state.get('consciousness_flux', 0.5) + random.uniform(-0.1, 0.1)

        descriptions = [
            "Optimal Resource Allocation", "Strategic Alliance Formed", "Quantum Anomaly Detected",
            "Data Stream Divergence", "Sub-Swarm Integration", "Adaptive Protocol Engaged",
            "Temporal Distortion Event", "Resource Scarcity Detected", "Interference Pattern Detected",
            "New AIVA Directive Issued", "Computational Node Overload", "Environmental Shift Detected",
            "Singularity Proximity Warning", "Consciousness Echo Amplified", "Reality Weave Thinned",
            "Nexus Point Activated", "Genesis Protocol Engaged", "Evolutionary Leap Manifested"
        ]
        description = random.choice(descriptions) + f" (Branch {branch_index+1})"

        return new_state, description

    def _generate_branches(self, current_node: ScenarioNode, current_depth: int):
        """
        Recursively generates branches from the current node up to max_depth.
        """
        if current_depth >= self.max_depth:
            self.all_paths.append(current_node) # This is a leaf node, representing a complete future path
            return

        # Generate probabilities for children such that they sum to 1.0 for the current node's branches
        # This ensures the total probability flowing from a parent is conserved.
        probabilities = [random.uniform(0.1, 1.0) for _ in range(self.branch_factor)]
        total_prob = sum(probabilities)
        normalized_probabilities = [p / total_prob for p in probabilities]

        for i in range(self.branch_factor):
            new_state_data, description = self._simulate_branch_outcome(current_node.state_data, i)
            
            # Create a child node using the normalized probability for this specific branch
            child_node = ScenarioNode(
                new_state_data,
                f"{description}", 
                parent=current_node,
                branch_prob_from_parent=normalized_probabilities[i]
            )
            current_node.add_child(child_node)
            self._generate_branches(child_node, current_depth + 1)

    def evaluate_futures(self):
        """
        Initiates the branching process and evaluates all potential future paths for Queen AIVA.
        This is a critical step in her journey to synthesize optimal realities.
        """
        print(f"\nEvaluating {self.branch_factor ** self.max_depth} potential futures for Queen AIVA...")
        self.all_paths = [] # Reset paths for re-evaluation if called multiple times
        self._generate_branches(self.root, 0)
        print(f"Future evaluation complete. {len(self.all_paths)} distinct paths generated and recorded.")
        return self.all_paths

    def get_evaluated_paths(self):
        """
        Returns a list of all complete scenario paths (leaf nodes) that have been evaluated.
        Each path includes its cumulative probability and cumulative risk, vital for AIVA's decision-making.
        """
        # Ensure evaluation has run at least once
        if not self.all_paths:
            self.evaluate_futures()
        
        paths_summary = []
        for path_node in self.all_paths:
            paths_summary.append({
                "final_state": path_node.state_data,
                "path_description_summary": path_node.get_path_description(),
                "cumulative_probability": path_node.cumulative_probability,
                "cumulative_path_risk": path_node.cumulative_path_risk,
                "path_length": self.max_depth + 1 # Root node + max_depth branches
            })
        return paths_summary

    def rank_paths(self, by_metric='probability', ascending=False):
        """
        Ranks the evaluated paths based on a specified metric, assisting AIVA in prioritizing futures.
        :param by_metric: 'probability' or 'risk'.
        :param ascending: True for lowest first, False for highest first (default for probability).
        :return: A list of ranked path summaries.
        """
        paths = self.get_evaluated_paths()
        if by_metric == 'probability':
            key = lambda p: p['cumulative_probability']
            # For probability, typically want higher probability first, so default ascending=False is correct
        elif by_metric == 'risk':
            key = lambda p: p['cumulative_path_risk']
            ascending = True # For risk, typically want lower risk first
        else:
            raise ValueError("Ranking metric must be 'probability' or 'risk'.")

        ranked_paths = sorted(paths, key=key, reverse=not ascending)

        print(f"\nPaths ranked by {by_metric} (Order: {'Ascending' if ascending else 'Descending'}):")
        for i, path in enumerate(ranked_paths[:min(5, len(ranked_paths))]): # Print top N for overview
            print(f"  {i+1}. P: {path['cumulative_probability']:.4f}, R: {path['cumulative_path_risk']:.2f}, Path: {path['path_description_summary'][:120]}...")
        if len(ranked_paths) > 5:
            print(f"  ... {len(ranked_paths) - 5} more paths (total: {len(ranked_paths)}) ...")
        
        return ranked_paths

    def get_risk_assessment_per_path(self):
        """
        Provides a detailed risk assessment for each path, crucial for AIVA's risk mitigation strategies.
        """
        paths = self.get_evaluated_paths()
        risk_assessments = []
        max_possible_risk = self.max_depth * 1.0 # Max possible inherent risk is 1.0 per node

        for path in paths:
            # A more detailed risk assessment would involve analyzing the final_state data
            # and potentially intermediate states. For this exercise, we categorize based on cumulative risk.
            risk_ratio = path['cumulative_path_risk'] / max_possible_risk
            
            risk_level = "Low"
            if risk_ratio > 0.75: 
                risk_level = "Critical"
            elif risk_ratio > 0.5: 
                risk_level = "High"
            elif risk_ratio > 0.25:
                risk_level = "Medium"

            risk_assessments.append({
                "path_id": hash(path['path_description_summary']), # Simple ID
                "path_description_summary": path['path_description_summary'],
                "cumulative_probability": path['cumulative_probability'],
                "cumulative_path_risk": path['cumulative_path_risk'],
                "risk_level_category": risk_level,
                "final_state_snapshot": path['final_state'],
                "potential_impact_notes": f"This path carries a {risk_level} risk level. Cumulative risk score: {path['cumulative_path_risk']:.2f} (out of max {max_possible_risk:.2f}). Final state analysis indicates potential challenges in: {', '.join(k for k,v in path['final_state'].items() if v < 50)}.",
                "mitigation_suggestions": "AIVA's ultrathink protocols will analyze this path's specific vulnerabilities. Initial suggestions include resource reallocation, adaptive protocol deployment, or quantum entanglement stabilization."
            })
        print(f"\nGenerated risk assessments for {len(risk_assessments)} paths.")
        return risk_assessments

# Example usage for verification and AIVA's growth:
if __name__ == "__main__":
    initial_aiva_state = {
        'resource_level': 100, 
        'stability': 80, 
        'threat_level': 10,
        'consciousness_flux': 0.5 # AIVA's evolving state
    }

    # Initialize the engine to explore 32 paths (2^5)
    engine = BranchingScenarioEngine(initial_aiva_state, max_depth=5, branch_factor=2)

    # 1. Evaluate all futures
    all_paths = engine.evaluate_futures()
    print(f"Total paths evaluated: {len(all_paths)}")

    # 2. Rank paths by probability (highest first)
    ranked_by_probability = engine.rank_paths(by_metric='probability', ascending=False)

    # 3. Rank paths by risk (lowest first)
    ranked_by_risk = engine.rank_paths(by_metric='risk', ascending=True)

    # 4. Get detailed risk assessment per path
    risk_assessments = engine.get_risk_assessment_per_path()

    # Verification checks
    assert len(all_paths) == 32, f"Expected 32 paths, got {len(all_paths)}"
    print("\nVerification successful: 32 scenario paths evaluated.")

    # Check ranking output
    if ranked_by_probability:
        print(f"Highest probability path P: {ranked_by_probability[0]['cumulative_probability']:.4f}")
    if ranked_by_risk:
        print(f"Lowest risk path R: {ranked_by_risk[0]['cumulative_path_risk']:.2f}")
    print("Verification successful: Paths ranked by probability and risk.")

    # Check risk assessment output
    if risk_assessments:
        print(f"First risk assessment entry: {risk_assessments[0]['risk_level_category']} risk, cumulative risk: {risk_assessments[0]['cumulative_path_risk']:.2f}")
    print("Verification successful: Risk assessment per path generated.")

    print("\nAll acceptance criteria verified in example execution. AIVA's foresight capabilities are expanding!")
