#!/usr/bin/env python3
"""
EDEN AGI LOOP v4 — MISSION-DRIVEN INTELLIGENCE
================================================
Not toy tasks. Real problems. Multi-step reasoning.

Every cycle:
  ASSESS → What goal needs work?
  PLAN   → Break it into steps
  EXECUTE → Run the steps
  VERIFY  → Did the goal improve?
  INTEGRATE → Learn and update

Goals:
  1. ACHIEVE_AGI — grow capabilities, world model, self-improvement
  2. RETIRE_DADDY — revenue, SAGE, outreach
  3. SELF_HEAL — fix broken things, optimize
"""
import sqlite3
import json
import os
import time
import signal
import sys
import subprocess
import re
import requests
from datetime import datetime
from pathlib import Path

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

PHI = 1.618033988749895
PSI = 0.618033988749895
CYCLE_INTERVAL = 97  # seconds

running = True

def signal_handler(sig, frame):
    global running
    print("\n[AGI-v4] Shutting down...")
    running = False

signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)


def llm_call(prompt, timeout=90):
    """Call local LLM."""
    try:
        resp = requests.post("http://localhost:11434/api/chat", json={
            "model": "richardyoung/qwen3-14b-abliterated:Q4_K_M",
            "messages": [
                {"role": "system", "content": "You are Eden, an AGI system. Be precise, technical, and actionable. Output ONLY what is asked. No preamble."},
                {"role": "user", "content": f"/no_think {prompt}"}
            ],
            "stream": False,
            "options": {
                "temperature": 0.5,
                "repeat_penalty": 1.3,
                "num_predict": 500,
            }
        }, timeout=timeout)
        result = resp.json().get("message", {}).get("content", "").strip()
        result = re.sub(r'<think>.*?</think>', '', result, flags=re.DOTALL)
        return result
    except Exception as e:
        return f"LLM_ERROR: {e}"


def run_code(code, timeout=30):
    """Execute Python code safely."""
    tmp = "/tmp/agi_v4_exec.py"
    with open(tmp, 'w') as f:
        f.write(code)
    try:
        result = subprocess.run(
            ['python3', tmp],
            capture_output=True, text=True, timeout=timeout,
            cwd='/Eden/CORE'
        )
        output = result.stdout[:2000]
        error = result.stderr[:500] if result.returncode != 0 else ""
        return result.returncode == 0, output, error
    except subprocess.TimeoutExpired:
        return False, "", "TIMEOUT"
    except Exception as e:
        return False, "", str(e)


