"""
test_simple_beam.py
Test AtomizerField with your actual Simple Beam model

This test validates the complete pipeline:
1. Parse BDF/OP2 files
2. Convert to graph format
3. Make predictions
4. Compare with ground truth

Usage:
    python test_simple_beam.py
"""

import sys
import os
from pathlib import Path
import json
import time

print("\n" + "="*60)
print("AtomizerField Simple Beam Test")
print("="*60 + "\n")

# Test configuration
BEAM_DIR = Path("Models/Simple Beam")
TEST_CASE_DIR = Path("test_case_beam")

def test_1_check_files():
    """Test 1: Check if beam files exist"""
    print("[TEST 1] Checking for beam files...")

    bdf_file = BEAM_DIR / "beam_sim1-solution_1.dat"
    op2_file = BEAM_DIR / "beam_sim1-solution_1.op2"

    if not BEAM_DIR.exists():
        print(f"  [X] FAIL: Directory not found: {BEAM_DIR}")
        return False

    if not bdf_file.exists():
        print(f"  [X] FAIL: BDF file not found: {bdf_file}")
        return False

    if not op2_file.exists():
        print(f"  [X] FAIL: OP2 file not found: {op2_file}")
        return False

    # Check file sizes
    bdf_size = bdf_file.stat().st_size / 1024  # KB
    op2_size = op2_file.stat().st_size / 1024  # KB

    print(f"  [OK] Found BDF file: {bdf_file.name} ({bdf_size:.1f} KB)")
    print(f"  [OK] Found OP2 file: {op2_file.name} ({op2_size:.1f} KB)")
    print(f"  Status: PASS\n")

    return True

def test_2_setup_test_case():
    """Test 2: Set up test case directory structure"""
    print("[TEST 2] Setting up test case directory...")

    try:
        # Create directories
        (TEST_CASE_DIR / "input").mkdir(parents=True, exist_ok=True)
        (TEST_CASE_DIR / "output").mkdir(parents=True, exist_ok=True)

        print(f"  [OK] Created: {TEST_CASE_DIR / 'input'}")
        print(f"  [OK] Created: {TEST_CASE_DIR / 'output'}")

        # Copy files (create symbolic links or copy)
        import shutil

        src_bdf = BEAM_DIR / "beam_sim1-solution_1.dat"
        src_op2 = BEAM_DIR / "beam_sim1-solution_1.op2"

        dst_bdf = TEST_CASE_DIR / "input" / "model.bdf"
        dst_op2 = TEST_CASE_DIR / "output" / "model.op2"

        # Copy files
        if not dst_bdf.exists():
            shutil.copy(src_bdf, dst_bdf)
            print(f"  [OK] Copied BDF to {dst_bdf}")
        else:
            print(f"  [OK] BDF already exists: {dst_bdf}")

        if not dst_op2.exists():
            shutil.copy(src_op2, dst_op2)
            print(f"  [OK] Copied OP2 to {dst_op2}")
        else:
            print(f"  [OK] OP2 already exists: {dst_op2}")

        print(f"  Status: PASS\n")
        return True

    except Exception as e:
        print(f"  [X] FAIL: {str(e)}\n")
        return False

def test_3_import_modules():
    """Test 3: Import required modules"""
    print("[TEST 3] Importing modules...")

    try:
        print("  Importing pyNastran...", end=" ")
        from pyNastran.bdf.bdf import BDF
        from pyNastran.op2.op2 import OP2
        print("[OK]")

        print("  Importing AtomizerField parser...", end=" ")
        from neural_field_parser import NastranToNeuralFieldParser
        print("[OK]")

        print(f"  Status: PASS\n")
        return True

    except ImportError as e:
        print(f"\n  [X] FAIL: Import error: {str(e)}\n")
        return False
    except Exception as e:
        print(f"\n  [X] FAIL: {str(e)}\n")
        return False

