"""
Breakdown risk assessment for cave passages.
Predicts roof collapse potential and analyzes structural stability.

Author: Samir Baladi
"""

import numpy as np
from typing import Dict, List, Tuple, Optional, Union
from dataclasses import dataclass
import logging

logger = logging.getLogger(__name__)

@dataclass
class BreakdownEvent:
    """Represents a breakdown (collapse) event."""
    location: str
    timestamp: float
    volume: float  # Collapse volume (m³)
    chamber_diameter: float  # Resulting chamber diameter (m)
    trigger: str  # Trigger mechanism
    casualties: bool  # Whether casualties occurred


@dataclass
class MonitoringPoint:
    """Represents a structural monitoring point."""
    location: str
    span: float  # Roof span (m)
    fracture_density: float  # Fractures per meter
    water_seepage: bool  # Whether water is seeping
    last_inspection: float  # Last inspection time
    crack_width: float  # Measured crack width (mm)


class BreakdownRisk:
    """
    Assesses breakdown (collapse) risk in cave passages.
    """
    
    def __init__(self,
                 rock_strength: float = 50,  # MPa
                 safety_factor_min: float = 1.5):
        """
        Initialize breakdown risk assessment.
        
        Args:
            rock_strength: Unconfined compressive strength (MPa)
            safety_factor_min: Minimum acceptable safety factor
        """
        self.rock_strength = rock_strength
        self.safety_factor_min = safety_factor_min
        self.events = []
        self.monitoring_points = []
        logger.info(f"Initialized BreakdownRisk with strength={rock_strength}MPa")

    def add_event(self,
                 location: str,
                 timestamp: float,
                 volume: float,
                 chamber_diameter: float,
                 trigger: str,
                 casualties: bool = False) -> None:
        """
        Add a breakdown event to history.
        
        Args:
            location: Event location
            timestamp: Event time
            volume: Collapse volume
            chamber_diameter: Resulting chamber size
            trigger: Trigger mechanism
            casualties: Whether casualties occurred
        """
        event = BreakdownEvent(
            location=location,
            timestamp=timestamp,
            volume=volume,
            chamber_diameter=chamber_diameter,
            trigger=trigger,
            casualties=casualties
        )
        
        self.events.append(event)
        logger.warning(f"Added breakdown event at {location}: {volume}m³ collapse")

    def add_monitoring_point(self,
                            location: str,
                            span: float,
                            fracture_density: float,
                            water_seepage: bool,
                            last_inspection: float,
                            crack_width: float = 0) -> None:
        """
        Add a structural monitoring point.
        
        Args:
            location: Point location
            span: Roof span (m)
            fracture_density: Fractures per meter
            water_seepage: Whether water is seeping
            last_inspection: Last inspection time
            crack_width: Measured crack width (mm)
        """
        point = MonitoringPoint(
            location=location,
            span=span,
            fracture_density=fracture_density,
            water_seepage=water_seepage,
            last_inspection=last_inspection,
            crack_width=crack_width
        )
        
        self.monitoring_points.append(point)
        logger.info(f"Added monitoring point at {location}: span={span}m")

    def calculate_safety_factor(self,
                               span: float,
                               rock_quality: float = 1.0,
                               fracture_factor: float = 1.0) -> float:
        """
        Calculate roof safety factor using beam theory.
        
        Args:
            span: Roof span (m)
            rock_quality: Rock quality factor (0-1)
            fracture_factor: Fracture reduction factor (0-1)
            
        Returns:
            Safety factor
        """
        # Simplified beam theory for roof stability
        # Maximum stress = (q * L²) / (2 * t²) for cantilever
        # Where q is load, L is span, t is thickness
        
        # Assume roof thickness = 1m for simplicity
        thickness = 1.0
        
        # Load (rock weight)
        rock_density = 2700  # kg/m³
        g = 9.81  # m/s²
        load = rock_density * g * thickness  # Pa per m width
        
        # Maximum stress
        max_stress = (load * span**2) / (2 * thickness**2)  # Pa
        
        # Convert to MPa
        max_stress_mpa = max_stress / 1e6
        
        # Apply quality factors
        effective_strength = self.rock_strength * rock_quality * fracture_factor
        
        # Safety factor
        if max_stress_mpa > 0:
            safety_factor = effective_strength / max_stress_mpa
        else:
            safety_factor = float('inf')
        
        return safety_factor

    def assess_roof_stability(self,
                             span: float,
                             fracture_density: float,
                             water_seepage: bool = False) -> Dict[str, Union[str, float]]:
        """
        Assess roof stability for a given span.
        
        Args:
            span: Roof span (m)
            fracture_density: Fractures per meter
            water_seepage: Whether water is seeping
            
        Returns:
            Dictionary with stability assessment
        """
        # Calculate rock quality factor from fracture density
        # More fractures = lower quality
        rock_quality = max(0.3, 1.0 - fracture_density / 10)
        
        # Fracture factor
        fracture_factor = max(0.2, 1.0 - fracture_density / 5)
        
        # Water seepage reduces stability
        if water_seepage:
            fracture_factor *= 0.8
        
        # Calculate safety factor
        safety_factor = self.calculate_safety_factor(span, rock_quality, fracture_factor)
        
        # Determine stability level
        if safety_factor >= 3.0:
            level = "STABLE"
            color = "🟢"
            action = "No action needed"
            risk = "LOW"
        elif safety_factor >= 2.0:
            level = "CAUTION"
            color = "🟡"
            action = "Monitor periodically"
            risk = "MODERATE"
        elif safety_factor >= 1.5:
            level = "UNSTABLE"
            color = "🟠"
            action = "Avoid area, monitor closely"
            risk = "HIGH"
        elif safety_factor >= 1.0:
            level = "HIGH RISK"
            color = "🔴"
            action = "Evacuate area"
            risk = "VERY HIGH"
        else:
            level = "CRITICAL"
            color = "⚫"
            action = "IMMEDIATE EVACUATION"
            risk = "IMMINENT COLLAPSE"
        
        # Estimate time to failure (simplified)
        if safety_factor < 1.5:
            # Exponential decay model
            time_factor = np.exp(5 * (safety_factor - 1.5))
            time_to_failure = 24 * time_factor  # hours
        else:
            time_to_failure = float('inf')
        
        return {
            'level': level,
            'color': color,
            'action': action,
            'risk': risk,
            'safety_factor': round(safety_factor, 2),
            'span': span,
            'fracture_density': fracture_density,
            'rock_quality': round(rock_quality, 2),
            'time_to_failure_hours': round(time_to_failure, 1) if time_to_failure != float('inf') else '>24h',
            'water_seepage': water_seepage
        }

    def predict_collapse_volume(self,
                               span: float,
                               height: float,
                               fracture_density: float) -> Dict[str, float]:
        """
        Predict potential collapse volume.
        
        Args:
            span: Roof span (m)
            height: Passage height (m)
            fracture_density: Fractures per meter
            
        Returns:
            Dictionary with collapse predictions
        """
        # Collapse volume estimation
        # Assume collapse shape is roughly hemispherical
        
        # Maximum stable span
        max_span = 5.0 / (1 + fracture_density)  # Simplified
        
        if span <= max_span:
            # No collapse expected
            volume = 0
            probability = 0
        else:
            # Excess span leads to collapse
            excess = span - max_span
            collapse_radius = min(height, excess * 2)
            volume = (2/3) * np.pi * collapse_radius**3  # Hemisphere volume
            
            # Probability increases with excess
            probability = min(0.95, excess / 5)
        
        # Chamber diameter after collapse
        chamber_diameter = max(span, collapse_radius * 2)
        
        return {
            'predicted_volume': round(volume, 1),
            'collapse_probability': round(probability, 2),
            'chamber_diameter': round(chamber_diameter, 1),
            'max_stable_span': round(max_span, 1),
            'excess_span': round(max(0, span - max_span), 1)
        }

    def monitor_crack_propagation(self,
                                 initial_width: float,
                                 current_width: float,
                                 time_elapsed: float) -> Dict[str, Union[str, float]]:
        """
        Monitor crack propagation and predict failure.
        
        Args:
            initial_width: Initial crack width (mm)
            current_width: Current crack width (mm)
            time_elapsed: Time elapsed (days)
            
        Returns:
            Dictionary with crack analysis
        """
        # Calculate rate
        rate = (current_width - initial_width) / time_elapsed if time_elapsed > 0 else 0
        
        # Critical width (empirical)
        critical_width = 50  # mm
        
        if current_width >= critical_width:
            status = "CRITICAL"
            action = "IMMEDIATE EVACUATION"
            time_to_critical = 0
        elif rate > 0:
            time_to_critical = (critical_width - current_width) / rate if rate > 0 else float('inf')
            
            if rate > 1:  # mm/day
                status = "RAPID PROPAGATION"
                action = "EVACUATE AREA"
            elif rate > 0.1:
                status = "ACTIVE PROPAGATION"
                action = "MONITOR CLOSELY"
            else:
                status = "SLOW PROPAGATION"
                action = "CONTINUE MONITORING"
        else:
            status = "STABLE"
            action = "NO ACTION"
            time_to_critical = float('inf')
        
        return {
            'status': status,
            'action': action,
            'initial_width': initial_width,
            'current_width': current_width,
            'propagation_rate': round(rate, 2),
            'time_to_critical_days': round(time_to_critical, 1) if time_to_critical != float('inf') else '>30d',
            'percent_of_critical': round(current_width / critical_width * 100, 1)
        }

    def analyze_breakdown_triggers(self) -> Dict[str, Union[str, float]]:
        """
        Analyze common breakdown triggers from historical events.
        
        Returns:
            Dictionary with trigger analysis
        """
        if not self.events:
            return {'status': 'No historical events'}
        
        triggers = {}
        for event in self.events:
            triggers[event.trigger] = triggers.get(event.trigger, 0) + 1
        
        total = len(self.events)
        trigger_analysis = {}
        for trigger, count in triggers.items():
            trigger_analysis[trigger] = {
                'count': count,
                'percentage': round(count / total * 100, 1)
            }
        
        # Most common trigger
        most_common = max(triggers.items(), key=lambda x: x[1])
        
        # Calculate risk by trigger
        risk_by_trigger = {}
        for trigger, count in triggers.items():
            events_with_casualties = sum(1 for e in self.events 
                                       if e.trigger == trigger and e.casualties)
            risk_by_trigger[trigger] = round(events_with_casualties / count * 100, 1)
        
        return {
            'total_events': total,
            'triggers': trigger_analysis,
            'most_common_trigger': most_common[0],
            'most_common_percentage': round(most_common[1] / total * 100, 1),
            'risk_by_trigger': risk_by_trigger
        }

    def get_monitoring_recommendations(self,
                                      current_safety_factor: float) -> Dict[str, Union[str, float]]:
        """
        Get monitoring recommendations based on safety factor.
        
        Args:
            current_safety_factor: Current calculated safety factor
            
        Returns:
            Dictionary with monitoring recommendations
        """
        if current_safety_factor >= 3.0:
            frequency = "Monthly"
            instruments = "Visual inspection only"
            priority = "LOW"
        elif current_safety_factor >= 2.0:
            frequency = "Weekly"
            instruments = "Crack monitoring, tape extensometer"
            priority = "MEDIUM"
        elif current_safety_factor >= 1.5:
            frequency = "Daily"
            instruments = "Automated crack gauges, tilt meters"
            priority = "HIGH"
        elif current_safety_factor >= 1.0:
            frequency = "Continuous"
            instruments = "Real-time monitoring, laser scanning"
            priority = "URGENT"
        else:
            frequency = "Immediate evacuation"
            instruments = "N/A - Evacuate"
            priority = "CRITICAL"
        
        return {
            'monitoring_frequency': frequency,
            'recommended_instruments': instruments,
            'priority': priority,
            'safety_factor': current_safety_factor
        }

    def summary(self) -> Dict[str, Union[float, str, List]]:
        """Get summary of breakdown risk analysis."""
        summary = {
            'rock_strength_mpa': self.rock_strength,
            'min_safety_factor': self.safety_factor_min,
            'monitoring_points': len(self.monitoring_points),
            'historical_events': len(self.events)
        }
        
        if self.events:
            summary['trigger_analysis'] = self.analyze_breakdown_triggers()
        
        # Current highest risk point
        if self.monitoring_points:
            highest_risk = None
            lowest_sf = float('inf')
            
            for point in self.monitoring_points:
                sf = self.calculate_safety_factor(
                    point.span,
                    1.0 - point.fracture_density/10,
                    1.0 - point.fracture_density/5
                )
                if sf < lowest_sf:
                    lowest_sf = sf
                    highest_risk = point.location
            
            summary['highest_risk_location'] = highest_risk
            summary['lowest_safety_factor'] = round(lowest_sf, 2)
        
        return summary


