#!/usr/bin/env python3
"""Final validation script for Senior Architecture.

Validates:
1. All new modules import successfully
2. SQLite database is accessible
3. Metadata store works without numpy
4. ArrangementRecorder state machine functions
5. LiveBridge connects to Ableton
6. Integration coordinator initializes
7. End-to-end workflow executes
"""

import sys
import os
import json
import traceback
import argparse
import tempfile
import sqlite3
from datetime import datetime
from pathlib import Path

class ValidationRunner:
    """Runs all validations and generates report."""
    
    def __init__(self, verbose=False):
        self.verbose = verbose
        self.results = []
        self.errors = []
        self.warnings = []
        self.fix_suggestions = {}
    
    def run_all(self, selective=None):
        """Execute all validation checks."""
        all_checks = [
            ("Module Imports", self._check_imports),
            ("SQLite Database", self._check_database),
            ("Metadata Store", self._check_metadata_store),
            ("Numpy Independence", self._check_numpy_independence),
            ("ArrangementRecorder", self._check_arrangement_recorder),
            ("LiveBridge", self._check_live_bridge),
            ("Integration", self._check_integration),
            ("Ableton Connection", self._check_ableton_connection),
        ]
        
        # Filter if selective checks requested
        if selective:
            all_checks = [
                (name, func) for name, func in all_checks
                if name.lower() in selective
            ]
            if not all_checks:
                print(f"Error: No checks match {selective}")
                return False
        
        for name, check_func in all_checks:
            if self.verbose:
                print(f"\nRunning: {name}...")
            
            try:
                result = check_func()
                self.results.append({
                    "name": name,
                    "status": "PASS" if result else "FAIL",
                    "timestamp": datetime.now().isoformat()
                })
                if not result:
                    self.fix_suggestions[name] = self._generate_fix_suggestion(name, Exception("Check returned False"))
                    
            except Exception as e:
                self.results.append({
                    "name": name,
                    "status": "ERROR",
                    "error": str(e),
                    "traceback": traceback.format_exc()
                })
                self.errors.append((name, e))
                self.fix_suggestions[name] = self._generate_fix_suggestion(name, e)
        
        return self._generate_report()
    
    def _check_imports(self):
        """Check all new modules import successfully."""
        imports = [
            'mcp_server.engines.metadata_store',
            'mcp_server.engines.abstract_analyzer',
            'mcp_server.engines.arrangement_recorder',
            'mcp_server.engines.live_bridge',
            'mcp_server.integration',
        ]
        
        for module in imports:
            try:
                __import__(module)
                if self.verbose:
                    print(f"  [OK] Imported {module}")
            except ImportError as e:
                if self.verbose:
                    print(f"  [FAIL] Failed to import {module}: {e}")
                raise ImportError(f"Failed to import {module}: {e}")
        
        return True
    
    def _check_database(self):
        """Check SQLite database is accessible."""
        try:
            # Try to create in-memory database
            conn = sqlite3.connect(':memory:')
            conn.execute('SELECT 1')
            conn.close()
            if self.verbose:
                print("  [OK] SQLite in-memory database created")
            return True
        except Exception as e:
            if self.verbose:
                print(f"  [FAIL] SQLite error: {e}")
            raise
    
    def _check_metadata_store(self):
        """Check metadata store works without numpy."""
        from mcp_server.engines.metadata_store import SampleMetadataStore, SampleFeatures
        
        # Create temp database
        fd, path = tempfile.mkstemp(suffix='.db')
        try:
            store = SampleMetadataStore(path)
            store.init_database()
            if self.verbose:
                print(f"  [OK] Database initialized at {path}")
            
            # Save sample features
            features = SampleFeatures(
                path="/test/sample.wav",
                bpm=95.0,
                key="Am",
                duration=2.0,
                rms=-12.0,
                spectral_centroid=1000.0,
                spectral_rolloff=5000.0,
                zero_crossing_rate=0.1,
                mfcc_1=0.1, mfcc_2=0.1, mfcc_3=0.1, mfcc_4=0.1,
                mfcc_5=0.1, mfcc_6=0.1, mfcc_7=0.1, mfcc_8=0.1,
                mfcc_9=0.1, mfcc_10=0.1, mfcc_11=0.1, mfcc_12=0.1, mfcc_13=0.1
            )
            store.save_sample_features("/test/sample.wav", features)
            if self.verbose:
                print("  [OK] Sample features saved")
            
            # Retrieve
            retrieved = store.get_sample_features("/test/sample.wav")
            assert retrieved is not None, "Retrieved features should not be None"
            assert retrieved.bpm == 95.0, f"BPM should be 95.0, got {retrieved.bpm}"
            if self.verbose:
                print("  [OK] Sample features retrieved correctly")
            
            return True
        finally:
            try:
                os.close(fd)
                os.unlink(path)
            except:
                pass
    
    def _check_numpy_independence(self):
        """Verify core functionality works without numpy."""
        # Check if numpy is available
        try:
            import numpy
            numpy_available = True
        except ImportError:
            numpy_available = False
        
        if not numpy_available:
            if self.verbose:
                print("  [INFO] Numpy not available - skipping independence test")
            return True  # Already independent by absence
        
        # Temporarily hide numpy
        import sys
        numpy_backup = sys.modules.pop('numpy', None)
        
        try:
            # Re-import metadata store (should work without numpy)
            if 'mcp_server.engines.metadata_store' in sys.modules:
                del sys.modules['mcp_server.engines.metadata_store']
            
            from mcp_server.engines.metadata_store import SampleMetadataStore
            if self.verbose:
                print("  [OK] Metadata store imports without numpy")
            return True
        finally:
            # Restore numpy
            if numpy_backup:
                sys.modules['numpy'] = numpy_backup
    
    def _check_arrangement_recorder(self):
        """Check ArrangementRecorder state machine."""
        from mcp_server.engines.arrangement_recorder import (
            ArrangementRecorder, RecordingState, RecordingConfig
        )
        
        # Create mock objects
        class MockSong:
            def __init__(self):
                self.tempo = 95.0
                self.current_song_time = 0.0
                self.arrangement_overdub = False
                self.is_playing = False
        
        class MockConn:
            pass
        
        recorder = ArrangementRecorder(MockSong(), MockConn())
        
        # Check initial state
        assert recorder.get_state() == RecordingState.IDLE, \
            f"Initial state should be IDLE, got {recorder.get_state()}"
        
        if self.verbose:
            print("  [OK] Initial state is IDLE")
        
        # Check config can be created
        config = RecordingConfig(
            duration_bars=4.0, 
            tempo=95.0,
            start_bar=0.0,
            scene_index=0
        )
        assert config.duration_bars == 4.0, \
            f"Duration bars mismatch: expected 4.0, got {config.duration_bars}"
        assert config.tempo == 95.0, \
            f"Tempo mismatch: expected 95.0, got {config.tempo}"
        
        if self.verbose:
            print("  [OK] RecordingConfig created successfully")
            print("  [OK] State transitions available:")
            for state in RecordingState:
                print(f"    - {state.name}")
        
        return True
    
    def _check_live_bridge(self):
        """Check LiveBridge initializes."""
        from mcp_server.engines.live_bridge import AbletonLiveBridge
        
        class MockSong:
            pass
        
        class MockConn:
            pass
        
        bridge = AbletonLiveBridge(MockSong(), MockConn())
        assert bridge is not None, "LiveBridge should initialize"
        
        if self.verbose:
            print("  [OK] LiveBridge initialized")
            print("  [OK] Available methods:")
            methods = [m for m in dir(bridge) if not m.startswith('_')]
            for method in methods[:5]:
                print(f"    - {method}")
            if len(methods) > 5:
                print(f"    ... and {len(methods) - 5} more")
        
        return True
    
    def _check_integration(self):
        """Check integration coordinator."""
        from mcp_server.integration import SeniorArchitectureCoordinator
        
        class MockSong:
            pass
        
        class MockConn:
            pass
        
        coord = SeniorArchitectureCoordinator(MockSong(), MockConn())
        assert coord is not None, "Coordinator should initialize"
        
        if self.verbose:
            print("  [OK] SeniorArchitectureCoordinator initialized")
        
        return True
    
    def _check_ableton_connection(self):
        """Check Ableton Live is accessible."""
        # Try to ping Ableton via existing wrapper
        try:
            import socket
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(2)
            result = s.connect_ex(('127.0.0.1', 9877))
            s.close()
            
            if result == 0:
                if self.verbose:
                    print("  [OK] Ableton Live TCP server responding on port 9877")
                return True
            else:
                if self.verbose:
                    print(f"  [WARN] Ableton Live not available on port 9877 (error code: {result})")
                    print("  [WARN] This is OK - Ableton may not be running")
                self.warnings.append("Ableton not running - some checks skipped")
                return False  # Not a failure, just not available
        except Exception as e:
            if self.verbose:
                print(f"  [WARN] Connection check error: {e}")
            self.warnings.append(f"Connection check: {e}")
            return False
    
    def _generate_fix_suggestion(self, check_name, error):
        """Generate fix suggestion for a failed check."""
        suggestions = {
            "Module Imports": """
Fix: Ensure all new modules exist in mcp_server/engines/:
  1. metadata_store.py - SQLite-based sample metadata
  2. abstract_analyzer.py - Hybrid feature extraction
  3. arrangement_recorder.py - Recording state machine
  4. live_bridge.py - Direct Ableton API execution
  5. integration.py - Coordinator

Run: python -m py_compile on each file to check for syntax errors.
""",
            "SQLite Database": """
Fix: SQLite is part of Python standard library. If this fails:
  1. Check Python installation: python --version
  2. Verify sqlite3 module: python -c "import sqlite3; print(sqlite3.version)"
  3. Reinstall Python if necessary
""",
            "Metadata Store": """
Fix: If metadata store fails:
  1. Check database schema in metadata_store.py
  2. Verify SampleFeatures dataclass definition
  3. Check for SQL syntax errors in init_database()
  4. Ensure proper error handling in save/get methods
""",
            "Numpy Independence": """
Fix: If numpy independence fails:
  1. Ensure metadata_store.py has no 'import numpy' at top level
  2. Move numpy imports inside functions that need them
  3. Use type checking (TYPE_CHECKING) for numpy type hints
  4. Provide fallback implementations for numpy operations
""",
            "ArrangementRecorder": """
Fix: If ArrangementRecorder fails:
  1. Check RecordingState enum definition
  2. Verify RecordingConfig dataclass
  3. Ensure proper mock objects for testing
  4. Check state transition logic
""",
            "LiveBridge": """
Fix: If LiveBridge fails:
  1. Check Ableton Live is running with Remote Script loaded
  2. Verify TCP connection on port 9877
  3. Check Live API access in __init__.py
  4. Verify song and connection objects are properly passed
""",
            "Integration": """
Fix: If Integration coordinator fails:
  1. Check all dependencies are imported correctly
  2. Verify mode detection logic (numpy/librosa availability)
  3. Check for circular imports
  4. Ensure proper initialization of sub-components
""",
            "Ableton Connection": """
Fix: If Ableton connection fails:
  1. Start Ableton Live 12 Suite
  2. Verify AbletonMCP_AI is selected in Preferences > MIDI > Control Surface
  3. Check that __init__.py is in correct location
  4. Verify port 9877 is not blocked by firewall
  5. Check Ableton log for errors
""",
        }
        
        return suggestions.get(check_name, f"""
Fix: General troubleshooting for {check_name}:
  1. Check error traceback above
  2. Verify file exists and has no syntax errors
  3. Check import paths are correct
  4. Run with --verbose for more details
  5. Check AGENTS.md for architecture details
""")
    
    def _generate_report(self):
        """Generate validation report."""
        total = len(self.results)
        passed = sum(1 for r in self.results if r['status'] == 'PASS')
        failed = sum(1 for r in self.results if r['status'] == 'FAIL')
        errors = sum(1 for r in self.results if r['status'] == 'ERROR')
        
        report = {
            "timestamp": datetime.now().isoformat(),
            "summary": {
                "total": total,
                "passed": passed,
                "failed": failed,
                "errors": errors,
                "success_rate": passed / total if total > 0 else 0
            },
            "results": self.results,
            "warnings": self.warnings,
            "errors": [{"check": name, "error": str(e)} for name, e in self.errors]
        }
        
        # Print to console
        print("\n" + "="*60)
        print("SENIOR ARCHITECTURE VALIDATION REPORT")
        print("="*60)
        print(f"Timestamp: {report['timestamp']}")
        print(f"Passed: {passed}/{total}")
        print(f"Failed: {failed}/{total}")
        print(f"Errors: {errors}/{total}")
        print(f"Success Rate: {report['summary']['success_rate']:.1%}")
        
        if self.warnings:
            print(f"\nWarnings: {len(self.warnings)}")
            for warning in self.warnings:
                print(f"  [WARN] {warning}")
        
        print("-"*60)
        
        for result in self.results:
            status_icon = "[PASS]" if result['status'] == 'PASS' else "[FAIL]" if result['status'] == 'FAIL' else "[WARN]"
            print(f"{status_icon} {result['name']}: {result['status']}")
            
            if self.verbose and 'error' in result and result['error']:
                print(f"  Error: {result['error'][:100]}...")
        
        print("="*60)
        
        # Print fix suggestions for failed checks
        if self.fix_suggestions:
            print("\n" + "="*60)
            print("FIX SUGGESTIONS")
            print("="*60)
            for check_name, suggestion in self.fix_suggestions.items():
                print(f"\n{check_name}:")
                print(suggestion)
            print("="*60)
        
        # Save JSON report
        report_path = "senior_validation_report.json"
        with open(report_path, 'w') as f:
            json.dump(report, f, indent=2)
        print(f"\nFull report saved to: {report_path}")
        
        # Also save fix suggestions
        if self.fix_suggestions:
            fixes_path = "senior_validation_fixes.txt"
            with open(fixes_path, 'w') as f:
                f.write("SENIOR ARCHITECTURE - FIX SUGGESTIONS\n")
                f.write("="*60 + "\n\n")
                for check_name, suggestion in self.fix_suggestions.items():
                    f.write(f"{check_name}:\n")
                    f.write(suggestion + "\n")
            print(f"Fix suggestions saved to: {fixes_path}")
        
        return report['summary']['success_rate'] >= 0.8  # 80% pass threshold

