"""
test_suite.py
Master test orchestrator for AtomizerField

AtomizerField Testing Framework v1.0
Comprehensive validation from basic functionality to full neural FEA predictions.

Usage:
    python test_suite.py --quick      # 5-minute smoke tests
    python test_suite.py --physics    # Physics validation tests
    python test_suite.py --learning   # Learning capability tests
    python test_suite.py --full       # Complete test suite (1 hour)

Testing Strategy:
    1. Smoke Tests (5 min) → Verify basic functionality
    2. Physics Tests (15 min) → Validate physics constraints
    3. Learning Tests (30 min) → Confirm learning capability
    4. Integration Tests (1 hour) → Full system validation
"""

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

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

# Test results storage
TEST_RESULTS = {
    'timestamp': datetime.now().isoformat(),
    'tests': [],
    'summary': {
        'total': 0,
        'passed': 0,
        'failed': 0,
        'skipped': 0
    }
}


class TestRunner:
    """
    Test orchestrator that runs all tests in sequence
    """

    def __init__(self, mode='quick'):
        """
        Initialize test runner

        Args:
            mode (str): Testing mode ('quick', 'physics', 'learning', 'full')
        """
        self.mode = mode
        self.results_dir = Path('test_results')
        self.results_dir.mkdir(exist_ok=True)

        print(f"\n{'='*60}")
        print(f"AtomizerField Test Suite v1.0")
        print(f"Mode: {mode.upper()}")
        print(f"{'='*60}\n")

    def run_test(self, test_name, test_func, description):
        """
        Run a single test and record results

        Args:
            test_name (str): Name of test
            test_func (callable): Test function to run
            description (str): Test description

        Returns:
            bool: True if passed
        """
        print(f"[TEST] {test_name}")
        print(f"  Description: {description}")

        start_time = time.time()
        result = {
            'name': test_name,
            'description': description,
            'status': 'unknown',
            'duration': 0,
            'message': '',
            'metrics': {}
        }

        try:
            test_result = test_func()

            if test_result is None or test_result is True:
                result['status'] = 'PASS'
                result['message'] = 'Test passed successfully'
                print(f"  Status: [PASS]")
                TEST_RESULTS['summary']['passed'] += 1
            elif isinstance(test_result, dict):
                result['status'] = test_result.get('status', 'PASS')
                result['message'] = test_result.get('message', '')
                result['metrics'] = test_result.get('metrics', {})

                if result['status'] == 'PASS':
                    print(f"  Status: [PASS]")
                    TEST_RESULTS['summary']['passed'] += 1
                else:
                    print(f"  Status: [FAIL]")
                    print(f"  Reason: {result['message']}")
                    TEST_RESULTS['summary']['failed'] += 1
            else:
                result['status'] = 'FAIL'
                result['message'] = str(test_result)
                print(f"  Status: [FAIL]")
                TEST_RESULTS['summary']['failed'] += 1

        except Exception as e:
            result['status'] = 'FAIL'
            result['message'] = f"Exception: {str(e)}"
            print(f"  Status: [FAIL]")
            print(f"  Error: {str(e)}")
            TEST_RESULTS['summary']['failed'] += 1

        result['duration'] = time.time() - start_time
        print(f"  Duration: {result['duration']:.2f}s\n")

        TEST_RESULTS['tests'].append(result)
        TEST_RESULTS['summary']['total'] += 1

        return result['status'] == 'PASS'

    def run_smoke_tests(self):
        """
        Quick smoke tests (5 minutes)
        Verify basic functionality
        """
        print(f"\n{'='*60}")
        print("PHASE 1: SMOKE TESTS (5 minutes)")
        print(f"{'='*60}\n")

        from tests import test_synthetic

        # Test 1: Model creation
        self.run_test(
            "Model Creation",
            test_synthetic.test_model_creation,
            "Verify GNN model can be instantiated"
        )

        # Test 2: Forward pass
        self.run_test(
            "Forward Pass",
            test_synthetic.test_forward_pass,
            "Verify model can process dummy data"
        )

        # Test 3: Loss computation
        self.run_test(
            "Loss Computation",
            test_synthetic.test_loss_computation,
            "Verify loss functions work"
        )

    def run_physics_tests(self):
        """
        Physics validation tests (15 minutes)
        Ensure physics constraints work
        """
        print(f"\n{'='*60}")
        print("PHASE 2: PHYSICS VALIDATION (15 minutes)")
        print(f"{'='*60}\n")

        from tests import test_physics

        # Test 1: Cantilever beam
        self.run_test(
            "Cantilever Beam (Analytical)",
            test_physics.test_cantilever_analytical,
            "Compare with δ = FL³/3EI solution"
        )

        # Test 2: Equilibrium
        self.run_test(
            "Equilibrium Check",
            test_physics.test_equilibrium,
            "Verify force balance (∇·σ + f = 0)"
        )

        # Test 3: Energy conservation
        self.run_test(
            "Energy Conservation",
            test_physics.test_energy_conservation,
            "Verify strain energy = work done"
        )

    def run_learning_tests(self):
        """
        Learning capability tests (30 minutes)
        Confirm network can learn
        """
        print(f"\n{'='*60}")
        print("PHASE 3: LEARNING CAPABILITY (30 minutes)")
        print(f"{'='*60}\n")

        from tests import test_learning

        # Test 1: Memorization
        self.run_test(
            "Memorization Test",
            test_learning.test_memorization,
            "Can network memorize small dataset?"
        )

        # Test 2: Interpolation
        self.run_test(
            "Interpolation Test",
            test_learning.test_interpolation,
            "Can network interpolate between training points?"
        )

        # Test 3: Pattern recognition
        self.run_test(
            "Pattern Recognition",
            test_learning.test_pattern_recognition,
            "Does network learn thickness → stress relationship?"
        )

    def run_integration_tests(self):
        """
        Full integration tests (1 hour)
        Complete system validation
        """
        print(f"\n{'='*60}")
        print("PHASE 4: INTEGRATION TESTS (1 hour)")
        print(f"{'='*60}\n")

        from tests import test_predictions

        # Test 1: Parser validation
        self.run_test(
            "Parser Validation",
            test_predictions.test_parser,
            "Verify data parsing works correctly"
        )

        # Test 2: Training pipeline
        self.run_test(
            "Training Pipeline",
            test_predictions.test_training,
            "Verify complete training workflow"
        )

        # Test 3: Prediction accuracy
        self.run_test(
            "Prediction Accuracy",
            test_predictions.test_prediction_accuracy,
            "Compare neural vs FEA predictions"
        )

    def print_summary(self):
        """Print test summary"""
        summary = TEST_RESULTS['summary']

        print(f"\n{'='*60}")
        print("TEST SUMMARY")
        print(f"{'='*60}\n")

        total = summary['total']
        passed = summary['passed']
        failed = summary['failed']

        pass_rate = (passed / total * 100) if total > 0 else 0

        print(f"Total Tests: {total}")
        print(f"  + Passed: {passed}")
        print(f"  - Failed: {failed}")
        print(f"  Pass Rate: {pass_rate:.1f}%\n")

        if failed == 0:
            print("[SUCCESS] ALL TESTS PASSED - SYSTEM READY!")
        else:
            print(f"[ERROR] {failed} TEST(S) FAILED - REVIEW REQUIRED")

        print(f"\n{'='*60}\n")

    def save_results(self):
        """Save test results to JSON"""
        results_file = self.results_dir / f'test_results_{self.mode}_{int(time.time())}.json'

        with open(results_file, 'w') as f:
            json.dump(TEST_RESULTS, f, indent=2)

        print(f"Results saved to: {results_file}")

    def run(self):
        """
        Run test suite based on mode
        """
        start_time = time.time()

        if self.mode == 'quick':
            self.run_smoke_tests()

        elif self.mode == 'physics':
            self.run_smoke_tests()
            self.run_physics_tests()

        elif self.mode == 'learning':
            self.run_smoke_tests()
            self.run_physics_tests()
            self.run_learning_tests()

        elif self.mode == 'full':
            self.run_smoke_tests()
            self.run_physics_tests()
            self.run_learning_tests()
            self.run_integration_tests()

        total_time = time.time() - start_time

        # Print summary
        self.print_summary()

        print(f"Total testing time: {total_time/60:.1f} minutes\n")

        # Save results
        self.save_results()

        # Return exit code
        return 0 if TEST_RESULTS['summary']['failed'] == 0 else 1