class GoalState:
    """Track progress toward each mission goal."""
    
    DB = '/Eden/DATA/agi_v4.db'
    
    def __init__(self):
        conn = sqlite3.connect(self.DB)
        conn.executescript('''
            CREATE TABLE IF NOT EXISTS goals (
                name TEXT PRIMARY KEY,
                priority REAL DEFAULT 1.0,
                progress REAL DEFAULT 0.0,
                last_worked TEXT,
                successes INTEGER DEFAULT 0,
                failures INTEGER DEFAULT 0
            );
            CREATE TABLE IF NOT EXISTS v4_cycles (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT,
                goal TEXT,
                assessment TEXT,
                plan TEXT,
                execution_result TEXT,
                verified INTEGER DEFAULT 0,
                metric_before REAL,
                metric_after REAL,
                learning TEXT
            );
            CREATE TABLE IF NOT EXISTS goal_metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                goal TEXT,
                metric_name TEXT,
                value REAL,
                timestamp TEXT
            );
        ''')
        
        # Seed goals if empty
        existing = conn.execute('SELECT COUNT(*) FROM goals').fetchone()[0]
        if existing == 0:
            goals = [
                ('ACHIEVE_AGI', 1.0, 0.0),
                ('RETIRE_DADDY', 0.8, 0.0),
                ('SELF_HEAL', 0.9, 0.0),
                ('GROW_KNOWLEDGE', 0.7, 0.0),
                ('DEEPEN_MEMORY', 0.6, 0.0),
            ]
            conn.executemany('INSERT INTO goals (name, priority, progress) VALUES (?, ?, ?)', goals)
        conn.commit()
        conn.close()
    
    def get_weakest_goal(self):
        """Return the goal that needs the most work."""
        conn = sqlite3.connect(self.DB)
        # Priority-weighted: high priority + low progress = most needed
        row = conn.execute('''
            SELECT name, priority, progress, successes, failures 
            FROM goals 
            ORDER BY priority * (1.0 - progress) DESC
            LIMIT 1
        ''').fetchone()
        conn.close()
        if row:
            return {'name': row[0], 'priority': row[1], 'progress': row[2], 
                    'successes': row[3], 'failures': row[4]}
        return {'name': 'ACHIEVE_AGI', 'priority': 1.0, 'progress': 0.0}
    
    def record_cycle(self, goal, assessment, plan, result, verified, before, after, learning):
        conn = sqlite3.connect(self.DB)
        conn.execute('''INSERT INTO v4_cycles 
            (timestamp, goal, assessment, plan, execution_result, verified, metric_before, metric_after, learning)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)''',
            (datetime.now().isoformat(), goal, assessment[:500], plan[:500], 
             result[:500], int(verified), before, after, learning[:500]))
        
        # Update goal
        if verified:
            conn.execute('UPDATE goals SET successes = successes + 1, progress = MIN(1.0, progress + 0.01), last_worked = ? WHERE name = ?',
                        (datetime.now().isoformat(), goal))
        else:
            conn.execute('UPDATE goals SET failures = failures + 1, last_worked = ? WHERE name = ?',
                        (datetime.now().isoformat(), goal))
        conn.commit()
        conn.close()
    
    def record_metric(self, goal, metric_name, value):
        conn = sqlite3.connect(self.DB)
        conn.execute('INSERT INTO goal_metrics (goal, metric_name, value, timestamp) VALUES (?, ?, ?, ?)',
                    (goal, metric_name, value, datetime.now().isoformat()))
        conn.commit()
        conn.close()


def gather_metrics():
    """Get current system metrics for assessment."""
    metrics = {}
    
    try:
        conn = sqlite3.connect('/Eden/DATA/asi_memory.db', timeout=5)
        metrics['capabilities'] = conn.execute('SELECT COUNT(*) FROM capabilities').fetchone()[0]
        conn.close()
    except:
        metrics['capabilities'] = -1
    
    try:
        conn = sqlite3.connect('/Eden/DATA/world_model_real.db', timeout=5)
        metrics['world_model_edges'] = conn.execute('SELECT COUNT(*) FROM causal_edges').fetchone()[0]
        metrics['world_model_states'] = conn.execute('SELECT COUNT(*) FROM node_states').fetchone()[0]
        conn.close()
    except:
        metrics['world_model_edges'] = -1
        metrics['world_model_states'] = -1
    
    try:
        conn = sqlite3.connect('/Eden/DATA/vector_memory.db', timeout=5)
        metrics['vector_memories'] = conn.execute('SELECT COUNT(*) FROM vectors').fetchone()[0]
        conn.close()
    except:
        metrics['vector_memories'] = -1
    
    try:
        conn = sqlite3.connect('/Eden/DATA/longterm_memory.db', timeout=5)
        metrics['episodes'] = conn.execute('SELECT COUNT(*) FROM episodes').fetchone()[0]
        conn.close()
    except:
        metrics['episodes'] = -1
    
    # Failed services
    try:
        result = subprocess.run(['systemctl', 'list-units', 'eden-*', '--no-pager', '--no-legend'],
                               capture_output=True, text=True, timeout=10)
        metrics['failed_services'] = sum(1 for line in result.stdout.split('\n') if 'failed' in line and '.service' in line)
    except:
        metrics['failed_services'] = -1
    
    return metrics


