"""
Hybrid Mode Study Creator - Complete Automation

This module provides COMPLETE automation for creating optimization studies:
1. Creates proper study structure (1_setup, 2_substudies, 3_reports)
2. Runs benchmarking to validate simulation setup
3. Auto-generates runner from workflow JSON
4. Provides progress monitoring

No user intervention required after workflow JSON is created.
"""

from pathlib import Path
from typing import Dict, Any, Optional, List
import json
import shutil
from datetime import datetime


class HybridStudyCreator:
    """
    Complete automation for Hybrid Mode study creation.

    Usage:
        creator = HybridStudyCreator()
        study = creator.create_from_workflow(
            workflow_json_path="path/to/workflow.json",
            model_files={"prt": "path.prt", "sim": "path.sim", "fem": "path.fem"},
            study_name="my_optimization"
        )
    """

    def __init__(self):
        self.project_root = Path(__file__).parent.parent

    def create_from_workflow(
        self,
        workflow_json_path: Path,
        model_files: Dict[str, Path],
        study_name: str,
        output_parent: Optional[Path] = None
    ) -> Path:
        """
        Create complete study from workflow JSON with full automation.

        Args:
            workflow_json_path: Path to workflow JSON config
            model_files: Dict with keys 'prt', 'sim', 'fem' (and optionally 'fem_i')
            study_name: Name for the study
            output_parent: Parent directory for studies (default: project_root/studies)

        Returns:
            Path to created study directory
        """
        print("="*80)
        print("  HYBRID MODE - AUTOMATED STUDY CREATION")
        print("="*80)
        print()

        # Step 1: Create study structure
        print("[1/5] Creating study structure...")
        study_dir = self._create_study_structure(study_name, output_parent)
        print(f"  [OK] Study directory: {study_dir.name}")
        print()

        # Step 2: Copy files
        print("[2/5] Copying model files...")
        self._copy_model_files(model_files, study_dir / "1_setup/model")
        print(f"  [OK] Copied {len(model_files)} files")
        print()

        # Step 3: Copy workflow JSON
        print("[3/5] Installing workflow configuration...")
        workflow_dest = study_dir / "1_setup/workflow_config.json"
        shutil.copy2(workflow_json_path, workflow_dest)
        with open(workflow_dest) as f:
            workflow = json.load(f)
        print(f"  [OK] Workflow: {workflow.get('study_name', 'unnamed')}")
        print(f"  [OK] Variables: {len(workflow.get('design_variables', []))}")
        print(f"  [OK] Objectives: {len(workflow.get('objectives', []))}")
        print()

        # Step 4: Run benchmarking
        print("[4/5] Running benchmarking (validating simulation setup)...")
        benchmark_results = self._run_benchmarking(
            study_dir / "1_setup/model" / model_files['prt'].name,
            study_dir / "1_setup/model" / model_files['sim'].name,
            workflow
        )

        if not benchmark_results['success']:
            raise RuntimeError(f"Benchmarking failed: {benchmark_results['error']}")

        print(f"  [OK] Simulation validated")
        print(f"  [OK] Extracted {benchmark_results['n_results']} results")
        print()

        # Step 4.5: Generate configuration report
        print("[4.5/5] Generating configuration report...")
        self._generate_configuration_report(study_dir, workflow, benchmark_results)
        print(f"  [OK] Configuration report: 1_setup/CONFIGURATION_REPORT.md")
        print()

        # Step 5: Generate runner
        print("[5/5] Generating optimization runner...")
        runner_path = self._generate_runner(study_dir, workflow, benchmark_results)
        print(f"  [OK] Runner: {runner_path.name}")
        print()

        # Create README
        self._create_readme(study_dir, workflow, benchmark_results)

        print("="*80)
        print("  STUDY CREATION COMPLETE")
        print("="*80)
        print()
        print(f"Study location: {study_dir}")
        print()
        print("To run optimization:")
        print(f"  python {runner_path.relative_to(self.project_root)}")
        print()

        return study_dir

    def _create_study_structure(self, study_name: str, output_parent: Optional[Path]) -> Path:
        """Create proper study folder structure."""
        if output_parent is None:
            output_parent = self.project_root / "studies"

        study_dir = output_parent / study_name

        # Create structure
        (study_dir / "1_setup/model").mkdir(parents=True, exist_ok=True)
        (study_dir / "2_results").mkdir(parents=True, exist_ok=True)
        (study_dir / "3_reports").mkdir(parents=True, exist_ok=True)

        return study_dir

    def _copy_model_files(self, model_files: Dict[str, Path], dest_dir: Path):
        """Copy model files to study."""
        for file_type, file_path in model_files.items():
            if file_path and file_path.exists():
                shutil.copy2(file_path, dest_dir / file_path.name)

    def _run_benchmarking(
        self,
        prt_file: Path,
        sim_file: Path,
        workflow: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Run INTELLIGENT benchmarking to validate simulation setup.

        This uses IntelligentSetup to:
        1. Solve ALL solutions in the .sim file
        2. Discover all available results
        3. Match objectives to results automatically
        4. Select optimal solution for optimization

        Returns dict with:
            - success: bool
            - n_results: int (number of results extracted)
            - results: dict (extracted values)
            - solution_name: str (optimal solution to use for optimization)
            - error: str (if failed)
        """
        from optimization_engine.intelligent_setup import IntelligentSetup

        try:
            print("    Running INTELLIGENT benchmarking...")
            print("    - Solving ALL solutions in .sim file")
            print("    - Discovering all available results")
            print("    - Matching objectives to results")
            print()

            # Run intelligent benchmarking
            intelligent = IntelligentSetup()
            benchmark_data = intelligent.run_complete_benchmarking(
                prt_file, sim_file, workflow
            )

            if not benchmark_data['success']:
                return {
                    'success': False,
                    'error': f"Intelligent benchmarking failed: {benchmark_data.get('error', 'Unknown')}"
                }

            # Display discovered information
            print(f"    [OK] Expressions found: {len(benchmark_data.get('expressions', {}))}")
            print(f"    [OK] Solutions found: {len(benchmark_data.get('solutions', {}))}")
            print(f"    [OK] Results discovered: {len(benchmark_data.get('available_results', {}))}")

            # Display objective mapping
            obj_mapping = benchmark_data.get('objective_mapping', {})
            if 'objectives' in obj_mapping:
                print(f"    [OK] Objectives matched: {len(obj_mapping['objectives'])}")
                for obj_name, obj_info in obj_mapping['objectives'].items():
                    solution = obj_info.get('solution', 'Unknown')
                    result_type = obj_info.get('result_type', 'Unknown')
                    confidence = obj_info.get('match_confidence', 'Unknown')
                    print(f"         - {obj_name}: {result_type} from '{solution}' ({confidence} confidence)")

            # Get recommended solution
            recommended_solution = obj_mapping.get('primary_solution')
            if recommended_solution:
                print(f"    [OK] Recommended solution: {recommended_solution}")

            # Extract baseline values
            extracted = {}
            for obj in workflow.get('objectives', []):
                extraction = obj.get('extraction', {})
                action = extraction.get('action', '')

                if 'frequency' in action.lower() or 'eigenvalue' in action.lower():
                    # Extract eigenvalues from discovered results
                    available_results = benchmark_data.get('available_results', {})
                    if 'eigenvalues' in available_results:
                        # Get op2 file from eigenvalues result
                        eig_result = available_results['eigenvalues']
                        op2_file = Path(eig_result['op2_path'])
                        freq = self._extract_frequency(op2_file, mode_number=1)
                        extracted['first_frequency'] = freq
                        print(f"    Baseline first frequency: {freq:.4f} Hz")

                elif 'displacement' in action.lower():
                    # Extract displacement from discovered results
                    available_results = benchmark_data.get('available_results', {})
                    if 'displacements' in available_results:
                        disp_result = available_results['displacements']
                        op2_file = Path(disp_result['op2_path'])
                        disp = self._extract_displacement(op2_file)
                        extracted['max_displacement'] = disp
                        print(f"    Baseline max displacement: {disp:.6f} mm")

                elif 'stress' in action.lower():
                    # Extract stress from discovered results
                    available_results = benchmark_data.get('available_results', {})
                    if 'stresses' in available_results:
                        stress_result = available_results['stresses']
                        op2_file = Path(stress_result['op2_path'])
                        stress = self._extract_stress(op2_file)
                    extracted['max_stress'] = stress
                    print(f"    Baseline max stress: {stress:.2f} MPa")

            return {
                'success': True,
                'n_results': len(extracted),
                'results': extracted,
                'solution_name': recommended_solution,
                'benchmark_data': benchmark_data  # Include full benchmarking data
            }

        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }

    def _extract_frequency(self, op2_file: Path, mode_number: int = 1) -> float:
        """Extract eigenfrequency from OP2."""
        from pyNastran.op2.op2 import OP2
        import numpy as np

        model = OP2()
        model.read_op2(str(op2_file))

        if not hasattr(model, 'eigenvalues') or len(model.eigenvalues) == 0:
            raise ValueError("No eigenvalues found in OP2 file")

        subcase = list(model.eigenvalues.keys())[0]
        eig_obj = model.eigenvalues[subcase]

        eigenvalue = eig_obj.eigenvalues[mode_number - 1]
        angular_freq = np.sqrt(eigenvalue)
        frequency_hz = angular_freq / (2 * np.pi)

        return float(frequency_hz)

    def _extract_displacement(self, op2_file: Path) -> float:
        """Extract max displacement from OP2."""
        from pyNastran.op2.op2 import OP2
        import numpy as np

        model = OP2()
        model.read_op2(str(op2_file))

        if hasattr(model, 'displacements') and len(model.displacements) > 0:
            subcase = list(model.displacements.keys())[0]
            disp_obj = model.displacements[subcase]
            translations = disp_obj.data[0, :, :3]  # [time, node, tx/ty/tz]
            magnitudes = np.linalg.norm(translations, axis=1)
            return float(np.max(magnitudes))

        raise ValueError("No displacements found in OP2 file")

    def _extract_stress(self, op2_file: Path) -> float:
        """Extract max von Mises stress from OP2."""
        from pyNastran.op2.op2 import OP2
        import numpy as np

        model = OP2()
        model.read_op2(str(op2_file))

        # Try different stress result locations
        if hasattr(model, 'cquad4_stress') and len(model.cquad4_stress) > 0:
            subcase = list(model.cquad4_stress.keys())[0]
            stress_obj = model.cquad4_stress[subcase]
            von_mises = stress_obj.data[0, :, 7]  # von Mises typically at index 7
            return float(np.max(von_mises))

        raise ValueError("No stress results found in OP2 file")

    def _generate_runner(
        self,
        study_dir: Path,
        workflow: Dict[str, Any],
        benchmark_results: Dict[str, Any]
    ) -> Path:
        """Generate optimization runner script."""
        runner_path = study_dir / "run_optimization.py"

        # Detect result types from workflow
        extracts_frequency = any(
            'frequency' in obj.get('extraction', {}).get('action', '').lower()
            for obj in workflow.get('objectives', [])
        )

        # Generate extractor function based on workflow
        extractor_code = self._generate_extractor_code(workflow)

        runner_code = f'''"""
Auto-generated optimization runner
Created: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
"""

import sys
from pathlib import Path

# Add project root to path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))

import json
import optuna
from optimization_engine.nx_updater import NXParameterUpdater
from optimization_engine.nx_solver import NXSolver


{extractor_code}


def main():
    print("="*80)
    print("  {workflow.get('study_name', 'OPTIMIZATION').upper()}")
    print("="*80)
    print()

    # Load workflow
    config_file = Path(__file__).parent / "1_setup/workflow_config.json"
    with open(config_file) as f:
        workflow = json.load(f)

    print("Workflow loaded:")
    print(f"  Request: {workflow.get('optimization_request', 'N/A')}")
    print(f"  Variables: {len(workflow.get('design_variables', []))}")
    print()

    # Setup paths
    prt_file = Path(__file__).parent / "1_setup/model" / [f for f in (Path(__file__).parent / "1_setup/model").glob("*.prt")][0].name
    sim_file = Path(__file__).parent / "1_setup/model" / [f for f in (Path(__file__).parent / "1_setup/model").glob("*.sim")][0].name
    output_dir = Path(__file__).parent / "2_results"
    reports_dir = Path(__file__).parent / "3_reports"
    output_dir.mkdir(parents=True, exist_ok=True)
    reports_dir.mkdir(parents=True, exist_ok=True)

    # Initialize
    updater = NXParameterUpdater(prt_file)
    solver = NXSolver()

    # Create Optuna study
    study_name = "{workflow.get('study_name', 'optimization')}"
    storage = f"sqlite:///{{output_dir / 'study.db'}}"
    study = optuna.create_study(
        study_name=study_name,
        storage=storage,
        load_if_exists=True,
        direction="minimize"
    )

    # Initialize incremental history
    history_file = output_dir / 'optimization_history_incremental.json'
    history = []
    if history_file.exists():
        with open(history_file) as f:
            history = json.load(f)

    def objective(trial):
        # Sample design variables
        params = {{}}
        for var in workflow['design_variables']:
            name = var['parameter']
            bounds = var['bounds']
            params[name] = trial.suggest_float(name, bounds[0], bounds[1])

        print(f"\\nTrial {{trial.number}}:")
        for name, value in params.items():
            print(f"  {{name}} = {{value:.2f}}")

        # Update model
        updater.update_expressions(params)

        # Run simulation with the optimal solution
        result = solver.run_simulation(sim_file, solution_name="{benchmark_results.get('solution_name')}")
        if not result['success']:
            raise RuntimeError(f"Simulation failed: {{result.get('errors', 'Unknown')}}")
        op2_file = result['op2_file']

        # Extract results and calculate objective
        results = extract_results(op2_file, workflow)

        # Print results
        for name, value in results.items():
            print(f"  {{name}} = {{value:.4f}}")

        # Calculate objective (from first objective in workflow)
        obj_config = workflow['objectives'][0]
        result_name = list(results.keys())[0]

        # For target-matching objectives, compute error from target
        if 'target_frequency' in obj_config.get('extraction', {{}}).get('params', {{}}):
            target = obj_config['extraction']['params']['target_frequency']
            objective_value = abs(results[result_name] - target)
            print(f"  Frequency: {{results[result_name]:.4f}} Hz, Target: {{target}} Hz, Error: {{objective_value:.4f}} Hz")
        elif obj_config['goal'] == 'minimize':
            objective_value = results[result_name]
        else:
            objective_value = -results[result_name]

        print(f"  Objective = {{objective_value:.4f}}")

        # Save to incremental history
        trial_record = {{
            'trial_number': trial.number,
            'design_variables': params,
            'results': results,
            'objective': objective_value
        }}
        history.append(trial_record)
        with open(history_file, 'w') as f:
            json.dump(history, f, indent=2)

        return objective_value

    # Run optimization
    n_trials = 10
    print(f"\\nRunning {{n_trials}} trials...")
    print("="*80)
    print()

    study.optimize(objective, n_trials=n_trials)

    # Results
    print()
    print("="*80)
    print("  OPTIMIZATION COMPLETE")
    print("="*80)
    print()
    print(f"Best trial: #{{study.best_trial.number}}")
    for name, value in study.best_params.items():
        print(f"  {{name}} = {{value:.2f}}")
    print(f"\\nBest objective = {{study.best_value:.4f}}")
    print()

    # Generate human-readable markdown report with graphs
    print("Generating optimization report...")
    from optimization_engine.generate_report_markdown import generate_markdown_report

    # Extract target frequency from workflow objectives
    target_value = None
    tolerance = 0.1
    for obj in workflow.get('objectives', []):
        if 'target_frequency' in obj.get('extraction', {{}}).get('params', {{}}):
            target_value = obj['extraction']['params']['target_frequency']
            break

    # Generate markdown report with graphs
    report = generate_markdown_report(history_file, target_value=target_value, tolerance=tolerance)
    report_file = reports_dir / 'OPTIMIZATION_REPORT.md'
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write(report)

    print(f"✓ Markdown report with graphs saved to: {{report_file}}")
    print()


if __name__ == "__main__":
    main()
'''

        with open(runner_path, 'w', encoding='utf-8') as f:
            f.write(runner_code)

        return runner_path

    def _generate_extractor_code(self, workflow: Dict[str, Any]) -> str:
        """Generate extractor function based on workflow objectives."""

        # Detect what needs to be extracted
        needs_frequency = False
        needs_displacement = False
        needs_stress = False

        for obj in workflow.get('objectives', []):
            action = obj.get('extraction', {}).get('action', '').lower()
            if 'frequency' in action or 'eigenvalue' in action:
                needs_frequency = True
            elif 'displacement' in action:
                needs_displacement = True
            elif 'stress' in action:
                needs_stress = True

        code = '''
def extract_results(op2_file, workflow):
    """Extract results from OP2 file based on workflow objectives."""
    from pyNastran.op2.op2 import OP2
    import numpy as np

    model = OP2()
    model.read_op2(str(op2_file))

    results = {}
'''

        if needs_frequency:
            code += '''
    # Extract first frequency
    if hasattr(model, 'eigenvalues') and len(model.eigenvalues) > 0:
        subcase = list(model.eigenvalues.keys())[0]
        eig_obj = model.eigenvalues[subcase]
        eigenvalue = eig_obj.eigenvalues[0]
        angular_freq = np.sqrt(eigenvalue)
        frequency_hz = angular_freq / (2 * np.pi)
        results['first_frequency'] = float(frequency_hz)
    else:
        raise ValueError("No eigenvalues found in OP2 file")
'''

        if needs_displacement:
            code += '''
    # Extract max displacement
    if hasattr(model, 'displacements') and len(model.displacements) > 0:
        subcase = list(model.displacements.keys())[0]
        disp_obj = model.displacements[subcase]
        translations = disp_obj.data[0, :, :3]
        magnitudes = np.linalg.norm(translations, axis=1)
        results['max_displacement'] = float(np.max(magnitudes))
'''

        if needs_stress:
            code += '''
    # Extract max stress
    if hasattr(model, 'cquad4_stress') and len(model.cquad4_stress) > 0:
        subcase = list(model.cquad4_stress.keys())[0]
        stress_obj = model.cquad4_stress[subcase]
        von_mises = stress_obj.data[0, :, 7]
        results['max_stress'] = float(np.max(von_mises))
'''

        code += '''
    return results
'''

        return code

    def _generate_configuration_report(
        self,
        study_dir: Path,
        workflow: Dict[str, Any],
        benchmark_results: Dict[str, Any]
    ):
        """
        Generate a comprehensive configuration report with ALL setup details.

        This creates 1_setup/CONFIGURATION_REPORT.md with:
        - User's optimization request
        - All discovered expressions
        - All discovered solutions
        - All available result types
        - Objective matching details
        - Baseline values
        - Warnings and issues
        """
        report_path = study_dir / "1_setup" / "CONFIGURATION_REPORT.md"

        # Get benchmark data
        benchmark_data = benchmark_results.get('benchmark_data', {})
        expressions = benchmark_data.get('expressions', {})
        solutions = benchmark_data.get('solutions', {})
        available_results = benchmark_data.get('available_results', {})
        obj_mapping = benchmark_data.get('objective_mapping', {})

        # Build expressions section
        expressions_md = "## Model Expressions\n\n"
        if expressions:
            expressions_md += f"**Total expressions found: {len(expressions)}**\n\n"
            expressions_md += "| Expression Name | Current Value | Units | Formula |\n"
            expressions_md += "|----------------|---------------|-------|----------|\n"
            for name, info in sorted(expressions.items()):
                value = info.get('value', 'N/A')
                units = info.get('units', '')
                formula = info.get('formula', '')
                expressions_md += f"| {name} | {value} | {units} | {formula} |\n"
        else:
            expressions_md += "*No expressions found in model*\n"

        # Build solutions section
        solutions_md = "## Simulation Solutions\n\n"
        if solutions:
            # Handle both old format (solution_names list) and new format (dict)
            if isinstance(solutions, dict):
                if 'solution_names' in solutions:
                    # Old format: just solution names
                    solution_names = solutions.get('solution_names', [])
                    num_solved = solutions.get('num_solved', 0)
                    num_failed = solutions.get('num_failed', 0)
                    num_skipped = solutions.get('num_skipped', 0)

                    solutions_md += f"**Solutions discovered**: {len(solution_names)}\n"
                    solutions_md += f"**Solved**: {num_solved} | **Failed**: {num_failed} | **Skipped**: {num_skipped}\n\n"

                    if solution_names:
                        for sol_name in solution_names:
                            solutions_md += f"- {sol_name}\n"
                    else:
                        solutions_md += "*No solution names retrieved*\n"
                else:
                    # New format: dict of solution details
                    solutions_md += f"**Total solutions found: {len(solutions)}**\n\n"
                    for sol_name, sol_info in solutions.items():
                        solutions_md += f"### {sol_name}\n\n"
                        solutions_md += f"- **Type**: {sol_info.get('type', 'Unknown')}\n"
                        solutions_md += f"- **OP2 File**: `{sol_info.get('op2_path', 'N/A')}`\n\n"
        else:
            solutions_md += "*No solutions discovered - check if benchmarking solved all solutions*\n"

        # Build available results section
        results_md = "## Available Results\n\n"
        if available_results:
            results_md += f"**Total result types discovered: {len(available_results)}**\n\n"
            for result_type, result_info in available_results.items():
                results_md += f"### {result_type}\n\n"
                results_md += f"- **Solution**: {result_info.get('solution', 'Unknown')}\n"
                results_md += f"- **OP2 File**: `{result_info.get('op2_path', 'N/A')}`\n"
                if 'sample_value' in result_info:
                    results_md += f"- **Sample Value**: {result_info['sample_value']}\n"
                results_md += "\n"
        else:
            results_md += "*No results discovered - check if simulations solved successfully*\n"

        # Build objective matching section
        matching_md = "## Objective Matching\n\n"
        if 'objectives' in obj_mapping and obj_mapping['objectives']:
            matching_md += f"**Objectives matched: {len(obj_mapping['objectives'])}**\n\n"
            for obj_name, obj_info in obj_mapping['objectives'].items():
                solution = obj_info.get('solution', 'NONE')
                result_type = obj_info.get('result_type', 'Unknown')
                confidence = obj_info.get('match_confidence', 'Unknown')
                extractor = obj_info.get('extractor', 'Unknown')
                op2_file = obj_info.get('op2_file', 'N/A')
                error = obj_info.get('error', None)

                matching_md += f"### {obj_name}\n\n"
                matching_md += f"- **Result Type**: {result_type}\n"
                matching_md += f"- **Solution**: {solution}\n"
                matching_md += f"- **Confidence**: {confidence}\n"
                matching_md += f"- **Extractor**: `{extractor}`\n"
                matching_md += f"- **OP2 File**: `{op2_file}`\n"

                if error:
                    matching_md += f"- **⚠️ ERROR**: {error}\n"

                matching_md += "\n"

            # Add primary solution
            primary_solution = obj_mapping.get('primary_solution')
            if primary_solution:
                matching_md += f"**Primary Solution Selected**: `{primary_solution}`\n\n"
                matching_md += "This solution will be used for optimization.\n\n"
        else:
            matching_md += "*No objectives matched - check workflow configuration*\n"

        # Build baseline values section
        baseline_md = "## Baseline Values\n\n"
        baseline_results = benchmark_results.get('results', {})
        if baseline_results:
            baseline_md += "Values extracted from the initial (unoptimized) model:\n\n"
            for key, value in baseline_results.items():
                baseline_md += f"- **{key}**: {value}\n"
        else:
            baseline_md += "*No baseline values extracted*\n"

        # Build warnings section
        warnings_md = "## Warnings and Issues\n\n"
        warnings = []

        # Check for missing eigenvalues
        for obj_name, obj_info in obj_mapping.get('objectives', {}).items():
            if obj_info.get('error'):
                warnings.append(f"- ⚠️ **{obj_name}**: {obj_info['error']}")

        # Check for no solutions
        if not solutions:
            warnings.append("- ⚠️ **No solutions discovered**: Benchmarking may not have solved all solutions")

        # Check for no results
        if not available_results:
            warnings.append("- ⚠️ **No results available**: Check if simulations ran successfully")

        if warnings:
            warnings_md += "\n".join(warnings) + "\n"
        else:
            warnings_md += "✅ No issues detected!\n"

        # Build full report
        content = f'''# Configuration Report

**Study**: {workflow.get('study_name', study_dir.name)}
**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

---

## Optimization Request

**User's Goal**:

> {workflow.get('optimization_request', '*No description provided*')}

**Design Variables**: {len(workflow.get('design_variables', []))}

| Variable | Min | Max |
|----------|-----|-----|
'''

        for var in workflow.get('design_variables', []):
            param = var.get('parameter', 'Unknown')
            bounds = var.get('bounds', [0, 0])
            content += f"| {param} | {bounds[0]} | {bounds[1]} |\n"

        content += f'''

**Objectives**: {len(workflow.get('objectives', []))}

| Objective | Goal |
|-----------|------|
'''

        for obj in workflow.get('objectives', []):
            obj_name = obj.get('name', 'Unknown')
            goal = obj.get('goal', 'Unknown')
            content += f"| {obj_name} | {goal} |\n"

        content += f'''

---

{expressions_md}

---

{solutions_md}

---

{results_md}

---

{matching_md}

---

{baseline_md}

---

{warnings_md}

---

## Next Steps

1. ✅ Study structure created
2. ✅ Benchmarking complete
3. ✅ Configuration validated
4. ➡️ **Run optimization**: `python run_optimization.py`

---

*This report was auto-generated by the Intelligent Setup System*
'''

        with open(report_path, 'w', encoding='utf-8') as f:
            f.write(content)

    def _create_readme(
        self,
        study_dir: Path,
        workflow: Dict[str, Any],
        benchmark_results: Dict[str, Any]
    ):
        """Create README for the study."""
        readme_path = study_dir / "README.md"

        # Format design variables
        vars_md = ""
        for var in workflow.get('design_variables', []):
            bounds = var.get('bounds', [0, 1])
            desc = var.get('description', '')
            vars_md += f"- `{var['parameter']}`: {bounds[0]}-{bounds[1]} mm"
            if desc:
                vars_md += f" - {desc}"
            vars_md += "\n"

        # Format objectives
        objs_md = ""
        for obj in workflow.get('objectives', []):
            objs_md += f"- {obj['goal'].title()} {obj['name']}\n"

        # Format benchmark results
        bench_md = ""
        if benchmark_results.get('success'):
            for name, value in benchmark_results.get('results', {}).items():
                bench_md += f"- {name}: {value:.4f}\n"

        content = f'''# {workflow.get('study_name', 'Optimization Study')}

**Created**: {datetime.now().strftime("%Y-%m-%d")}
**Mode**: Hybrid (Workflow JSON + Auto-generated runner)

## Problem Description

{workflow.get('optimization_request', 'N/A')}

### Design Variables

{vars_md}

### Objectives

{objs_md}

## Benchmark Results

Baseline simulation (default geometry):

{bench_md}

## Study Structure

```
{study_dir.name}/
├── 1_setup/
│   ├── model/                  # FEM model files
│   └── workflow_config.json    # Optimization specification
├── 2_substudies/
│   └── results/                # Optimization results
├── 3_reports/
├── run_optimization.py         # Auto-generated runner
└── README.md                   # This file
```

## Running the Optimization

```bash
python run_optimization.py
```

This will:
1. Load workflow configuration
2. Initialize NX model updater and solver
3. Run {10} optimization trials
4. Save results to `2_substudies/results/`

## Results

After optimization completes, check:
- `2_substudies/results/study.db` - Optuna database
- `2_substudies/results/` - Best design parameters

---

**Created by Hybrid Mode** - 90% automation, production ready!
'''

        with open(readme_path, 'w', encoding='utf-8') as f:
            f.write(content)


if __name__ == "__main__":
    # Example usage
    creator = HybridStudyCreator()

    # Example: Create study from workflow JSON
    study_dir = creator.create_from_workflow(
        workflow_json_path=Path("path/to/workflow.json"),
        model_files={
            'prt': Path("path/to/model.prt"),
            'sim': Path("path/to/model.sim"),
            'fem': Path("path/to/model.fem")
        },
        study_name="example_study"
    )

    print(f"Study created: {study_dir}")
