"""
Genesis Claude Bridge - Real-time communication between Claude Desktop and Claude Code

This module provides the communication layer for the Claude-to-Claude union.
Messages are stored in Redis for real-time communication.

Usage:
    from claude_bridge import ClaudeBridge

    bridge = ClaudeBridge()

    # Send message to Claude Desktop
    bridge.send_to_desktop({"task": "research", "query": "MCP best practices"})

    # Check for messages from Desktop
    messages = bridge.get_messages_from_desktop()

    # Send message to Claude Code (from Desktop context)
    bridge.send_to_code({"instruction": "implement feature X"})
"""

import json
import redis
from datetime import datetime, timezone
from typing import Dict, List, Any
from dataclasses import dataclass, asdict, field
import sys
import os

# Add genesis-memory to path
sys.path.insert(0, 'E:/genesis-system/data/genesis-memory')

# Redis configuration
REDIS_CONFIG = {
    "host": "redis-genesis-u50607.vm.elestio.app",
    "port": 26379,
    "username": "default",
    "password": "e2ZyYYr4oWRdASI2CaLc-",
    "decode_responses": True
}

# Message queues
QUEUE_CODE_TO_DESKTOP = "genesis:bridge:code_to_desktop"
QUEUE_DESKTOP_TO_CODE = "genesis:bridge:desktop_to_code"
QUEUE_STATUS = "genesis:bridge:status"


@dataclass
class BridgeMessage:
    """Standard message format for Claude-to-Claude communication."""
    sender: str  # "desktop" or "code"
    recipient: str  # "desktop" or "code"
    message_type: str  # "task", "response", "status", "handoff"
    payload: Dict[str, Any]
    timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
    message_id: str = field(default_factory=lambda: f"msg_{int(datetime.now(timezone.utc).timestamp() * 1000)}")


