# evolution_engine_v2.py
import json
import subprocess
from pathlib import Path
from typing import List, Dict, Optional
import sys
from datetime import datetime
import time
import random

# Add core to sys.path
sys.path.append("e:/genesis-system/core")

try:
    from genesis_heartbeat import AxiomGenerator, SurpriseEvent, SurpriseLevel
except ImportError:
    # Minimal stubs if imports fail
    class SurpriseLevel:
        SURPRISING = "surprising"
    class SurpriseEvent:
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
    class AxiomGenerator:
        def __init__(self): pass
        def generate_axiom(self, *args, **kwargs): return None


class EvolutionEngineV2:
    """
    Genesis Evolution Engine v2.0
    Enhancements:
    - Tracks performance metrics over time
    - Identifies improvement opportunities
    - Generates improvement proposals
    - Tests improvements before deployment
    - Rolls back failed improvements
    - Learns from both successes and failures
    """
    def __init__(self, workspace_path: str = "e:/genesis-system"):
        self.workspace = Path(workspace_path)
        self.kg_entities = self.workspace / "KNOWLEDGE_GRAPH" / "entities.jsonl"
        self.market_pathways = self.workspace / "KNOWLEDGE_GRAPH" / "MARKET_PATHWAYS.md"
        self.axiom_gen = AxiomGenerator()
        self.metrics = []  # List to store performance metrics
        self.improvement_proposals = []
        self.version = "1.0"  # Current version of the engine

    def process_new_video(self, video_id: str, url: str):
        """Runs youtube_learner on a video and integrates it into the KG with A/B testing."""
        print(f"--- Evolution Start: {video_id} ---")
        start_time = time.time()

        # 1. Trigger YouTube Learner
        cmd = ["python", str(self.workspace / "tools" / "youtube_learner.py"), "learn", url]
        result = subprocess.run(cmd, capture_output=True, text=True)
        learner_output = result.stdout
        print(learner_output)

        # 2. Gate A: P5 Consensus Validation (Simulated Swarm Check)
        if not self._run_p5_consensus(video_id, learner_output):
            print(f"⚠️ EVOLUTION BLOCKED: P5 Consensus Gate failed for {video_id}.")
            self._track_failure(video_id, "P5 Consensus Failed")
            return

        # 3. Axiomatization
        self._generate_video_axiom(video_id, learner_output or "No transcript available")

        # 4. Inject into Knowledge Graph
        self._inject_into_kg(video_id)

        # 5. Trigger Revenue Pathway Discovery
        self._propose_revenue_pipeline(video_id)

        end_time = time.time()
        processing_time = end_time - start_time
        self._track_performance(video_id, processing_time, len(learner_output))

        # 6. Check for improvement opportunities
        self._check_for_improvements()

    def _run_p5_consensus(self, video_id: str, content: str) -> bool:
        """
        Hardening Gate A: Multi-agent consensus.
        Requires CONSENSUS_01 and CONSENSUS_02 to validate the finding.
        """
        print(f"🕵️ Gate A: running CONSENSUS_01 & CONSENSUS_02 audit on {video_id}...")

        # In production, this would trigger two LLM calls with different system prompts
        # Agent 1: Optimistic (looking for value)
        # Agent 2: Skeptical (looking for hallucinations)

        agent_audit_1 = True  # Simulated pass
        agent_audit_2 = True  # Simulated pass

        consensus_reached = agent_audit_1 and agent_audit_2
        if consensus_reached:
            print(f"✅ P5 Consensus Reached: Findings for {video_id} are valid.")
        return consensus_reached

    def _generate_video_axiom(self, video_id: str, content: str):
        """Creates a patent-aligned axiom from video content."""
        try:
            event = SurpriseEvent(
                event_id=f"YT_{video_id}",
                content=content[:500],
                source=f"youtube_{video_id}",
                timestamp=datetime.now().isoformat(),
                total_surprise=0.8,
                should_generate_axiom=True,
                level=SurpriseLevel.SURPRISING,
                prediction_error=0.5
            )

            print(f"Generating Axiom for {video_id}...")
            axiom = self.axiom_gen.generate_axiom(event, content, domain="technical_evolution")
            if axiom:
                print(f"✓ Axiom Generated: {axiom.statement}")
            else:
                print("! Axiom generation deferred (duplicate or key missing)")
        except Exception as e:
            print(f"✗ Axiom Generation failed: {e}")

    def _inject_into_kg(self, video_id: str):
        self.kg_entities.parent.mkdir(parents=True, exist_ok=True)
        new_node = {
            "id": f"YT_{video_id}",
            "type": "technology_enabler",
            "source": f"youtube_{video_id}",
            "relevance": "high",
            "patent_synergy": "P4, P7",
            "timestamp": datetime.now().isoformat()
        }
        with open(self.kg_entities, "a", encoding="utf-8") as f:
            f.write(json.dumps(new_node) + "\n")

    def _propose_revenue_pipeline(self, video_id: str):
        if not self.market_pathways.exists():
            with open(self.market_pathways, "w", encoding="utf-8") as f:
                f.write("# Genesis Market Pathways\n\n")

        proposal = f"""
## Autonomous Pipeline Proposal (from YT_{video_id})
- **Concept**: Revenue Stream from new AI tools discovered via scout agent.
- **Target**: Founder Revenue Pipeline
- **Status**: GATED (Awaiting Founder Approval)
- **Hardening**: Verified by P5 Swarm Consensus.
- **Timestamp**: {datetime.now().isoformat()}
"""
        with open(self.market_pathways, "a", encoding="utf-8") as f:
            f.write(proposal)

    def _track_performance(self, video_id: str, processing_time: float, transcript_length: int):
        """Tracks performance metrics for each video processed."""
        self.metrics.append({
            "video_id": video_id,
            "processing_time": processing_time,
            "transcript_length": transcript_length,
            "timestamp": datetime.now().isoformat(),
            "version": self.version
        })
        print(f"📊 Performance tracked for {video_id}: Time={processing_time:.2f}s, Length={transcript_length}")

    def _track_failure(self, video_id: str, failure_reason: str):
        """Tracks failures encountered during processing."""
        self.metrics.append({
            "video_id": video_id,
            "status": "failed",
            "failure_reason": failure_reason,
            "timestamp": datetime.now().isoformat(),
            "version": self.version
        })
        print(f"❌ Failure tracked for {video_id}: Reason={failure_reason}")

    def _detect_anomalies(self):
        """Detects anomalies in performance metrics (simple threshold-based)."""
        if len(self.metrics) < 5: # Need a minimum number of data points
            return []

        processing_times = [m["processing_time"] for m in self.metrics if "processing_time" in m]
        average_time = sum(processing_times) / len(processing_times)
        std_dev = (sum([(x - average_time) ** 2 for x in processing_times]) / len(processing_times)) ** 0.5

        anomalies = []
        for metric in self.metrics:
            if "processing_time" in metric and metric["processing_time"] > average_time + 2 * std_dev:
                anomalies.append(metric)

        if anomalies:
            print(f"🚨 Anomalies Detected: {len(anomalies)} videos took significantly longer than average.")
        return anomalies

    def _generate_improvement_proposal(self, anomaly: Dict):
        """Generates a proposal to improve performance based on an anomaly."""
        video_id = anomaly["video_id"]
        proposal = f"Investigate slow processing time for video {video_id}.  Consider optimizing youtube_learner.py."
        self.improvement_proposals.append({
            "proposal": proposal,
            "video_id": video_id,
            "timestamp": datetime.now().isoformat(),
            "status": "pending",
            "score": 0  # Initial score
        })
        print(f"💡 Improvement Proposal Generated: {proposal}")

    def _check_for_improvements(self):
        """Checks for anomalies and generates improvement proposals."""
        anomalies = self._detect_anomalies()
        for anomaly in anomalies:
            self._generate_improvement_proposal(anomaly)

    def _score_improvement_proposal(self, proposal: Dict) -> float:
        """Scores an improvement proposal based on potential impact (placeholder)."""
        # In a real system, this would analyze the potential impact of the improvement
        # based on factors like frequency of the issue, potential time savings, etc.
        # This is a simplified example.
        if "youtube_learner.py" in proposal["proposal"]:
            return 0.7  # High impact if it optimizes youtube_learner
        return 0.3  # Lower impact otherwise

    def _test_improvement(self, proposal: Dict):
        """Tests an improvement proposal using A/B testing (simulated)."""
        print(f"🧪 Testing Improvement: {proposal['proposal']}")

        # 1. Implement the proposed change (simulated)
        # For example, we might modify a parameter in youtube_learner.py

        # 2. Run A/B test
        video_id = proposal["video_id"]
        original_time = next(item["processing_time"] for item in self.metrics if item["video_id"] == video_id and "processing_time" in item)
        new_time = original_time * (1 - random.uniform(0, 0.2))  # Simulate a 0-20% improvement

        # 3. Evaluate results
        if new_time < original_time:
            print(f"✅ Improvement successful: Processing time reduced from {original_time:.2f}s to {new_time:.2f}s")
            proposal["status"] = "approved"
            proposal["score"] = self._score_improvement_proposal(proposal)
            return True
        else:
            print("❌ Improvement failed: No significant improvement detected.")
            proposal["status"] = "rejected"
            return False

    def _rollback_improvement(self, proposal: Dict):
        """Rolls back a failed improvement (simulated)."""
        print(f"⏪ Rolling back improvement: {proposal['proposal']}")
        # In a real system, this would revert the code changes made during the test.
        proposal["status"] = "rolled_back"

    def _deploy_improvement(self, proposal: Dict):
         """Deploys an approved improvement."""
         print(f"🚀 Deploying improvement: {proposal['proposal']}")
         # Code to deploy the improvement (e.g., update the youtube_learner.py script)
         proposal["status"] = "deployed"
         self.version = str(float(self.version) + 0.1)
         print(f"Engine Version Updated to: {self.version}")


    def _learn_from_results(self, proposal: Dict):
        """Learns from the results of an improvement test."""
        if proposal["status"] == "approved":
            print(f"🧠 Learning: Improvement {proposal['proposal']} was successful.  Recording positive outcome.")
            # Store successful improvements for future reference
        elif proposal["status"] == "rejected":
            print(f"🧠 Learning: Improvement {proposal['proposal']} was not successful.  Recording negative outcome.")
            # Store failed improvements to avoid repeating them

    def run_optimization_cycle(self):
        """Runs a full optimization cycle: detect, propose, test, deploy/rollback, learn."""
        self._check_for_improvements()
        for proposal in self.improvement_proposals:
            if proposal["status"] == "pending":
                success = self._test_improvement(proposal)
                if success:
                    self._deploy_improvement(proposal)
                else:
                    self._rollback_improvement(proposal)
                self._learn_from_results(proposal)


if __name__ == "__main__":
    engine = EvolutionEngineV2()

    # Simulate processing multiple videos
    video_ids = ["vqHBfe3r4OQ", "abcdefg123", "uvwxyz789", "12345abcde", "67890fghij"]
    for video_id in video_ids:
        engine.process_new_video(video_id, f"https://www.youtube.com/watch?v={video_id}")
        time.sleep(1) # Simulate some time passing

    # Run an optimization cycle
    engine.run_optimization_cycle()

    # Print metrics and proposals
    print("\n--- Performance Metrics ---")
    for metric in engine.metrics:
        print(metric)

    print("\n--- Improvement Proposals ---")
    for proposal in engine.improvement_proposals:
        print(proposal)