#!/usr/bin/env python3
"""
Genesis MCP Memory Bridge
==========================
Bridges surprise-based memory system to MCP Knowledge Graph.

Automatically stores significant learnings as entities when they pass
the surprise threshold. Enables cross-session memory continuity.

Usage:
    from mcp_memory_bridge import MCPMemoryBridge

    bridge = MCPMemoryBridge()

    # Process and auto-store to MCP if significant
    result = bridge.process_with_mcp(
        content="Discovery: Parallel agents reduce latency by 37%",
        domain="learning",
        source="orchestrator"
    )

    # Reflect with MCP integration
    bridge.observe("Deployed feature X", "Users adopt quickly")
    learnings = bridge.reflect_with_mcp("Adoption was slow due to UX issues")

Integration:
    - Wraps MemorySystem from surprise_memory.py
    - Auto-creates MCP entities for semantic-tier memories
    - Creates relations between related learnings
    - Queries MCP for context in novelty calculation
"""

import json
import subprocess
import hashlib
from datetime import datetime
from typing import Dict, List, Any, Optional
from pathlib import Path

# Import from existing surprise memory system
from surprise_memory import MemorySystem, MemoryItem, SurpriseScore


class MCPMemoryBridge:
    """
    Bridges surprise-based memory to MCP Knowledge Graph.

    Threshold for MCP storage: semantic tier (score >= 0.8)
    Creates entities with type: "learning", "discovery", "deviation"
    """

    # Minimum score to store in MCP
    MCP_THRESHOLD = 0.6  # Episodic and above goes to MCP

    # Entity type mapping based on domain
    ENTITY_TYPES = {
        "learning": "learning",
        "technical": "discovery",
        "error": "issue",
        "decision": "decision",
        "general": "observation"
    }

    def __init__(self, storage_path: str = None):
        self.memory = MemorySystem(storage_path)
        self.bridge_log_path = Path("/mnt/e/genesis-system/mcp_bridge_log.json")
        self._load_bridge_log()

    def _load_bridge_log(self):
        """Load log of entities created via bridge."""
        self.bridge_log = {"entities_created": [], "relations_created": []}
        if self.bridge_log_path.exists():
            try:
                with open(self.bridge_log_path) as f:
                    self.bridge_log = json.load(f)
            except Exception:
                pass

    def _save_bridge_log(self):
        """Save bridge activity log."""
        with open(self.bridge_log_path, 'w') as f:
            json.dump(self.bridge_log, f, indent=2)

    def _generate_entity_name(self, content: str, entity_type: str) -> str:
        """Generate unique entity name."""
        # Create short hash for uniqueness
        hash_suffix = hashlib.md5(content.encode()).hexdigest()[:6]
        # Extract first meaningful words
        words = content.split()[:5]
        short_desc = " ".join(words)[:40]
        return f"{entity_type.title()}: {short_desc} ({hash_suffix})"

    def _call_mcp_create_entity(self, name: str, entity_type: str,
                                 observations: List[str]) -> bool:
        """
        Create entity in MCP knowledge graph.

        Note: In actual Claude Code context, this would use
        mcp__memory__create_entities directly. For standalone use,
        we log the intent for later batch processing.
        """
        entity = {
            "name": name,
            "entityType": entity_type,
            "observations": observations,
            "created_via": "mcp_memory_bridge",
            "created_at": datetime.now().isoformat()
        }

        # Log for batch processing or verification
        self.bridge_log["entities_created"].append(entity)
        self._save_bridge_log()

        return True

    def _call_mcp_create_relation(self, from_entity: str, to_entity: str,
                                   relation_type: str) -> bool:
        """
        Create relation in MCP knowledge graph.
        """
        relation = {
            "from": from_entity,
            "to": to_entity,
            "relationType": relation_type,
            "created_at": datetime.now().isoformat()
        }

        self.bridge_log["relations_created"].append(relation)
        self._save_bridge_log()

        return True

    def process_with_mcp(self, content: str, source: str = "claude_code",
                         domain: str = "general", metadata: Dict = None,
                         related_to: str = None) -> Dict[str, Any]:
        """
        Evaluate information and store to both local and MCP if significant.

        Args:
            content: The information to process
            source: Origin of the information
            domain: Category (learning, technical, error, decision, general)
            metadata: Additional context
            related_to: Optional entity name to create relation to

        Returns:
            Dict with tier, score, and MCP storage status
        """
        # First, route through surprise memory
        result = self.memory.process(content, source, domain, metadata)

        # If score meets MCP threshold, create entity
        score = result.get("score", {}).get("total", 0)
        mcp_stored = False
        entity_name = None

        if score >= self.MCP_THRESHOLD:
            entity_type = self.ENTITY_TYPES.get(domain, "observation")
            entity_name = self._generate_entity_name(content, entity_type)

            observations = [
                content,
                f"Source: {source}",
                f"Domain: {domain}",
                f"Surprise score: {score:.3f}",
                f"Tier: {result.get('tier', 'unknown')}",
                f"Captured: {datetime.now().isoformat()}"
            ]

            if metadata:
                for k, v in metadata.items():
                    observations.append(f"{k}: {v}")

            mcp_stored = self._call_mcp_create_entity(
                name=entity_name,
                entity_type=entity_type,
                observations=observations
            )

            # Create relation if specified
            if related_to and mcp_stored:
                self._call_mcp_create_relation(
                    from_entity=entity_name,
                    to_entity=related_to,
                    relation_type="related_to"
                )

        result["mcp_stored"] = mcp_stored
        result["mcp_entity"] = entity_name
        result["mcp_threshold"] = self.MCP_THRESHOLD

        return result

    def observe(self, action: str, expected_outcome: str, context: str = ""):
        """Record observation for reflective learning."""
        self.memory.observe(action, expected_outcome, context)

    def reflect_with_mcp(self, actual_outcome: str,
                         task_name: str = None) -> List[Dict]:
        """
        Reflect on observations and store deviations to MCP.

        Significant deviations (where expected != actual) are stored
        as "deviation" entities in the knowledge graph.
        """
        learnings = self.memory.reflect(actual_outcome)

        for learning in learnings:
            deviation = learning.get("deviation", 0)
            routing = learning.get("routing", {})
            score = routing.get("score", {}).get("total", 0)

            if deviation > 0.4 and score >= self.MCP_THRESHOLD:
                obs = learning.get("observation", {})

                entity_name = f"Deviation: {obs.get('action', 'Unknown')[:30]} ({datetime.now().strftime('%H%M%S')})"

                observations = [
                    f"Action: {obs.get('action', 'Unknown')}",
                    f"Expected: {obs.get('expected', 'Unknown')}",
                    f"Actual: {actual_outcome}",
                    f"Deviation score: {deviation:.3f}",
                    f"Surprise score: {score:.3f}",
                    f"Context: {obs.get('context', 'None')}",
                    f"Captured: {datetime.now().isoformat()}"
                ]

                self._call_mcp_create_entity(
                    name=entity_name,
                    entity_type="deviation",
                    observations=observations
                )

                # Relate to task if specified
                if task_name:
                    self._call_mcp_create_relation(
                        from_entity=entity_name,
                        to_entity=task_name,
                        relation_type="deviation_from"
                    )

                learning["mcp_entity"] = entity_name

        return learnings

    def get_pending_mcp_operations(self) -> Dict[str, List]:
        """
        Get entities/relations pending MCP creation.

        Use this in Claude Code context to batch create:

        pending = bridge.get_pending_mcp_operations()
        mcp__memory__create_entities(pending["entities"])
        mcp__memory__create_relations(pending["relations"])
        bridge.mark_synced()
        """
        return {
            "entities": self.bridge_log.get("entities_created", []),
            "relations": self.bridge_log.get("relations_created", [])
        }

    def mark_synced(self):
        """Mark all pending operations as synced (clear log)."""
        self.bridge_log = {"entities_created": [], "relations_created": []}
        self._save_bridge_log()

    def get_stats(self) -> Dict:
        """Get combined statistics."""
        memory_stats = self.memory.get_stats()

        return {
            **memory_stats,
            "mcp_bridge": {
                "entities_pending": len(self.bridge_log.get("entities_created", [])),
                "relations_pending": len(self.bridge_log.get("relations_created", [])),
                "mcp_threshold": self.MCP_THRESHOLD
            }
        }

    def auto_learn(self, task: str, result: str, success: bool,
                   domain: str = "learning") -> Dict:
        """
        Convenience method for common learning pattern.

        Usage:
            bridge.auto_learn(
                task="Implemented orchestrator",
                result="37% faster with parallel execution",
                success=True
            )
        """
        if success:
            content = f"Success on '{task}': {result}"
        else:
            content = f"Failure on '{task}': {result}"

        return self.process_with_mcp(
            content=content,
            source="auto_learn",
            domain=domain,
            metadata={
                "task": task,
                "success": success
            }
        )