def main():
    """Main entry point."""
    parser = argparse.ArgumentParser(
        description="Validate Senior Architecture implementation",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python validate_senior.py              # Run all checks
  python validate_senior.py -v           # Run with verbose output
  python validate_senior.py --list       # List available checks
  python validate_senior.py -c imports integration  # Run specific checks
        """
    )
    
    parser.add_argument(
        '-v', '--verbose',
        action='store_true',
        help='Enable verbose output with detailed information'
    )
    
    parser.add_argument(
        '-c', '--checks',
        nargs='+',
        metavar='CHECK',
        help='Run only specific checks (space-separated names)'
    )
    
    parser.add_argument(
        '--list',
        action='store_true',
        help='List all available checks and exit'
    )
    
    parser.add_argument(
        '--threshold',
        type=float,
        default=0.8,
        help='Success rate threshold (default: 0.8 = 80 percent)'
    )
    
    args = parser.parse_args()
    
    if args.list:
        print("Available validation checks:")
        checks = [
            "Module Imports - Check all new modules import successfully",
            "SQLite Database - Check SQLite database is accessible",
            "Metadata Store - Check metadata store works without numpy",
            "Numpy Independence - Verify core functionality works without numpy",
            "ArrangementRecorder - Check ArrangementRecorder state machine",
            "LiveBridge - Check LiveBridge initializes",
            "Integration - Check integration coordinator",
            "Ableton Connection - Check Ableton Live is accessible",
        ]
        for check in checks:
            print(f"  - {check}")
        return 0
    
    # Normalize check names
    selective = None
    if args.checks:
        selective = [name.lower().replace("_", " ") for name in args.checks]
        print(f"Running selective validation: {selective}")
    
    runner = ValidationRunner(verbose=args.verbose)
    success = runner.run_all(selective=selective)
    
    # Apply custom threshold
    if selective:
        total = len(runner.results)
        passed = sum(1 for r in runner.results if r['status'] == 'PASS')
        success_rate = passed / total if total > 0 else 0
        success = success_rate >= args.threshold
    
    if success:
        print("\n[PASS] Senior Architecture validation PASSED")
        print(f"   Success rate meets {args.threshold:.0%} threshold")
        return 0
    else:
        print("\n[FAIL] Senior Architecture validation FAILED")
        print(f"   Success rate below {args.threshold:.0%} threshold")
        print("\nTo fix issues:")
        print("  1. Check senior_validation_fixes.txt for suggestions")
        print("  2. Run with --verbose to see detailed errors")
        print("  3. Review AGENTS.md for architecture details")
        return 1

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