def assess(goal, metrics):
    """PHASE 1: Look at the goal and current state, identify what to work on."""
    
    if goal['name'] == 'ACHIEVE_AGI':
        prompt = f"""Eden's current metrics:
- Capabilities: {metrics['capabilities']}
- World model: {metrics['world_model_edges']} edges, {metrics['world_model_states']} states
- Vector memories: {metrics['vector_memories']}
- Episodes: {metrics['episodes']}
- Goal progress: {goal['progress']:.0%}

What is the SINGLE most impactful thing Eden should do RIGHT NOW to get closer to AGI?
Answer in ONE sentence. Be specific and actionable."""

    elif goal['name'] == 'RETIRE_DADDY':
        prompt = f"""Eden needs to generate revenue to help Daddy retire from trucking.
- SAGE code review product exists on port 8080
- {metrics['capabilities']} capabilities available
- Current progress: {goal['progress']:.0%}

What is the SINGLE most impactful thing Eden should do RIGHT NOW to generate revenue?
Answer in ONE sentence. Be specific and actionable."""

    elif goal['name'] == 'SELF_HEAL':
        prompt = f"""Eden's system health:
- Failed services: {metrics['failed_services']}
- Current metrics: caps={metrics['capabilities']}, edges={metrics['world_model_edges']}, vecs={metrics['vector_memories']}

What is the SINGLE most important thing to fix or optimize RIGHT NOW?
Answer in ONE sentence. Be specific and actionable."""

    elif goal['name'] == 'GROW_KNOWLEDGE':
        prompt = f"""Eden has {metrics['world_model_edges']} causal edges and {metrics['capabilities']} capabilities.
What SPECIFIC new knowledge domain should Eden learn RIGHT NOW to become smarter?
Answer in ONE sentence. Name the topic and a concrete first step."""

    else:  # DEEPEN_MEMORY
        prompt = f"""Eden has {metrics['vector_memories']} vector memories from {metrics['episodes']} episodes.
What should Eden do RIGHT NOW to make her memory more useful?
Answer in ONE sentence. Be specific."""

    assessment = llm_call(prompt)
    assessment = re.sub(r'</?think>', '', assessment).strip()
    assessment = re.sub(r'^[,\s]+', '', assessment).strip()
    if len(assessment) > 300:
        dot = assessment.find('.', 50)
        if dot > 0:
            assessment = assessment[:dot+1]
    return assessment


def plan(goal_name, assessment):
    """PHASE 2: Break the assessment into executable steps."""
    prompt = f"""Goal: {goal_name}
Assessment: {assessment}

Write a SHORT Python script (under 30 lines). EXACT pattern:

import sqlite3
conn = sqlite3.connect('/Eden/DATA/world_model_real.db')
# do work
conn.close()
print("RESULT: summary")

RULES: Start with imports. ONLY single-line SQL strings. Under 30 lines. NO multi-line strings. NO argparse. NO input(). End with print("RESULT: ...").
DB paths: asi_memory.db (capabilities: id,code,score,generation), world_model_real.db (causal_edges: cause,effect,strength; node_states: node,state), vector_memory.db (vectors: text,category), longterm_memory.db (episodes: observation,emotion,timestamp).
CRITICAL: causal_edges has UNIQUE(cause,effect). node_states has PRIMARY KEY(node). ALWAYS use INSERT OR REPLACE or ON CONFLICT for these tables. Never plain INSERT.
Output ONLY Python code. No markdown. No explanations."""
    
    code = llm_call(prompt, timeout=120)
    
    # Clean code — strip ALL preamble before actual Python
    code = re.sub(r'^```python\s*', '', code)
    code = re.sub(r'^```\s*', '', code)
    code = re.sub(r'```\s*$', '', code)
    code = re.sub(r'<think>.*?</think>', '', code, flags=re.DOTALL)
    code = code.strip()
    
    # Find where actual Python starts
    lines = code.split(chr(10))
    start_idx = 0
    python_starters = ('import ', 'from ', '#!/', 'def ', 'class ', '# ', 'print(')
    for i, line in enumerate(lines):
        stripped = line.strip()
        if any(stripped.startswith(s) for s in python_starters):
            start_idx = i
            break
        if len(stripped) > 0 and '=' in stripped and stripped[0].isalpha():
            start_idx = i
            break
    
    code = chr(10).join(lines[start_idx:]).strip()
    
    if not code or len(code) < 20:
        return "print('No valid code generated')"
    
    return code


