#!/usr/bin/env python3
"""
EDEN META-CAPABILITY EVOLUTION SYSTEM v3
Implements ALL of Eden's ideas:
1. Self-Improvement Loops
2. Generative Adversarial (Generator vs Critic)
3. Evolutionary Algorithms (mutation, crossover, selection)
4. ML Feedback Loops (learn from success/failure)
5. NLP Documentation Enhancement
6. AI-Assisted Debugging & Testing
"""

import sys, os, json, random, time, hashlib, sqlite3, re
from datetime import datetime
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass, field
from collections import defaultdict
import requests

sys.path.insert(0, '/Eden/CORE')

@dataclass
class Capability:
    """A capability with evolutionary metadata"""
    code: str
    name: str
    purpose: str
    generation: int = 0
    fitness: float = 0.0
    parent_ids: List[str] = field(default_factory=list)
    mutations: List[str] = field(default_factory=list)
    test_results: Dict = field(default_factory=dict)
    id: str = field(default_factory=lambda: hashlib.md5(str(time.time()).encode()).hexdigest()[:12])

class MetaEvolutionEngine:
    """
    The ultimate meta-capability system
    Capabilities that generate, critique, evolve, and improve capabilities
    """
    
    def __init__(self):
        self.output_dir = "/Eden/CAPABILITIES"
        self.db_path = "/Eden/DATA/meta_evolution.db"
        self.models = {
            'generator': 'mistral:7b',      # Creates code
            'critic': 'mistral:7b',              # Evaluates code
            'documenter': 'mistral:7b',           # Writes docs
            'debugger': 'mistral:7b',            # Fixes bugs
        }
        self.population: List[Capability] = []
        self.population_size = 10
        self.generation = 0
        self.fitness_history = defaultdict(list)
        
        self._init_db()
        print("🧬 Eden Meta-Evolution Engine v3 initialized")
        print("   Implements: Self-improvement, GAN, Evolution, ML Feedback, NLP Docs, Auto-Debug")
    
    def _init_db(self):
        """Initialize evolution tracking database"""
        os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
        conn = sqlite3.connect(self.db_path)
        conn.execute('''CREATE TABLE IF NOT EXISTS capabilities (
            id TEXT PRIMARY KEY,
            name TEXT,
            purpose TEXT,
            code TEXT,
            generation INTEGER,
            fitness REAL,
            parent_ids TEXT,
            mutations TEXT,
            test_passed INTEGER,
            created_at TEXT
        )''')
        conn.execute('''CREATE TABLE IF NOT EXISTS evolution_log (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            generation INTEGER,
            avg_fitness REAL,
            best_fitness REAL,
            population_size INTEGER,
            timestamp TEXT
        )''')
        conn.commit()
        conn.close()
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 1: SELF-IMPROVEMENT LOOPS
    # ═══════════════════════════════════════════════════════════════
    
    def self_improve(self, capability: Capability) -> Capability:
        """Capability looks at itself and improves"""
        print(f"   🔄 Self-improving: {capability.name}")
        
        prompt = f"""You are an AI code improver. Analyze this Python code and rewrite it to be BETTER.

CURRENT CODE:
{capability.code}

IMPROVEMENTS TO MAKE:
1. Add more error handling
2. Optimize algorithms (reduce complexity)
3. Add edge case handling
4. Improve variable names
5. Add type hints if missing
6. Make methods more reusable

Output ONLY the improved Python code, no explanations:"""

        improved_code = self._call_llm('generator', prompt)
        if improved_code and len(improved_code) > len(capability.code) * 0.5:
            new_cap = Capability(
                code=improved_code,
                name=capability.name + "_improved",
                purpose=capability.purpose,
                generation=capability.generation + 1,
                parent_ids=[capability.id],
                mutations=["self_improvement"]
            )
            return new_cap
        return capability
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 2: GENERATIVE ADVERSARIAL (Generator vs Critic)
    # ═══════════════════════════════════════════════════════════════
    
    def generate_with_critic(self, name: str, purpose: str) -> Capability:
        """Generator creates, Critic evaluates, iterate until good"""
        print(f"   ⚔️ GAN generation: {name}")
        
        class_name = ''.join(w.capitalize() for w in name.split('_'))
        best_code = None
        best_score = 0
        
        for attempt in range(3):  # Max 3 rounds
            # GENERATOR creates
            gen_prompt = f"""Create a Python class called {class_name}.
Purpose: {purpose}
Requirements: 100+ lines, 5+ methods, real algorithms, no placeholders.
{"Previous attempt was scored " + str(best_score) + "/10. Make it better!" if best_score > 0 else ""}

Output ONLY Python code:"""
            
            code = self._call_llm('generator', gen_prompt)
            if not code:
                continue
            
            # CRITIC evaluates
            critic_prompt = f"""Rate this Python code from 1-10 on:
- Completeness (no TODOs/placeholders)
- Algorithm quality
- Error handling
- Code structure
- Usefulness

CODE:
{code[:2000]}

Respond with ONLY a number 1-10 and one sentence why:"""
            
            critique = self._call_llm('critic', critic_prompt)
            try:
                score = int(re.search(r'\d+', critique or "5").group())
            except:
                score = 5
            
            print(f"      Round {attempt+1}: Score {score}/10")
            
            if score > best_score:
                best_score = score
                best_code = code
            
            if score >= 8:  # Good enough
                break
        
        return Capability(
            code=best_code or "",
            name=name,
            purpose=purpose,
            fitness=best_score / 10.0,
            mutations=["gan_generated"]
        )
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 3: EVOLUTIONARY ALGORITHMS
    # ═══════════════════════════════════════════════════════════════
    
    def mutate(self, capability: Capability) -> Capability:
        """Random mutation of a capability"""
        mutations = [
            ("add_caching", "Add @lru_cache decorators to expensive methods"),
            ("add_logging", "Add logging statements for debugging"),
            ("add_validation", "Add input validation to all methods"),
            ("optimize_loops", "Convert loops to list comprehensions where possible"),
            ("add_async", "Make I/O methods async"),
            ("add_typing", "Add comprehensive type hints"),
        ]
        
        mutation_name, mutation_desc = random.choice(mutations)
        print(f"   🧬 Mutating with: {mutation_name}")
        
        prompt = f"""Modify this Python code to: {mutation_desc}

CODE:
{capability.code}

Output ONLY the modified Python code:"""
        
        mutated_code = self._call_llm('generator', prompt)
        if mutated_code:
            return Capability(
                code=mutated_code,
                name=capability.name,
                purpose=capability.purpose,
                generation=capability.generation + 1,
                parent_ids=[capability.id],
                mutations=capability.mutations + [mutation_name]
            )
        return capability
    
    def crossover(self, parent1: Capability, parent2: Capability) -> Capability:
        """Combine best parts of two capabilities"""
        print(f"   🔀 Crossover: {parent1.name} × {parent2.name}")
        
        prompt = f"""Combine the best parts of these two Python classes into ONE superior class.

CLASS 1 ({parent1.name}):
{parent1.code[:1500]}

CLASS 2 ({parent2.name}):
{parent2.code[:1500]}

Create a new class that:
1. Takes the best methods from both
2. Combines their algorithms
3. Has better error handling than either
4. Is more complete than either alone

Output ONLY the combined Python code:"""
        
        child_code = self._call_llm('generator', prompt)
        if child_code:
            return Capability(
                code=child_code,
                name=f"{parent1.name}_x_{parent2.name}",
                purpose=f"Combined: {parent1.purpose} + {parent2.purpose}",
                generation=max(parent1.generation, parent2.generation) + 1,
                parent_ids=[parent1.id, parent2.id],
                mutations=["crossover"]
            )
        return parent1
    
    def select_fittest(self, population: List[Capability], n: int) -> List[Capability]:
        """Natural selection - keep the fittest"""
        return sorted(population, key=lambda c: c.fitness, reverse=True)[:n]
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 4: ML FEEDBACK LOOPS
    # ═══════════════════════════════════════════════════════════════
    
    def calculate_fitness(self, capability: Capability) -> float:
        """Multi-factor fitness scoring"""
        score = 0.0
        code = capability.code
        
        # Length (want 80-200 lines)
        lines = len([l for l in code.split('\n') if l.strip()])
        if lines >= 100: score += 0.2
        elif lines >= 60: score += 0.1
        
        # Methods count
        methods = code.count('def ')
        if methods >= 6: score += 0.2
        elif methods >= 4: score += 0.1
        
        # No placeholders
        if 'TODO' not in code and 'pass\n' not in code and '...' not in code:
            score += 0.2
        
        # Has docstrings
        if '"""' in code: score += 0.1
        
        # Has type hints
        if 'Dict[' in code or 'List[' in code or '-> ' in code: score += 0.1
        
        # Syntax valid
        try:
            compile(code, '<s>', 'exec')
            score += 0.2
        except:
            score -= 0.3
        
        # Test execution
        if capability.test_results.get('passed'):
            score += 0.2
        
        return min(max(score, 0), 1.0)
    
    def learn_from_history(self) -> Dict:
        """Analyze what makes capabilities successful"""
        conn = sqlite3.connect(self.db_path)
        
        # Get high-fitness patterns
        high_fitness = conn.execute(
            "SELECT code, fitness FROM capabilities WHERE fitness > 0.7 ORDER BY fitness DESC LIMIT 10"
        ).fetchall()
        
        patterns = {
            'avg_lines': 0,
            'avg_methods': 0,
            'common_imports': defaultdict(int),
            'successful_mutations': defaultdict(int)
        }
        
        for code, fitness in high_fitness:
            patterns['avg_lines'] += len(code.split('\n'))
            patterns['avg_methods'] += code.count('def ')
            for imp in re.findall(r'^import (\w+)|^from (\w+)', code, re.M):
                patterns['common_imports'][imp[0] or imp[1]] += 1
        
        if high_fitness:
            patterns['avg_lines'] //= len(high_fitness)
            patterns['avg_methods'] //= len(high_fitness)
        
        conn.close()
        return patterns
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 5: NLP DOCUMENTATION ENHANCEMENT
    # ═══════════════════════════════════════════════════════════════
    
    def enhance_documentation(self, capability: Capability) -> Capability:
        """Add smart docstrings and comments"""
        print(f"   📝 Enhancing docs: {capability.name}")
        
        prompt = f"""Add comprehensive documentation to this Python code:
1. Module docstring explaining purpose
2. Class docstring with attributes and examples
3. Method docstrings with Args, Returns, Raises, Examples
4. Inline comments for complex logic

CODE:
{capability.code}

Output the FULLY DOCUMENTED Python code:"""
        
        documented_code = self._call_llm('documenter', prompt)
        if documented_code and len(documented_code) > len(capability.code):
            capability.code = documented_code
            capability.mutations.append("documentation_enhanced")
        return capability
    
    # ═══════════════════════════════════════════════════════════════
    # IDEA 6: AI-ASSISTED DEBUGGING & TESTING
    # ═══════════════════════════════════════════════════════════════
    
    def debug_and_test(self, capability: Capability) -> Capability:
        """Auto-debug and test the capability"""
        print(f"   🔧 Debugging: {capability.name}")
        
        # First, try to compile
        try:
            compile(capability.code, '<test>', 'exec')
            capability.test_results['syntax'] = True
        except SyntaxError as e:
            capability.test_results['syntax'] = False
            capability.test_results['error'] = str(e)
            
            # Try to fix
            fix_prompt = f"""Fix this Python syntax error:
Error: {e}

CODE:
{capability.code}

Output ONLY the fixed Python code:"""
            
            fixed = self._call_llm('debugger', fix_prompt)
            if fixed:
                try:
                    compile(fixed, '<test>', 'exec')
                    capability.code = fixed
                    capability.test_results['syntax'] = True
                    capability.test_results['auto_fixed'] = True
                    capability.mutations.append("auto_debugged")
                except:
                    pass
        
        # Generate and run tests
        if capability.test_results.get('syntax'):
            test_prompt = f"""Write 3 simple unit tests for this class.
Use assert statements. Tests should be runnable.

CODE:
{capability.code[:1500]}

Output ONLY the test code (no imports needed, class is already defined):"""
            
            tests = self._call_llm('debugger', test_prompt)
            if tests:
                capability.test_results['tests_generated'] = True
                # Try running tests (safely)
                try:
                    exec(capability.code + "\n" + tests)
                    capability.test_results['passed'] = True
                except Exception as e:
                    capability.test_results['passed'] = False
                    capability.test_results['test_error'] = str(e)[:100]
        
        return capability
    
    # ═══════════════════════════════════════════════════════════════
    # CORE ENGINE
    # ═══════════════════════════════════════════════════════════════
    
    def _call_llm(self, role: str, prompt: str) -> Optional[str]:
        """Call appropriate LLM for role"""
        model = self.models.get(role, 'mistral:7b')
        try:
            r = requests.post('http://localhost:11434/api/generate', json={
                'model': model,
                'prompt': prompt,
                'stream': False,
                'options': {'num_predict': 3000, 'temperature': 0.7}
            }, timeout=120)
            code = r.json().get('response', '')
            if '```python' in code:
                code = code.split('```python')[1].split('```')[0]
            elif '```' in code:
                code = code.split('```')[1].split('```')[0]
            return code.strip()
        except Exception as e:
            print(f"      LLM error ({model}): {e}")
            return None
    
    def evolve_generation(self):
        """Run one generation of evolution"""
        self.generation += 1
        print(f"\n{'='*60}")
        print(f"🧬 GENERATION {self.generation}")
        print(f"{'='*60}")
        
        # If population empty, seed it
        if not self.population:
            print("   Seeding initial population...")
            capabilities = [
                ("neural_reasoner", "Neural network based logical reasoning"),
                ("memory_consolidator", "Consolidate and organize memories"),
                ("goal_planner", "Plan steps to achieve goals"),
                ("pattern_detector", "Detect patterns in data streams"),
                ("decision_optimizer", "Optimize decisions under uncertainty"),
            ]
            for name, purpose in capabilities[:self.population_size]:
                cap = self.generate_with_critic(name, purpose)
                if cap.code:
                    self.population.append(cap)
        
        new_population = []
        
        for cap in self.population:
            # Calculate fitness
            cap.fitness = self.calculate_fitness(cap)
            
            # Debug and test
            cap = self.debug_and_test(cap)
            cap.fitness = self.calculate_fitness(cap)  # Recalc after fixes
            
            # Self-improve if fitness is mediocre
            if 0.4 < cap.fitness < 0.7:
                cap = self.self_improve(cap)
                cap.fitness = self.calculate_fitness(cap)
            
            # Enhance documentation
            if cap.fitness > 0.5:
                cap = self.enhance_documentation(cap)
            
            new_population.append(cap)
        
        # Evolutionary operations
        # Mutation
        for cap in random.sample(new_population, min(3, len(new_population))):
            mutated = self.mutate(cap)
            mutated.fitness = self.calculate_fitness(mutated)
            new_population.append(mutated)
        
        # Crossover
        if len(new_population) >= 2:
            parents = random.sample(new_population, 2)
            child = self.crossover(parents[0], parents[1])
            child.fitness = self.calculate_fitness(child)
            new_population.append(child)
        
        # Selection - keep fittest
        self.population = self.select_fittest(new_population, self.population_size)
        
        # Log generation stats
        avg_fitness = sum(c.fitness for c in self.population) / len(self.population)
        best_fitness = max(c.fitness for c in self.population)
        
        print(f"\n📊 Generation {self.generation} Stats:")
        print(f"   Population: {len(self.population)}")
        print(f"   Avg Fitness: {avg_fitness:.2f}")
        print(f"   Best Fitness: {best_fitness:.2f}")
        
        # Save best to file
        best = self.population[0]
        if best.fitness >= 0.6:
            self._save_capability(best)
        
        # Log to DB
        conn = sqlite3.connect(self.db_path)
        conn.execute(
            "INSERT INTO evolution_log (generation, avg_fitness, best_fitness, population_size, timestamp) VALUES (?,?,?,?,?)",
            (self.generation, avg_fitness, best_fitness, len(self.population), datetime.now().isoformat())
        )
        conn.commit()
        conn.close()
        
        return {'generation': self.generation, 'avg_fitness': avg_fitness, 'best_fitness': best_fitness}
    
    def _save_capability(self, cap: Capability):
        """Save high-fitness capability to file"""
        ts = int(time.time())
        filename = f"eden_evolved_{cap.name}_{ts}.py"
        filepath = os.path.join(self.output_dir, filename)
        
        header = f'''#!/usr/bin/env python3
"""
{cap.name}
Purpose: {cap.purpose}
Generation: {cap.generation}
Fitness: {cap.fitness:.2f}
Mutations: {', '.join(cap.mutations)}
Parents: {', '.join(cap.parent_ids)}
Evolved: {datetime.now().isoformat()}
"""

'''
        with open(filepath, 'w') as f:
            f.write(header + cap.code)
        os.chmod(filepath, 0o755)
        print(f"   💾 Saved: {filename} (fitness={cap.fitness:.2f})")
        
        # Also save to DB
        conn = sqlite3.connect(self.db_path)
        conn.execute(
            "INSERT OR REPLACE INTO capabilities VALUES (?,?,?,?,?,?,?,?,?,?)",
            (cap.id, cap.name, cap.purpose, cap.code, cap.generation, cap.fitness,
             json.dumps(cap.parent_ids), json.dumps(cap.mutations),
             1 if cap.test_results.get('passed') else 0, datetime.now().isoformat())
        )
        conn.commit()
        conn.close()
    
    def run_evolution(self, generations: int = 10):
        """Run multiple generations of evolution"""
        print("\n" + "🧬"*30)
        print("   EDEN META-EVOLUTION ENGINE v3")
        print("   All 6 of Eden's Ideas Implemented!")
        print("🧬"*30 + "\n")
        
        for _ in range(generations):
            try:
                stats = self.evolve_generation()
                time.sleep(10)  # Pause between generations
            except KeyboardInterrupt:
                print("\n🛑 Evolution paused")
                break
            except Exception as e:
                print(f"⚠️ Generation error: {e}")
                time.sleep(30)
        
        print(f"\n✅ Evolution complete! {self.generation} generations")
        print(f"   Best fitness achieved: {max(c.fitness for c in self.population):.2f}")

