"""
Bracket Displacement Maximization Study
========================================

Complete optimization workflow using Phase 3.3 Wizard:
1. Setup wizard validates the complete pipeline
2. Auto-detects element types from OP2
3. Runs 20-trial optimization
4. Generates comprehensive report
5. Saves results in study directory

Objective: Maximize displacement
Constraint: Safety factor >= 4.0
Material: Aluminum 6061-T6 (Yield = 276 MPa)
Design Variables: tip_thickness (15-25mm), support_angle (20-40deg)
"""

import sys
import json
from pathlib import Path
from datetime import datetime

# Add parent directories to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from optimization_engine.optimization_setup_wizard import OptimizationSetupWizard
from optimization_engine.llm_optimization_runner import LLMOptimizationRunner
from optimization_engine.nx_solver import NXSolver
from optimization_engine.nx_updater import NXParameterUpdater


def print_section(title: str):
    """Print a section header."""
    print()
    print("=" * 80)
    print(f"  {title}")
    print("=" * 80)
    print()


def save_results(results: dict, study_dir: Path):
    """Save optimization results to study directory."""
    results_dir = study_dir / "results"
    results_dir.mkdir(exist_ok=True)

    # Save complete history
    history_file = results_dir / "optimization_history.json"
    with open(history_file, 'w') as f:
        json.dump(results['history'], f, indent=2, default=str)

    # Save best design
    best_design = {
        'trial_number': results['best_trial_number'],
        'parameters': results['best_params'],
        'objective_value': results['best_value'],
        'timestamp': datetime.now().isoformat()
    }

    best_trial = results['history'][results['best_trial_number']]
    best_design['results'] = best_trial['results']
    best_design['calculations'] = best_trial['calculations']

    best_file = results_dir / "best_design.json"
    with open(best_file, 'w') as f:
        json.dump(best_design, f, indent=2, default=str)

    # Generate markdown report
    report_file = results_dir / "optimization_report.md"
    with open(report_file, 'w') as f:
        f.write("# Bracket Displacement Maximization - Optimization Report\n\n")
        f.write(f"**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")

        f.write("## Problem Definition\n\n")
        f.write("- **Objective**: Maximize displacement\n")
        f.write("- **Constraint**: Safety factor >= 4.0\n")
        f.write("- **Material**: Aluminum 6061-T6 (Yield = 276 MPa)\n")
        f.write("- **Design Variables**:\n")
        f.write("  - tip_thickness: 15-25 mm\n")
        f.write("  - support_angle: 20-40 degrees\n\n")

        f.write("## Best Design\n\n")
        f.write(f"- **Trial**: {results['best_trial_number']}\n")
        f.write(f"- **tip_thickness**: {results['best_params']['tip_thickness']:.3f} mm\n")
        f.write(f"- **support_angle**: {results['best_params']['support_angle']:.3f} degrees\n")
        f.write(f"- **Objective value**: {results['best_value']:.6f}\n\n")

        best_results = best_trial['results']
        best_calcs = best_trial['calculations']

        f.write("## Performance\n\n")
        f.write(f"- **Max displacement**: {best_results.get('max_displacement', 0):.6f} mm\n")
        f.write(f"- **Max stress**: {best_results.get('max_von_mises', 0):.3f} MPa\n")
        f.write(f"- **Safety factor**: {best_calcs.get('safety_factor', 0):.3f}\n")
        f.write(f"- **Constraint**: {'✓ SATISFIED' if best_calcs.get('safety_factor', 0) >= 4.0 else '✗ VIOLATED'}\n\n")

        f.write("## Trial History\n\n")
        f.write("| Trial | Tip (mm) | Angle (°) | Disp (mm) | Stress (MPa) | SF | Objective |\n")
        f.write("|-------|----------|-----------|-----------|--------------|----|-----------|\n")

        for trial in results['history']:
            num = trial['trial_number']
            tip = trial['design_variables']['tip_thickness']
            ang = trial['design_variables']['support_angle']
            disp = trial['results'].get('max_displacement', 0)
            stress = trial['results'].get('max_von_mises', 0)
            sf = trial['calculations'].get('safety_factor', 0)
            obj = trial['objective']
            f.write(f"| {num} | {tip:.2f} | {ang:.2f} | {disp:.6f} | {stress:.2f} | {sf:.2f} | {obj:.6f} |\n")

    return history_file, best_file, report_file