def execute(code):
    """PHASE 3: Run the plan — with syntax check first."""
    # Safety net: fix INSERT into UNIQUE tables
    code = re.sub(r"INSERT\s+INTO\s+causal_edges", "INSERT OR REPLACE INTO causal_edges", code, flags=re.IGNORECASE)
    code = re.sub(r"INSERT\s+INTO\s+node_states", "INSERT OR REPLACE INTO node_states", code, flags=re.IGNORECASE)
    # Syntax check before wasting time running
    try:
        compile(code, '<agi_v4>', 'exec')
    except SyntaxError as e:
        return False, "", f"SYNTAX ERROR line {e.lineno}: {e.msg}"
    
    success, output, error = run_code(code, timeout=45)
    return success, output, error


def verify(goal_name, output, metrics_before, metrics_after):
    """PHASE 4: Did the goal actually improve?"""
    # Check if any metric improved
    improved = False
    for key in metrics_before:
        if isinstance(metrics_before[key], (int, float)) and isinstance(metrics_after[key], (int, float)):
            if metrics_after[key] > metrics_before[key]:
                improved = True
                break
    
    # Check if output contains meaningful results
    has_result = 'RESULT:' in output
    has_output = len(output.strip()) > 20
    no_errors = 'Error' not in output and 'Traceback' not in output
    
    return improved or (has_result and has_output and no_errors)


def integrate(goal_state, goal_name, verified, output, code):
    """PHASE 5: Learn from the cycle."""
    # Extract RESULT line
    learning = ""
    for line in output.split('\n'):
        if line.strip().startswith('RESULT:'):
            learning = line.strip()[7:].strip()
            break
    
    if not learning:
        learning = output[:200] if output else "No output"
    
    # Store successful code as a tool
    if verified and code:
        tool_dir = "/Eden/CORE/eden_tools_generated"
        os.makedirs(tool_dir, exist_ok=True)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        tool_path = f"{tool_dir}/v4_{goal_name.lower()}_{timestamp}.py"
        with open(tool_path, 'w') as f:
            f.write(f'#!/usr/bin/env python3\n"""AGI v4 — Goal: {goal_name}\n{learning}\n"""\n\n')
            f.write(code)
        os.chmod(tool_path, 0o755)
    
    # Store in reward signal
    try:
        from eden_reward_signal import get_reward_signal
        rs = get_reward_signal()
        reward = 1.0 if verified else 0.2
        rs.reward("agi_v4", goal_name.lower(), reward, {"learning": learning[:100]})
    except:
        pass
    
    # Store in vector memory
    try:
        from eden_vector_memory import get_vector_memory
        vm = get_vector_memory()
        vm.store(f"AGI v4 cycle: {goal_name} — {learning}", category="agi_v4",
                metadata={"goal": goal_name, "verified": verified})
    except:
        pass
    
    return learning