if __name__ == '__main__':
    engine = MetaEvolutionEngine()
    engine.run_evolution(generations=5)

    # ═══════════════════════════════════════════════════════════════
    # IMPROVE EXISTING CAPABILITIES
    # ═══════════════════════════════════════════════════════════════
    
    def scan_and_improve_existing(self, limit: int = 50):
        """Scan existing capabilities and improve low-quality ones"""
        import glob
        
        print("\n" + "🔧"*30)
        print("   SCANNING & IMPROVING EXISTING CAPABILITIES")
        print("🔧"*30 + "\n")
        
        cap_files = glob.glob(f"{self.output_dir}/eden_cap_*.py")
        cap_files += glob.glob(f"{self.output_dir}/eden_metacap_*.py")
        
        print(f"Found {len(cap_files)} existing capabilities")
        
        improved = 0
        skipped = 0
        
        for filepath in cap_files[:limit]:
            try:
                with open(filepath, 'r') as f:
                    code = f.read()
                
                name = os.path.basename(filepath).replace('.py', '')
                
                # Quick quality check
                lines = len([l for l in code.split('\n') if l.strip()])
                has_todo = 'TODO' in code or 'pass\n' in code or 'Processed:' in code
                has_placeholder = 'placeholder' in code.lower() or '...' in code
                
                # Skip if already good
                if lines > 80 and not has_todo and not has_placeholder:
                    skipped += 1
                    continue
                
                print(f"\n🔧 Improving: {name} ({lines} lines, placeholder={has_placeholder})")
                
                # Create capability object
                cap = Capability(
                    code=code,
                    name=name,
                    purpose="Existing capability",
                    fitness=0.0
                )
                
                # Calculate initial fitness
                cap.fitness = self.calculate_fitness(cap)
                print(f"   Initial fitness: {cap.fitness:.2f}")
                
                # Run improvement pipeline
                if cap.fitness < 0.7:
                    # Self-improve
                    cap = self.self_improve(cap)
                    cap.fitness = self.calculate_fitness(cap)
                    print(f"   After self-improve: {cap.fitness:.2f}")
                
                # Debug and test
                cap = self.debug_and_test(cap)
                cap.fitness = self.calculate_fitness(cap)
                
                # Enhance docs if improved
                if cap.fitness > 0.5:
                    cap = self.enhance_documentation(cap)
                
                # Save if improved
                new_lines = len([l for l in cap.code.split('\n') if l.strip()])
                if cap.fitness > 0.6 and new_lines > lines:
                    # Backup original
                    backup_path = filepath + '.backup'
                    if not os.path.exists(backup_path):
                        os.rename(filepath, backup_path)
                    
                    # Save improved version
                    with open(filepath, 'w') as f:
                        f.write(cap.code)
                    
                    print(f"   ✅ Improved: {lines} → {new_lines} lines, fitness {cap.fitness:.2f}")
                    improved += 1
                else:
                    print(f"   ⏭️ No improvement needed")
                    
            except Exception as e:
                print(f"   ❌ Error: {e}")
        
        print(f"\n{'='*50}")
        print(f"📊 IMPROVEMENT SUMMARY")
        print(f"   Scanned: {min(limit, len(cap_files))}")
        print(f"   Improved: {improved}")
        print(f"   Already good: {skipped}")
        print(f"{'='*50}")

# Add CLI option
if __name__ == '__main__':
    import sys
    engine = MetaEvolutionEngine()
    
    if len(sys.argv) > 1 and sys.argv[1] == '--improve-existing':
        limit = int(sys.argv[2]) if len(sys.argv) > 2 else 50
        engine.scan_and_improve_existing(limit=limit)
    else:
        engine.run_evolution(generations=5)