# Example usage
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    
    print("="*60)
    print("CAVORA - Breakdown Risk Assessment")
    print("="*60)
    
    br = BreakdownRisk()
    
    # Add monitoring points
    br.add_monitoring_point("Main Chamber", span=8.5, fracture_density=2.3, 
                           water_seepage=True, last_inspection=2024.5)
    br.add_monitoring_point("Side Passage", span=4.2, fracture_density=1.1, 
                           water_seepage=False, last_inspection=2024.5)
    
    # Assess roof stability
    stability = br.assess_roof_stability(span=8.5, fracture_density=2.3, water_seepage=True)
    print(f"\nRoof Stability Assessment:")
    print(f"  - {stability['color']} {stability['level']}")
    print(f"  - Safety Factor: {stability['safety_factor']}")
    print(f"  - Action: {stability['action']}")
    
    # Predict collapse volume
    collapse = br.predict_collapse_volume(span=8.5, height=15.0, fracture_density=2.3)
    print(f"\nCollapse Prediction:")
    print(f"  - Probability: {collapse['collapse_probability']:.0%}")
    print(f"  - Predicted Volume: {collapse['predicted_volume']} m³")
    print(f"  - Chamber Diameter: {collapse['chamber_diameter']} m")
    
    # Monitor crack propagation
    crack = br.monitor_crack_propagation(initial_width=5, current_width=12, time_elapsed=30)
    print(f"\nCrack Monitoring:")
    print(f"  - Status: {crack['status']}")
    print(f"  - Action: {crack['action']}")
    print(f"  - Propagation Rate: {crack['propagation_rate']} mm/day")
    
    print("\n" + "="*60)