def main():
    """Main entry point"""
    parser = argparse.ArgumentParser(
        description='AtomizerField Test Suite',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    # Quick smoke tests (5 min)
    python test_suite.py --quick

    # Physics validation (15 min)
    python test_suite.py --physics

    # Learning tests (30 min)
    python test_suite.py --learning

    # Full test suite (1 hour)
    python test_suite.py --full
        """
    )

    parser.add_argument(
        '--quick',
        action='store_true',
        help='Run quick smoke tests (5 minutes)'
    )

    parser.add_argument(
        '--physics',
        action='store_true',
        help='Run physics validation tests (15 minutes)'
    )

    parser.add_argument(
        '--learning',
        action='store_true',
        help='Run learning capability tests (30 minutes)'
    )

    parser.add_argument(
        '--full',
        action='store_true',
        help='Run complete test suite (1 hour)'
    )

    args = parser.parse_args()

    # Determine mode
    if args.full:
        mode = 'full'
    elif args.learning:
        mode = 'learning'
    elif args.physics:
        mode = 'physics'
    elif args.quick:
        mode = 'quick'
    else:
        # Default to quick if no mode specified
        mode = 'quick'
        print("No mode specified, defaulting to --quick")

    # Run tests
    runner = TestRunner(mode=mode)
    exit_code = runner.run()

    sys.exit(exit_code)


if __name__ == "__main__":
    main()