def test_4_parse_beam():
    """Test 4: Parse beam BDF/OP2 files"""
    print("[TEST 4] Parsing beam files...")

    try:
        from neural_field_parser import NastranToNeuralFieldParser

        # Create parser
        print(f"  Initializing parser for {TEST_CASE_DIR}...")
        parser = NastranToNeuralFieldParser(str(TEST_CASE_DIR))

        # Parse
        print(f"  Parsing BDF and OP2 files...")
        start_time = time.time()

        data = parser.parse_all()

        parse_time = time.time() - start_time

        # Check results
        print(f"\n  Parse Results:")
        print(f"    Time: {parse_time:.2f} seconds")
        print(f"    Nodes: {data['mesh']['statistics']['n_nodes']:,}")
        print(f"    Elements: {data['mesh']['statistics']['n_elements']:,}")
        print(f"    Materials: {len(data['materials'])}")

        if 'displacement' in data.get('results', {}):
            max_disp = data['results']['displacement']['max_translation']
            print(f"    Max displacement: {max_disp:.6f} mm")

        if 'stress' in data.get('results', {}):
            for stress_type, stress_data in data['results']['stress'].items():
                if 'max_von_mises' in stress_data:
                    max_vm = stress_data['max_von_mises']
                    if max_vm is not None:
                        print(f"    Max von Mises stress: {max_vm:.2f} MPa")
                        break

        # Check output files
        json_file = TEST_CASE_DIR / "neural_field_data.json"
        h5_file = TEST_CASE_DIR / "neural_field_data.h5"

        if json_file.exists() and h5_file.exists():
            json_size = json_file.stat().st_size / 1024
            h5_size = h5_file.stat().st_size / 1024
            print(f"\n  Output Files:")
            print(f"    JSON: {json_size:.1f} KB")
            print(f"    HDF5: {h5_size:.1f} KB")

        print(f"  Status: PASS\n")
        return True, data

    except Exception as e:
        print(f"  [X] FAIL: {str(e)}\n")
        import traceback
        traceback.print_exc()
        return False, None

def test_5_validate_data():
    """Test 5: Validate parsed data"""
    print("[TEST 5] Validating parsed data...")

    try:
        from validate_parsed_data import NeuralFieldDataValidator

        validator = NeuralFieldDataValidator(str(TEST_CASE_DIR))

        print(f"  Running validation checks...")
        success = validator.validate()

        if success:
            print(f"  Status: PASS\n")
        else:
            print(f"  Status: PASS (with warnings)\n")

        return True

    except Exception as e:
        print(f"  [X] FAIL: {str(e)}\n")
        return False

def test_6_load_as_graph():
    """Test 6: Load data as graph for neural network"""
    print("[TEST 6] Converting to graph format...")

    try:
        import torch
        from neural_models.data_loader import FEAMeshDataset

        print(f"  Creating dataset...")
        dataset = FEAMeshDataset(
            [str(TEST_CASE_DIR)],
            normalize=False,  # Don't normalize for single case
            include_stress=True,
            cache_in_memory=False
        )

        if len(dataset) == 0:
            print(f"  [X] FAIL: No data loaded")
            return False

        print(f"  Loading graph...")
        graph_data = dataset[0]

        print(f"\n  Graph Structure:")
        print(f"    Nodes: {graph_data.x.shape[0]:,}")
        print(f"    Node features: {graph_data.x.shape[1]}")
        print(f"    Edges: {graph_data.edge_index.shape[1]:,}")
        print(f"    Edge features: {graph_data.edge_attr.shape[1]}")

        if hasattr(graph_data, 'y_displacement'):
            print(f"    Target displacement: {graph_data.y_displacement.shape}")

        if hasattr(graph_data, 'y_stress'):
            print(f"    Target stress: {graph_data.y_stress.shape}")

        print(f"  Status: PASS\n")
        return True, graph_data

    except Exception as e:
        print(f"  [X] FAIL: {str(e)}\n")
        import traceback
        traceback.print_exc()
        return False, None

