"""
Simulation Validator - Validates design parameters before running FEA simulations.

This module helps prevent simulation failures by:
1. Checking if geometry will be valid
2. Validating parameter combinations
3. Providing actionable error messages
4. Detecting likely failure modes

Usage:
    validator = SimulationValidator(model_type='circular_plate')
    is_valid, warnings = validator.validate(design_variables)
    if not is_valid:
        print(f"Invalid parameters: {warnings}")
"""

from typing import Dict, Tuple, List
from pathlib import Path


class SimulationValidator:
    """Validates design parameters before running simulations."""

    def __init__(self, model_type: str = 'generic', verbose: bool = True):
        """
        Initialize validator for specific model type.

        Args:
            model_type: Type of FEA model ('circular_plate', 'beam', etc.)
            verbose: Print validation warnings
        """
        self.model_type = model_type
        self.verbose = verbose

        # Model-specific validation rules
        self.validation_rules = self._get_validation_rules(model_type)

    def _get_validation_rules(self, model_type: str) -> Dict:
        """Get validation rules for specific model type."""

        if model_type == 'circular_plate':
            # NOTE: Only use parameter bounds for validation
            # No arbitrary aspect ratio checks - let Optuna explore the full parameter space
            # Modal analysis is robust and doesn't need strict aspect ratio limits
            return {}

        # Generic rules for unknown models
        return {}

    def validate(
        self,
        design_variables: Dict[str, float],
        strict: bool = False
    ) -> Tuple[bool, List[str]]:
        """
        Validate design variables before simulation.

        Args:
            design_variables: Dict of parameter names to values
            strict: If True, reject on soft limit violations (warnings)

        Returns:
            (is_valid, warnings_list)
            - is_valid: True if parameters are acceptable
            - warnings_list: List of warning/error messages
        """
        warnings = []
        is_valid = True

        # Check each parameter
        for param_name, value in design_variables.items():
            if param_name not in self.validation_rules:
                continue  # No rules for this parameter

            rules = self.validation_rules[param_name]

            # Hard limits (always reject)
            if value < rules.get('min', float('-inf')):
                is_valid = False
                warnings.append(
                    f"INVALID: {param_name}={value:.2f} < min={rules['min']:.2f}. "
                    f"{rules.get('reason', '')}"
                )

            if value > rules.get('max', float('inf')):
                is_valid = False
                warnings.append(
                    f"INVALID: {param_name}={value:.2f} > max={rules['max']:.2f}. "
                    f"{rules.get('reason', '')}"
                )

            # Soft limits (warnings, may cause issues)
            if 'soft_min' in rules and value < rules['soft_min']:
                msg = (
                    f"WARNING: {param_name}={value:.2f} < recommended={rules['soft_min']:.2f}. "
                    f"{rules.get('reason', 'May cause simulation issues')}"
                )
                warnings.append(msg)
                if strict:
                    is_valid = False

            if 'soft_max' in rules and value > rules['soft_max']:
                msg = (
                    f"WARNING: {param_name}={value:.2f} > recommended={rules['soft_max']:.2f}. "
                    f"{rules.get('reason', 'May cause simulation issues')}"
                )
                warnings.append(msg)
                if strict:
                    is_valid = False

        # Model-specific combined checks can be added here if needed
        # For now, rely only on parameter bounds (no arbitrary physics checks)

        # Print warnings if verbose
        if self.verbose and warnings:
            print(f"\n[VALIDATOR] Validation results:")
            for warning in warnings:
                print(f"  {warning}")

        return is_valid, warnings

    def _validate_circular_plate_aspect_ratio(
        self,
        design_variables: Dict[str, float]
    ) -> tuple[bool, List[str]]:
        """Check circular plate aspect ratio (diameter/thickness).

        Returns:
            (is_valid, warnings): Tuple of validation status and warning messages
        """
        warnings = []
        is_valid = True

        diameter = design_variables.get('inner_diameter')
        thickness = design_variables.get('plate_thickness')

        if diameter and thickness:
            aspect_ratio = diameter / thickness

            rules = self.validation_rules.get('aspect_ratio', {})
            min_aspect = rules.get('min', 0)
            max_aspect = rules.get('max', float('inf'))

            if aspect_ratio > max_aspect:
                is_valid = False  # FIX: Make this a hard rejection
                warnings.append(
                    f"INVALID: Aspect ratio {aspect_ratio:.1f} > {max_aspect:.1f}. "
                    f"Very thin plate will cause numerical instability."
                )
            elif aspect_ratio < min_aspect:
                is_valid = False  # FIX: Make this a hard rejection
                warnings.append(
                    f"INVALID: Aspect ratio {aspect_ratio:.1f} < {min_aspect:.1f}. "
                    f"Very thick plate will have poor mesh quality."
                )

        return is_valid, warnings

    def suggest_corrections(
        self,
        design_variables: Dict[str, float]
    ) -> Dict[str, float]:
        """
        Suggest corrected parameters that are more likely to succeed.

        Args:
            design_variables: Original parameters

        Returns:
            Corrected parameters (clamped to safe ranges)
        """
        corrected = design_variables.copy()

        for param_name, value in design_variables.items():
            if param_name not in self.validation_rules:
                continue

            rules = self.validation_rules[param_name]

            # Clamp to soft limits (safer range)
            soft_min = rules.get('soft_min', rules.get('min', float('-inf')))
            soft_max = rules.get('soft_max', rules.get('max', float('inf')))

            if value < soft_min:
                corrected[param_name] = soft_min
                if self.verbose:
                    print(f"[VALIDATOR] Corrected {param_name}: {value:.2f} -> {soft_min:.2f}")

            if value > soft_max:
                corrected[param_name] = soft_max
                if self.verbose:
                    print(f"[VALIDATOR] Corrected {param_name}: {value:.2f} -> {soft_max:.2f}")

        return corrected


def validate_before_simulation(
    design_variables: Dict[str, float],
    model_type: str = 'circular_plate',
    strict: bool = False
) -> Tuple[bool, List[str]]:
    """
    Convenience function for quick validation.

    Args:
        design_variables: Parameters to validate
        model_type: Type of FEA model
        strict: Reject on warnings (not just errors)

    Returns:
        (is_valid, warnings)
    """
    validator = SimulationValidator(model_type=model_type, verbose=False)
    return validator.validate(design_variables, strict=strict)
