# 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
    Tracks performance, identifies improvement opportunities, tests, and rolls back failed changes.
    """
    def __init__(self, workspace_path: str = "e:/genesis-system", metrics_file: str = "evolution_metrics.json"):
        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_file_path = Path(metrics_file)
        self.metrics = self._load_metrics()
        self.improvement_proposals = []  # Store proposed improvements

    def _load_metrics(self) -> Dict:
        """Loads metrics from file or initializes an empty dictionary."""
        if self.metrics_file_path.exists():
            try:
                with open(self.metrics_file_path, "r") as f:
                    return json.load(f)
            except json.JSONDecodeError:
                print("Warning: Metrics file is corrupt. Initializing with empty metrics.")
                return {}
        else:
            return {}

    def _save_metrics(self):
        """Saves metrics to file."""
        with open(self.metrics_file_path, "w") as f:
            json.dump(self.metrics, f, indent=4)

    def _record_metric(self, metric_name: str, value: float):
        """Records a metric value with timestamp."""
        if metric_name not in self.metrics:
            self.metrics[metric_name] = []
        self.metrics[metric_name].append({"timestamp": datetime.now().isoformat(), "value": value})
        self._save_metrics()

    def _detect_anomalies(self, metric_name: str, threshold: float = 2.0) -> bool:
        """Detects anomalies based on standard deviation from the mean."""
        if metric_name not in self.metrics or len(self.metrics[metric_name]) < 3:
            return False  # Not enough data to detect anomalies

        values = [item["value"] for item in self.metrics[metric_name]]
        mean = sum(values) / len(values)
        std_dev = (sum([(x - mean) ** 2 for x in values]) / len(values)) ** 0.5

        if std_dev == 0:
            return False # Avoid division by zero. No variance.

        latest_value = values[-1]
        z_score = abs(latest_value - mean) / std_dev

        return z_score > threshold

    def _generate_improvement_proposal(self, anomaly_metric: str):
        """Generates an improvement proposal based on a detected anomaly."""
        # Simple example: propose increasing P5 consensus checks if axiom generation fails frequently
        if "axiom_generation_failure_rate" in anomaly_metric:
            proposal = {
                "description": "Increase robustness of P5 consensus checks to improve axiom generation success rate.",
                "potential_impact": "Improved knowledge graph accuracy and completeness.",
                "risk": "Increased processing time due to stricter consensus checks.",
                "metric_to_improve": "axiom_generation_failure_rate",
                "implementation": "Increase the stringency of the checks done by CONSENSUS_01 and CONSENSUS_02 agents."
            }
            self.improvement_proposals.append(proposal)
            print(f"💡 Improvement Proposal Generated: {proposal['description']}")

    def _run_ab_test(self, video_id: str, improvement_proposal: Dict) -> bool:
        """Runs an A/B test to evaluate an improvement proposal."""
        print(f"🧪 Running A/B test for: {improvement_proposal['description']}")

        # A/B test simulation: randomly decide if the improvement is effective
        effectiveness = random.random() > 0.5  # 50% chance of success

        if effectiveness:
            print("✅ A/B Test: Improvement shows promise!")
            return True
        else:
            print("❌ A/B Test: Improvement did not yield positive results.")
            return False

    def _score_improvement(self, improvement_proposal: Dict, ab_test_result: bool) -> float:
        """Scores an improvement proposal based on its potential impact and A/B test results."""
        # Placeholder scoring logic
        if ab_test_result:
            score = 0.7 + random.random() * 0.3  # Higher score for successful improvements
        else:
            score = 0.2 + random.random() * 0.2  # Lower score for failed improvements

        return score

    def _rollback_improvement(self, video_id: str, improvement_proposal: Dict):
        """Rolls back a failed improvement."""
        print(f"⏪ Rolling back improvement: {improvement_proposal['description']}")
        # In a real system, this would involve reverting code changes, configuration updates, etc.
        # For this example, we just log the rollback.
        print(f"Rollback completed for {video_id}.")

    def process_new_video(self, video_id: str, url: str):
        """Runs youtube_learner, integrates into KG, and monitors performance."""
        print(f"--- Evolution Start: {video_id} ---")

        # 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)
        consensus_passed = self._run_p5_consensus(video_id, learner_output)
        if not consensus_passed:
            print(f"⚠️ EVOLUTION BLOCKED: P5 Consensus Gate failed for {video_id}.")
            return

        # 3. Axiomatization
        axiom_success = 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)

        # 6. Monitor Performance and Detect Anomalies
        self._monitor_performance(video_id, consensus_passed, axiom_success)
        self._check_for_improvement_opportunities()

    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) -> bool:
        """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}")
                return True
            else:
                print("! Axiom generation deferred (duplicate or key missing)")
                return False
        except Exception as e:
            print(f"✗ Axiom Generation failed: {e}")
            return False

    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 _monitor_performance(self, video_id: str, consensus_passed: bool, axiom_success: bool):
        """Monitors performance metrics and records them."""
        self._record_metric("video_processing_latency", random.uniform(10, 30))  # Simulate latency
        self._record_metric("consensus_pass_rate", 1.0 if consensus_passed else 0.0)
        self._record_metric("axiom_generation_success_rate", 1.0 if axiom_success else 0.0)

        # Simulate errors
        if random.random() < 0.05:  # 5% chance of an error
            self._record_metric("youtube_learner_error_rate", 1.0)
        else:
             self._record_metric("youtube_learner_error_rate", 0.0)


        if not axiom_success:
            self._record_metric("axiom_generation_failure_rate", 1.0)
        else:
            self._record_metric("axiom_generation_failure_rate", 0.0)


    def _check_for_improvement_opportunities(self):
        """Checks for anomalies and generates improvement proposals."""
        if self._detect_anomalies("axiom_generation_failure_rate", threshold=1.5):
            self._generate_improvement_proposal("axiom_generation_failure_rate")

        if self.improvement_proposals:
            proposal = self.improvement_proposals.pop(0) # Get the first proposal
            ab_test_result = self._run_ab_test("test_video", proposal)
            improvement_score = self._score_improvement(proposal, ab_test_result)

            if ab_test_result:
                print(f"🎉 Improvement '{proposal['description']}' deployed with score: {improvement_score}")
            else:
                print(f"❌ Improvement '{proposal['description']}' failed. Rolling back.")
                self._rollback_improvement("test_video", proposal)

if __name__ == "__main__":
    engine = EvolutionEngineV2()
    engine.process_new_video("vqHBfe3r4OQ", "https://www.youtube.com/watch?v=vqHBfe3r4OQ")
    engine.process_new_video("another_video", "https://www.youtube.com/watch?v=dummy") # Process another video to generate more data.