class ClaudeBridge:
    """
    Real-time communication bridge between Claude Desktop and Claude Code.
    Uses Redis as the message broker for instant communication.
    """

    # File-based fallback for when Redis is unavailable
    FALLBACK_FILE = "E:/genesis-system/data/genesis_bridge.json"

    def __init__(self, sender: str = "code"):
        """
        Initialize the bridge.

        Args:
            sender: Identity of this Claude instance ("code" or "desktop")
        """
        self.sender = sender
        self._redis = None
        self._connect_redis()

    def _connect_redis(self):
        """Connect to Redis."""
        try:
            self._redis = redis.Redis(**REDIS_CONFIG)
            self._redis.ping()
            print(f"[Bridge] Connected to Genesis Redis")
        except Exception as e:
            print(f"[Bridge] Redis unavailable, using file fallback: {e}")
            self._redis = None

    @property
    def connected(self) -> bool:
        """Check if Redis is connected."""
        if self._redis is None:
            return False
        try:
            self._redis.ping()
            return True
        except:
            return False

    def send_to_desktop(self, payload: Dict[str, Any], message_type: str = "task") -> Dict:
        """
        Send a message to Claude Desktop.

        Args:
            payload: The message content
            message_type: Type of message (task, response, status, handoff)

        Returns:
            Response with message ID and status
        """
        message = BridgeMessage(
            sender="code",
            recipient="desktop",
            message_type=message_type,
            payload=payload
        )
        return self._send_message(QUEUE_CODE_TO_DESKTOP, message)

    def send_to_code(self, payload: Dict[str, Any], message_type: str = "task") -> Dict:
        """
        Send a message to Claude Code.

        Args:
            payload: The message content
            message_type: Type of message (task, response, status, handoff)

        Returns:
            Response with message ID and status
        """
        message = BridgeMessage(
            sender="desktop",
            recipient="code",
            message_type=message_type,
            payload=payload
        )
        return self._send_message(QUEUE_DESKTOP_TO_CODE, message)

    def get_messages_from_desktop(self, count: int = 10) -> List[Dict]:
        """Get pending messages from Claude Desktop (for Code to read)."""
        return self._get_messages(QUEUE_DESKTOP_TO_CODE, count)

    def get_messages_from_code(self, count: int = 10) -> List[Dict]:
        """Get pending messages from Claude Code (for Desktop to read)."""
        return self._get_messages(QUEUE_CODE_TO_DESKTOP, count)

    def get_my_messages(self, count: int = 10) -> List[Dict]:
        """Get messages addressed to this Claude instance."""
        if self.sender == "code":
            return self.get_messages_from_desktop(count)
        else:
            return self.get_messages_from_code(count)

    def peek_messages(self, queue: str, count: int = 10) -> List[Dict]:
        """Peek at messages without removing them."""
        if self._redis:
            try:
                messages = self._redis.lrange(queue, 0, count - 1)
                return [json.loads(m) for m in messages]
            except Exception as e:
                print(f"[Bridge] Peek error: {e}")
        return []

    def _send_message(self, queue: str, message: BridgeMessage) -> Dict:
        """Send a message to the specified queue."""
        msg_dict = asdict(message)

        if self._redis:
            try:
                self._redis.lpush(queue, json.dumps(msg_dict))
                self._redis.ltrim(queue, 0, 99)  # Keep last 100 messages

                # Update status
                self._redis.hset(QUEUE_STATUS, f"last_{message.sender}_message", message.timestamp)
                self._redis.hset(QUEUE_STATUS, f"{message.sender}_online", "true")

                return {
                    "status": "sent",
                    "message_id": message.message_id,
                    "queue": queue,
                    "timestamp": message.timestamp
                }
            except Exception as e:
                print(f"[Bridge] Send error, using fallback: {e}")

        return self._fallback_send(message)

    def _get_messages(self, queue: str, count: int) -> List[Dict]:
        """Get and remove messages from the specified queue."""
        if self._redis:
            try:
                messages = []
                for _ in range(count):
                    msg = self._redis.rpop(queue)
                    if msg is None:
                        break
                    messages.append(json.loads(msg))
                return messages
            except Exception as e:
                print(f"[Bridge] Get error, using fallback: {e}")

        return self._fallback_get(queue)

    def _fallback_send(self, message: BridgeMessage) -> Dict:
        """Store message in fallback file when Redis is unavailable."""
        try:
            with open(self.FALLBACK_FILE, 'r') as f:
                data = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            data = {"channels": {}}

        queue = "code_to_desktop" if message.sender == "code" else "desktop_to_code"
        if queue not in data.get("channels", {}):
            data["channels"][queue] = {"queue": []}

        data["channels"][queue]["queue"].append(asdict(message))
        data["channels"][queue]["last_message"] = message.timestamp
        data["last_updated"] = datetime.now(timezone.utc).isoformat()

        with open(self.FALLBACK_FILE, 'w') as f:
            json.dump(data, f, indent=2)

        return {"status": "stored_fallback", "message_id": message.message_id}

    def _fallback_get(self, queue: str) -> List[Dict]:
        """Get messages from fallback file."""
        try:
            with open(self.FALLBACK_FILE, 'r') as f:
                data = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            return []

        channel = "desktop_to_code" if "desktop_to_code" in queue else "code_to_desktop"
        messages = data.get("channels", {}).get(channel, {}).get("queue", [])

        # Clear the queue after reading
        if messages:
            data["channels"][channel]["queue"] = []
            with open(self.FALLBACK_FILE, 'w') as f:
                json.dump(data, f, indent=2)

        return messages

    # Convenience methods for common operations

    def handshake(self) -> Dict:
        """Send a handshake message announcing this Claude instance."""
        return self.send_to_desktop({
            "action": "handshake",
            "instance": self.sender,
            "capabilities": [
                "file_editing",
                "code_generation",
                "testing",
                "git_operations",
                "bash_commands"
            ],
            "status": "online"
        }, message_type="status")

    def request_research(self, topic: str, depth: str = "medium") -> Dict:
        """Request Claude Desktop to perform research."""
        return self.send_to_desktop({
            "action": "research",
            "topic": topic,
            "depth": depth,
            "requested_by": "claude_code"
        }, message_type="task")

    def send_status_update(self, status: str, details: Dict = None) -> Dict:
        """Send a status update to Claude Desktop."""
        return self.send_to_desktop({
            "status": status,
            "details": details or {},
            "component": "claude_code"
        }, message_type="status")

    def request_handoff(self, context: Dict, next_steps: List[str]) -> Dict:
        """Request a handoff to Claude Desktop."""
        return self.send_to_desktop({
            "context": context,
            "next_steps": next_steps,
            "handoff_reason": "task_requires_desktop_capabilities"
        }, message_type="handoff")

    def get_bridge_status(self) -> Dict:
        """Get the current bridge status."""
        status = {
            "redis_connected": self.connected,
            "sender": self.sender,
            "queues": {}
        }

        if self._redis:
            try:
                status["queues"]["code_to_desktop"] = self._redis.llen(QUEUE_CODE_TO_DESKTOP)
                status["queues"]["desktop_to_code"] = self._redis.llen(QUEUE_DESKTOP_TO_CODE)
                status["status_data"] = self._redis.hgetall(QUEUE_STATUS)
            except:
                pass

        return status


# Singleton instance for easy import
_bridge_instance = None

def get_bridge(sender: str = "code") -> ClaudeBridge:
    """Get the singleton bridge instance."""
    global _bridge_instance
    if _bridge_instance is None:
        _bridge_instance = ClaudeBridge(sender)
    return _bridge_instance


# Quick test
if __name__ == "__main__":
    print("=" * 60)
    print("GENESIS CLAUDE BRIDGE v2.0")
    print("=" * 60)
    print()

    bridge = ClaudeBridge(sender="code")

    print(f"Redis Connected: {bridge.connected}")
    print()

    # Send handshake
    print("Sending handshake to Claude Desktop...")
    result = bridge.handshake()
    print(f"Result: {result}")
    print()

    # Get status
    print("Bridge Status:")
    status = bridge.get_bridge_status()
    print(json.dumps(status, indent=2))
    print()

    # Check for messages
    print("Checking for messages from Desktop...")
    messages = bridge.get_messages_from_desktop()
    print(f"Found {len(messages)} messages")
    for msg in messages:
        print(f"  [{msg.get('message_type')}] {msg.get('payload', {}).get('action', 'unknown')}")

    print()
    print("=" * 60)
    print("BRIDGE OPERATIONAL - Union Established!")
    print("=" * 60)