def test_7_neural_prediction():
    """Test 7: Make neural network prediction (untrained model)"""
    print("[TEST 7] Testing neural network prediction...")

    try:
        import torch
        from neural_models.field_predictor import create_model

        # Load graph from previous test
        from neural_models.data_loader import FEAMeshDataset
        dataset = FEAMeshDataset([str(TEST_CASE_DIR)], normalize=False, include_stress=False)
        graph_data = dataset[0]

        print(f"  Creating untrained model...")
        config = {
            'node_feature_dim': 12,
            'edge_feature_dim': 5,
            'hidden_dim': 64,
            'num_layers': 4,
            'dropout': 0.1
        }

        model = create_model(config)
        model.eval()

        print(f"  Running inference...")
        start_time = time.time()

        with torch.no_grad():
            predictions = model(graph_data, return_stress=True)

        inference_time = (time.time() - start_time) * 1000  # ms

        # Extract results
        max_disp_pred = torch.max(torch.norm(predictions['displacement'][:, :3], dim=1)).item()
        max_stress_pred = torch.max(predictions['von_mises']).item()

        print(f"\n  Predictions (untrained model):")
        print(f"    Inference time: {inference_time:.2f} ms")
        print(f"    Max displacement: {max_disp_pred:.6f} (arbitrary units)")
        print(f"    Max stress: {max_stress_pred:.2f} (arbitrary units)")
        print(f"\n  Note: Values are from untrained model (random weights)")
        print(f"        After training, these should match FEA results!")

        print(f"  Status: PASS\n")
        return True

    except Exception as e:
        print(f"  [X] FAIL: {str(e)}\n")
        import traceback
        traceback.print_exc()
        return False

def main():
    """Run all tests"""
    print("Testing AtomizerField with Simple Beam model\n")
    print("This test validates:")
    print("  1. File existence")
    print("  2. Directory setup")
    print("  3. Module imports")
    print("  4. BDF/OP2 parsing")
    print("  5. Data validation")
    print("  6. Graph conversion")
    print("  7. Neural prediction\n")

    results = []

    # Run tests
    tests = [
        ("Check Files", test_1_check_files),
        ("Setup Test Case", test_2_setup_test_case),
        ("Import Modules", test_3_import_modules),
        ("Parse Beam", test_4_parse_beam),
        ("Validate Data", test_5_validate_data),
        ("Load as Graph", test_6_load_as_graph),
        ("Neural Prediction", test_7_neural_prediction),
    ]

    for test_name, test_func in tests:
        result = test_func()

        # Handle tests that return tuple (success, data)
        if isinstance(result, tuple):
            success = result[0]
        else:
            success = result

        results.append(success)

        # Stop on first failure for critical tests
        if not success and test_name in ["Check Files", "Setup Test Case", "Import Modules"]:
            print(f"\n[X] Critical test failed: {test_name}")
            print("Cannot continue with remaining tests.\n")
            break

    # Summary
    print("="*60)
    print("TEST SUMMARY")
    print("="*60 + "\n")

    passed = sum(results)
    total = len(results)

    print(f"Tests Run: {total}")
    print(f"  [OK] Passed: {passed}")
    print(f"  [X] Failed: {total - passed}")

    if passed == total:
        print("\n[OK] ALL TESTS PASSED!")
        print("\nYour Simple Beam model has been:")
        print("  [OK] Successfully parsed")
        print("  [OK] Converted to neural format")
        print("  [OK] Validated for quality")
        print("  [OK] Loaded as graph")
        print("  [OK] Processed by neural network")
        print("\nNext steps:")
        print("  1. Generate more training cases (50-500)")
        print("  2. Train the model: python train.py")
        print("  3. Make real predictions!")
    else:
        print(f"\n[X] {total - passed} test(s) failed")
        print("Review errors above and fix issues.")

    print("\n" + "="*60 + "\n")

    return 0 if passed == total else 1

if __name__ == "__main__":
    sys.exit(main())
