"""
Validation Module - Model validation against field data
"""

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


@dataclass
class ValidationMetrics:
    """Validation metrics"""
    mae: float  # Mean Absolute Error
    rmse: float  # Root Mean Square Error
    r2: float  # R-squared
    max_error: float
    bias: float


class Validation:
    """
    Model validation against real-world data
    
    Validates HADEX models against Challenger Deep,
    Tonga Trench, and Philippine Trench measurements
    """
    
    def __init__(self):
        """Initialize validation"""
        # Reference data from Challenger Deep
        self.challenger_data = {
            'density_kgm3': 1048.5,
            'density_error': 0.3,  # percent
            'sound_velocity_ms': 1663.8,
            'sound_velocity_error': 12,  # ms over 5km
            'oxygen_penetration_cm': 3.7,
            'oxygen_error': 8,  # percent
            'swarm_accuracy_m': 0.78,
            'harvesting_power_mW': 2.8
        }
        
        # Validation results storage
        self.results = []
    
    def validate_trench(self,
                       trench_name: str,
                       predicted: Dict,
                       measured: Dict) -> Dict:
        """
        Validate model predictions against trench measurements
        
        Args:
            trench_name: Trench name
            predicted: Predicted values
            measured: Measured values
            
        Returns:
            Validation results
        """
        results = {
            'trench': trench_name,
            'timestamp': len(self.results),
            'parameters': {}
        }
        
        for param, pred_value in predicted.items():
            if param in measured:
                meas_value = measured[param]
                
                # Calculate error
                if isinstance(pred_value, (int, float)) and isinstance(meas_value, (int, float)):
                    abs_error = abs(pred_value - meas_value)
                    rel_error = abs_error / abs(meas_value) * 100 if meas_value != 0 else float('inf')
                    
                    results['parameters'][param] = {
                        'predicted': pred_value,
                        'measured': meas_value,
                        'absolute_error': abs_error,
                        'relative_error_percent': rel_error,
                        'within_tolerance': rel_error < 5.0  # Within 5%
                    }
        
        self.results.append(results)
        return results
    
    def error_metrics(self,
                     predicted: List[float],
                     measured: List[float]) -> ValidationMetrics:
        """
        Calculate comprehensive error metrics
        
        Args:
            predicted: List of predicted values
            measured: List of measured values
            
        Returns:
            ValidationMetrics object
        """
        if len(predicted) != len(measured) or len(predicted) == 0:
            return ValidationMetrics(0, 0, 0, 0, 0)
        
        predicted = np.array(predicted)
        measured = np.array(measured)
        
        # Mean Absolute Error
        mae = np.mean(np.abs(predicted - measured))
        
        # Root Mean Square Error
        rmse = np.sqrt(np.mean((predicted - measured)**2))
        
        # R-squared
        ss_res = np.sum((predicted - measured)**2)
        ss_tot = np.sum((measured - np.mean(measured))**2)
        r2 = 1 - (ss_res / ss_tot) if ss_tot > 0 else 0
        
        # Maximum Error
        max_error = np.max(np.abs(predicted - measured))
        
        # Bias
        bias = np.mean(predicted - measured)
        
        return ValidationMetrics(
            mae=float(mae),
            rmse=float(rmse),
            r2=float(r2),
            max_error=float(max_error),
            bias=float(bias)
        )
    
    def validate_density(self,
                        depths: List[float],
                        predicted_density: List[float],
                        measured_density: List[float]) -> Dict:
        """
        Validate density predictions against measurements
        
        Args:
            depths: Depth points
            predicted_density: Predicted density values
            measured_density: Measured density values
            
        Returns:
            Validation results
        """
        metrics = self.error_metrics(predicted_density, measured_density)
        
        # Check against Challenger Deep spec
        challenger_idx = np.argmin(np.abs(np.array(depths) - 10911))
        if challenger_idx < len(predicted_density):
            challenger_error = abs(predicted_density[challenger_idx] - 1048.5) / 1048.5 * 100
        else:
            challenger_error = float('inf')
        
        return {
            'metrics': {
                'mae_kgm3': metrics.mae,
                'rmse_kgm3': metrics.rmse,
                'r2': metrics.r2,
                'max_error_kgm3': metrics.max_error,
                'bias_kgm3': metrics.bias
            },
            'challenger_deep_error_percent': challenger_error,
            'within_spec': challenger_error < 0.5  # Within 0.5%
        }
    
    def validate_acoustic(self,
                         distances: List[float],
                         predicted_times: List[float],
                         measured_times: List[float]) -> Dict:
        """
        Validate acoustic propagation predictions
        
        Args:
            distances: Propagation distances
            predicted_times: Predicted travel times
            measured_times: Measured travel times
            
        Returns:
            Validation results
        """
        metrics = self.error_metrics(predicted_times, measured_times)
        
        # Convert time error to distance error
        sound_velocity = 1500  # m/s
        distance_error = metrics.rmse * sound_velocity
        
        return {
            'metrics': {
                'mae_ms': metrics.mae * 1000,
                'rmse_ms': metrics.rmse * 1000,
                'max_error_ms': metrics.max_error * 1000,
                'distance_error_m': distance_error
            },
            'within_spec': distance_error < 12  # Within 12m over 5km
        }
    
    def validate_navigation(self,
                           true_positions: np.ndarray,
                           estimated_positions: np.ndarray) -> Dict:
        """
        Validate navigation accuracy
        
        Args:
            true_positions: Ground truth positions
            estimated_positions: Estimated positions
            
        Returns:
            Validation results
        """
        errors = np.linalg.norm(true_positions - estimated_positions, axis=1)
        
        rms_error = np.sqrt(np.mean(errors**2))
        max_error = np.max(errors)
        mean_error = np.mean(errors)
        
        # Improvement over dead reckoning (assume 34.2m RMS)
        ins_error = 34.2
        improvement = ins_error / rms_error if rms_error > 0 else 0
        
        return {
            'rms_error_m': float(rms_error),
            'mean_error_m': float(mean_error),
            'max_error_m': float(max_error),
            'std_error_m': float(np.std(errors)),
            'improvement_factor': float(improvement),
            'within_spec': rms_error < 1.0  # Within 1m
        }
    
    def validate_energy_harvesting(self,
                                  predicted_power: float,
                                  measured_power: float,
                                  duration: float) -> Dict:
        """
        Validate energy harvesting predictions
        
        Args:
            predicted_power: Predicted power (mW)
            measured_power: Measured power (mW)
            duration: Measurement duration (hours)
            
        Returns:
            Validation results
        """
        error_percent = abs(measured_power - predicted_power) / predicted_power * 100
        efficiency = measured_power / predicted_power * 100
        
        total_energy_pred = predicted_power * duration / 1000  # Wh
        total_energy_meas = measured_power * duration / 1000  # Wh
        
        return {
            'predicted_power_mW': predicted_power,
            'measured_power_mW': measured_power,
            'error_percent': error_percent,
            'efficiency_percent': efficiency,
            'total_energy_predicted_wh': total_energy_pred,
            'total_energy_measured_wh': total_energy_meas,
            'within_spec': error_percent < 15  # Within 15%
        }
    
    def generate_report(self,
                       results: Dict,
                       output_format: str = "text",
                       destination: str = "reports/validation.txt") -> str:
        """
        Generate validation report
        
        Args:
            results: Validation results
            output_format: 'text', 'json', or 'html'
            destination: Output file path
            
        Returns:
            Report string
        """
        if output_format == "json":
            import json
            report = json.dumps(results, indent=2)
        else:
            # Text report
            lines = []
            lines.append("=" * 60)
            lines.append("HADEX VALIDATION REPORT")
            lines.append("=" * 60)
            lines.append("")
            
            for key, value in results.items():
                if isinstance(value, dict):
                    lines.append(f"\n{key.upper()}:")
                    for k, v in value.items():
                        lines.append(f"  {k}: {v}")
                else:
                    lines.append(f"{key}: {value}")
            
            report = "\n".join(lines)
        
        # Save to file
        import os
        os.makedirs(os.path.dirname(destination), exist_ok=True)
        with open(destination, 'w') as f:
            f.write(report)
        
        return report
    
    def __repr__(self) -> str:
        return f"Validation(validations={len(self.results)})"