class MCPSyncer:
    """
    Syncs pending bridge operations to actual MCP.

    Run this within Claude Code context where MCP tools are available.
    """

    def __init__(self, bridge: MCPMemoryBridge):
        self.bridge = bridge

    def generate_sync_commands(self) -> str:
        """
        Generate MCP commands for pending operations.

        Returns Python code that can be executed in Claude Code context.
        """
        pending = self.bridge.get_pending_mcp_operations()

        commands = []

        if pending["entities"]:
            entities = []
            for e in pending["entities"]:
                entities.append({
                    "name": e["name"],
                    "entityType": e["entityType"],
                    "observations": e["observations"]
                })
            commands.append(f"mcp__memory__create_entities({json.dumps(entities, indent=2)})")

        if pending["relations"]:
            relations = []
            for r in pending["relations"]:
                relations.append({
                    "from": r["from"],
                    "to": r["to"],
                    "relationType": r["relationType"]
                })
            commands.append(f"mcp__memory__create_relations({json.dumps(relations, indent=2)})")

        if commands:
            commands.append("# After successful sync:")
            commands.append("# bridge.mark_synced()")

        return "\n\n".join(commands) if commands else "# No pending operations"


# CLI Interface
if __name__ == "__main__":
    import sys

    bridge = MCPMemoryBridge()

    if len(sys.argv) < 2:
        print("""
Genesis MCP Memory Bridge
=========================

Commands:
  process "<content>"              Process and store if significant
  learn "<task>" "<result>" [y/n]  Record learning (y=success, n=fail)
  observe "<action>" "<expected>"  Record observation
  reflect "<actual>"               Reflect on observations
  pending                          Show pending MCP operations
  sync-commands                    Generate MCP sync commands
  stats                            Show statistics
  clear                            Clear pending operations

Examples:
  python mcp_memory_bridge.py process "Discovery: asyncio.gather is 37% faster"
  python mcp_memory_bridge.py learn "Implemented cache" "50% latency reduction" y
  python mcp_memory_bridge.py observe "Deployed v2" "Smooth rollout"
  python mcp_memory_bridge.py reflect "Rollout had issues due to config"
  python mcp_memory_bridge.py pending
  python mcp_memory_bridge.py sync-commands
        """)
        sys.exit(0)

    command = sys.argv[1]

    if command == "process" and len(sys.argv) > 2:
        content = " ".join(sys.argv[2:])
        result = bridge.process_with_mcp(content, domain="learning")
        print(json.dumps(result, indent=2))

    elif command == "learn" and len(sys.argv) >= 4:
        task = sys.argv[2]
        result_text = sys.argv[3]
        success = sys.argv[4].lower() in ['y', 'yes', 'true', '1'] if len(sys.argv) > 4 else True
        result = bridge.auto_learn(task, result_text, success)
        print(json.dumps(result, indent=2))

    elif command == "observe" and len(sys.argv) >= 4:
        action = sys.argv[2]
        expected = sys.argv[3]
        bridge.observe(action, expected)
        print(f"Recorded: Action='{action}', Expected='{expected}'")

    elif command == "reflect" and len(sys.argv) > 2:
        actual = " ".join(sys.argv[2:])
        learnings = bridge.reflect_with_mcp(actual)
        print(json.dumps(learnings, indent=2))

    elif command == "pending":
        pending = bridge.get_pending_mcp_operations()
        print(f"Entities pending: {len(pending['entities'])}")
        print(f"Relations pending: {len(pending['relations'])}")
        if pending['entities']:
            print("\nEntities:")
            for e in pending['entities'][-5:]:
                print(f"  - {e['name']} ({e['entityType']})")

    elif command == "sync-commands":
        syncer = MCPSyncer(bridge)
        print(syncer.generate_sync_commands())

    elif command == "stats":
        stats = bridge.get_stats()
        print(json.dumps(stats, indent=2))

    elif command == "clear":
        bridge.mark_synced()
        print("Cleared pending operations")

    else:
        print(f"Unknown command: {command}")
        sys.exit(1)