def main():
    print_section("BRACKET DISPLACEMENT MAXIMIZATION STUDY")

    print("Study Configuration:")
    print("  - Objective: Maximize displacement")
    print("  - Constraint: Safety factor >= 4.0")
    print("  - Material: Aluminum 6061-T6 (Yield = 276 MPa)")
    print("  - Design Variables:")
    print("    * tip_thickness: 15-25 mm")
    print("    * support_angle: 20-40 degrees")
    print("  - Optimization trials: 20")
    print()

    # File paths - USE STUDY DIRECTORY
    study_dir = Path(__file__).parent
    prt_file = study_dir / "model" / "Bracket.prt"
    sim_file = study_dir / "model" / "Bracket_sim1.sim"

    if not prt_file.exists():
        print(f"ERROR: Part file not found: {prt_file}")
        sys.exit(1)

    if not sim_file.exists():
        print(f"ERROR: Simulation file not found: {sim_file}")
        sys.exit(1)

    print(f"Part file: {prt_file}")
    print(f"Simulation file: {sim_file}")
    print(f"Study directory: {study_dir}")
    print()

    # =========================================================================
    # PHASE 3.3: OPTIMIZATION SETUP WIZARD
    # =========================================================================

    print_section("STEP 1: INITIALIZATION")

    print("Initializing Optimization Setup Wizard...")
    wizard = OptimizationSetupWizard(prt_file, sim_file)
    print("  [OK] Wizard initialized")
    print()

    print_section("STEP 2: MODEL INTROSPECTION")

    print("Reading NX model expressions...")
    model_info = wizard.introspect_model()

    print(f"Found {len(model_info.expressions)} expressions:")
    for name, info in model_info.expressions.items():
        print(f"  - {name}: {info['value']} {info['units']}")
    print()

    print_section("STEP 3: BASELINE SIMULATION")

    print("Running baseline simulation to generate reference OP2...")
    print("(This validates that NX simulation works before optimization)")
    baseline_op2 = wizard.run_baseline_simulation()
    print(f"  [OK] Baseline OP2: {baseline_op2.name}")
    print()

    print_section("STEP 4: OP2 INTROSPECTION")

    print("Analyzing OP2 file to auto-detect element types...")
    op2_info = wizard.introspect_op2()

    print("OP2 Contents:")
    print(f"  - Element types with stress: {', '.join(op2_info.element_types)}")
    print(f"  - Available result types: {', '.join(op2_info.result_types)}")
    print(f"  - Subcases: {op2_info.subcases}")
    print(f"  - Nodes: {op2_info.node_count}")
    print(f"  - Elements: {op2_info.element_count}")
    print()

    print_section("STEP 5: WORKFLOW CONFIGURATION")

    print("Building LLM workflow with auto-detected element types...")

    # Use the FIRST detected element type (could be CHEXA, CPENTA, CTETRA, etc.)
    detected_element_type = op2_info.element_types[0].lower() if op2_info.element_types else 'ctetra'

    print(f"  Using detected element type: {detected_element_type.upper()}")
    print()

    llm_workflow = {
        'engineering_features': [
            {
                'action': 'extract_displacement',
                'domain': 'result_extraction',
                'description': 'Extract displacement results from OP2 file',
                'params': {'result_type': 'displacement'}
            },
            {
                'action': 'extract_solid_stress',
                'domain': 'result_extraction',
                'description': f'Extract von Mises stress from {detected_element_type.upper()} elements',
                'params': {
                    'result_type': 'stress',
                    'element_type': detected_element_type  # AUTO-DETECTED!
                }
            }
        ],
        'inline_calculations': [
            {
                'action': 'calculate_safety_factor',
                'params': {
                    'input': 'max_von_mises',
                    'yield_strength': 276.0,  # MPa for Aluminum 6061-T6
                    'operation': 'divide'
                },
                'code_hint': 'safety_factor = 276.0 / max_von_mises'
            },
            {
                'action': 'negate_displacement',
                'params': {
                    'input': 'max_displacement',
                    'operation': 'negate'
                },
                'code_hint': 'neg_displacement = -max_displacement'
            }
        ],
        'post_processing_hooks': [],  # Using manual safety_factor_constraint hook
        'optimization': {
            'algorithm': 'TPE',
            'direction': 'minimize',  # Minimize neg_displacement = maximize displacement
            'design_variables': [
                {
                    'parameter': 'tip_thickness',
                    'min': 15.0,
                    'max': 25.0,
                    'units': 'mm'
                },
                {
                    'parameter': 'support_angle',
                    'min': 20.0,
                    'max': 40.0,
                    'units': 'degrees'
                }
            ]
        }
    }

    print_section("STEP 6: PIPELINE VALIDATION")

    print("Validating complete pipeline with baseline OP2...")
    print("(Dry-run test of extractors, calculations, hooks, objective)")
    print()

    validation_results = wizard.validate_pipeline(llm_workflow)

    all_passed = all(r.success for r in validation_results)

    print("Validation Results:")
    for result in validation_results:
        status = "[OK]" if result.success else "[FAIL]"
        print(f"  {status} {result.component}: {result.message.split(':')[-1].strip()}")
    print()

    if not all_passed:
        print("[FAILED] Pipeline validation failed!")
        print("Fix the issues above before running optimization.")
        sys.exit(1)

    print("[SUCCESS] All pipeline components validated!")
    print()

    print_section("STEP 7: OPTIMIZATION SETUP")

    print("Creating model updater and simulation runner...")

    # Model updater - UPDATE MODEL IN STUDY DIRECTORY
    updater = NXParameterUpdater(prt_file_path=prt_file)
    def model_updater(design_vars: dict):
        updater.update_expressions(design_vars)
        updater.save()

    # Simulation runner - RUN SIMULATIONS IN STUDY DIRECTORY
    solver = NXSolver(nastran_version='2412', use_journal=True)
    def simulation_runner(design_vars: dict) -> Path:
        # Pass expression values to NX journal so it can update geometry
        result = solver.run_simulation(sim_file, expression_updates=design_vars)
        return result['op2_file']

    print("  [OK] Model updater ready")
    print("  [OK] Simulation runner ready")
    print()

    print("Initializing LLM optimization runner...")

    # Save results in study/results directory
    runner = LLMOptimizationRunner(
        llm_workflow=llm_workflow,
        model_updater=model_updater,
        simulation_runner=simulation_runner,
        study_name='bracket_displacement_maximizing',
        output_dir=study_dir / "results"
    )

    print(f"  [OK] Output directory: {runner.output_dir}")
    print(f"  [OK] Extractors generated: {len(runner.extractors)}")
    print(f"  [OK] Inline calculations: {len(runner.inline_code)}")
    hook_summary = runner.hook_manager.get_summary()
    print(f"  [OK] Hooks loaded: {hook_summary['enabled_hooks']}")
    print()

    print_section("STEP 8: RUNNING OPTIMIZATION")

    print("Starting 20-trial optimization...")
    print("(This will take several minutes)")
    print()

    start_time = datetime.now()
    results = runner.run_optimization(n_trials=20)
    end_time = datetime.now()

    duration = (end_time - start_time).total_seconds()

    print()
    print_section("OPTIMIZATION COMPLETE!")

    print(f"Duration: {duration:.1f} seconds ({duration/60:.1f} minutes)")
    print()
    print("Best Design Found:")
    print(f"  - tip_thickness: {results['best_params']['tip_thickness']:.3f} mm")
    print(f"  - support_angle: {results['best_params']['support_angle']:.3f} degrees")
    print(f"  - Objective value: {results['best_value']:.6f}")
    print()

    # Show best trial details
    best_trial = results['history'][results['best_trial_number']]
    best_results = best_trial['results']
    best_calcs = best_trial['calculations']

    print("Best Design Performance:")
    print(f"  - Max displacement: {best_results.get('max_displacement', 0):.6f} mm")
    print(f"  - Max stress: {best_results.get('max_von_mises', 0):.3f} MPa")
    print(f"  - Safety factor: {best_calcs.get('safety_factor', 0):.3f}")
    print(f"  - Constraint: {'SATISFIED' if best_calcs.get('safety_factor', 0) >= 4.0 else 'VIOLATED'}")
    print()

    # Save results
    print("Saving results...")
    history_file, best_file, report_file = save_results(results, study_dir)
    print(f"  [OK] History: {history_file.name}")
    print(f"  [OK] Best design: {best_file.name}")
    print(f"  [OK] Report: {report_file.name}")
    print()

    print_section("STUDY COMPLETE!")
    print("Phase 3.3 Optimization Setup Wizard successfully guided the")
    print("complete optimization from setup through execution!")
    print()
    print(f"Study directory: {study_dir}")
    print(f"Results directory: {study_dir / 'results'}")
    print()


if __name__ == '__main__':
    main()