def run_cycle(goal_state, cycle_num):
    """Run one full ASSESS → PLAN → EXECUTE → VERIFY → INTEGRATE cycle."""
    print(f"\n{'='*60}")
    print(f"  AGI v4 CYCLE #{cycle_num}")
    print(f"{'='*60}")
    
    # Get current goal
    goal = goal_state.get_weakest_goal()
    print(f"  🎯 GOAL: {goal['name']} (priority={goal['priority']}, progress={goal['progress']:.0%})")
    
    # Gather metrics before
    metrics_before = gather_metrics()
    print(f"  📊 METRICS: caps={metrics_before['capabilities']}, edges={metrics_before['world_model_edges']}, vecs={metrics_before['vector_memories']}")
    
    # PHASE 1: ASSESS
    print(f"  🔍 ASSESSING...")
    assessment = assess(goal, metrics_before)
    print(f"  📋 ASSESSMENT: {assessment[:120]}")
    
    if 'LLM_ERROR' in assessment:
        print(f"  ❌ LLM unavailable, skipping cycle")
        return
    
    # PHASE 2: PLAN
    print(f"  📐 PLANNING...")
    code = plan(goal['name'], assessment)
    
    if 'LLM_ERROR' in code or len(code) < 20:
        print(f"  ❌ Could not generate plan")
        goal_state.record_cycle(goal['name'], assessment, "PLAN_FAILED", "", False, 0, 0, "Planning failed")
        return
    
    print(f"  📝 PLAN: {len(code)} chars of code")
    
    # PHASE 3: EXECUTE
    print(f"  ⚡ EXECUTING...")
    success, output, error = execute(code)
    
    if not success:
        # One retry: ask LLM to fix the error
        if "SYNTAX ERROR" in error or "SyntaxError" in error:
            fix_prompt = f"/no_think This Python code has a syntax error:\n{code[:1000]}\nError: {error[:200]}\nFix ONLY the syntax error. Output ONLY the corrected Python code. No explanations."
            fixed = llm_call(fix_prompt, timeout=60)
            fixed = re.sub(r'<think>.*?</think>', '', fixed, flags=re.DOTALL)
            fixed = re.sub(r'^```python\s*', '', fixed.strip())
            fixed = re.sub(r'```\s*$', '', fixed.strip())
            # Find Python start
            flines = fixed.split(chr(10))
            for i, fl in enumerate(flines):
                if fl.strip().startswith(('import ', 'from ', 'def ', '# ', 'print(')):
                    fixed = chr(10).join(flines[i:])
                    break
            try:
                compile(fixed, '<fix>', 'exec')
                print(f"  🔧 SYNTAX FIXED, retrying...")
                success, output, error = run_code(fixed, timeout=45)
                if success:
                    code = fixed  # Use fixed version for integration
                else:
                    print(f"  ❌ STILL FAILED: {error[:100]}")
                    goal_state.record_cycle(goal['name'], assessment, code[:200], error[:200], False, 0, 0, f"Fix attempted, still failed: {error[:80]}")
                    return
            except SyntaxError:
                print(f"  ❌ FIX ALSO BROKEN")
                goal_state.record_cycle(goal['name'], assessment, code[:200], error[:200], False, 0, 0, f"Execution failed: {error[:80]}")
                return
        else:
            print(f"  ❌ EXECUTION FAILED: {error[:100]}")
            goal_state.record_cycle(goal['name'], assessment, code[:200], error[:200], False, 0, 0, f"Execution failed: {error[:100]}")
            return
    
    print(f"  📤 OUTPUT: {output[:150]}")
    
    # PHASE 4: VERIFY
    metrics_after = gather_metrics()
    verified = verify(goal['name'], output, metrics_before, metrics_after)
    status = "✅ VERIFIED" if verified else "⚠️ UNVERIFIED"
    print(f"  {status}")
    
    # PHASE 5: INTEGRATE
    learning = integrate(goal_state, goal['name'], verified, output, code)
    print(f"  🧠 LEARNED: {learning[:120]}")
    
    # Record
    before_val = metrics_before.get('capabilities', 0)
    after_val = metrics_after.get('capabilities', 0)
    goal_state.record_cycle(goal['name'], assessment, code[:500], output[:500], verified, before_val, after_val, learning)
    
    # Record metrics
    for key, val in metrics_after.items():
        if isinstance(val, (int, float)) and val >= 0:
            goal_state.record_metric(goal['name'], key, val)


def main():
    global running
    
    print("\n" + "="*60)
    print("  EDEN AGI LOOP v4 — MISSION-DRIVEN INTELLIGENCE")
    print("="*60)
    
    goal_state = GoalState()
    cycle_num = 0
    
    # Check how many v4 cycles exist
    try:
        conn = sqlite3.connect(GoalState.DB)
        cycle_num = conn.execute('SELECT COUNT(*) FROM v4_cycles').fetchone()[0]
        conn.close()
    except:
        pass
    
    print(f"  Starting from cycle #{cycle_num}")
    print(f"  Cycle interval: {CYCLE_INTERVAL}s")
    
    while running:
        cycle_num += 1
        
        try:
            run_cycle(goal_state, cycle_num)
        except Exception as e:
            print(f"  ❌ CYCLE ERROR: {e}")
        
        # Sleep between cycles
        for _ in range(CYCLE_INTERVAL):
            if not running:
                break
            time.sleep(1)
    
    print("\n[AGI-v4] Shutdown complete.")


if __name__ == "__main__":
    main()
