"""Event Bus - Real-time event streaming for visualization

Every operation emits events that the frontend can visualize:
- FLIP: content -> coordinates
- DISPATCH: task -> agent -> outcome
- DIALOGUE: input -> thinking -> response
- CONNECT: sphere A <-> sphere B
- STATE_CHANGE: I-Logic state transitions
"""

import time
import json
from dataclasses import dataclass, field, asdict
from typing import Dict, Any, List, Callable, Optional
from enum import Enum


class EventType(Enum):
    # Core operations
    FLIP = "flip"
    DISPATCH = "dispatch"
    DIALOGUE = "dialogue"

    # Connections
    CONNECT = "connect"
    DISCONNECT = "disconnect"
    MESSAGE = "message"

    # State changes
    STATE_CHANGE = "state_change"
    POSITION_CHANGE = "position_change"

    # Agent activity
    AGENT_ACTIVE = "agent_active"
    AGENT_RESOLVE = "agent_resolve"
    AGENT_CHAIN = "agent_chain"

    # System
    SPHERE_CREATE = "sphere_create"
    SPHERE_BOUNCE = "sphere_bounce"
    TOOL_CALL = "tool_call"

    # Data flow
    DATA_FLOW = "data_flow"
    BASIN_UPDATE = "basin_update"


@dataclass
class Event:
    """A single event in the system."""
    type: EventType
    source_id: str           # Who emitted this
    target_id: str = ""      # Who receives (if applicable)
    data: Dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)

    # Visual hints
    color: str = "#ffffff"
    animation: str = "pulse"  # pulse, flow, highlight, shake
    duration_ms: int = 500

    def to_dict(self) -> Dict[str, Any]:
        return {
            'type': self.type.value,
            'source_id': self.source_id,
            'target_id': self.target_id,
            'data': self.data,
            'timestamp': self.timestamp,
            'visual': {
                'color': self.color,
                'animation': self.animation,
                'duration_ms': self.duration_ms,
            }
        }

    def to_json(self) -> str:
        return json.dumps(self.to_dict())


class EventBus:
    """Central event bus for the visual system.

    All components publish events here.
    The WebSocket server subscribes and forwards to frontend.
    """

    def __init__(self, max_history: int = 1000):
        self._subscribers: List[Callable[[Event], None]] = []
        self._history: List[Event] = []
        self._max_history = max_history
        self._filters: Dict[str, List[EventType]] = {}

    def subscribe(self, callback: Callable[[Event], None],
                  filter_types: List[EventType] = None) -> str:
        """Subscribe to events. Returns subscription ID."""
        sub_id = f"sub_{len(self._subscribers)}_{int(time.time()*1000)}"
        self._subscribers.append(callback)
        if filter_types:
            self._filters[sub_id] = filter_types
        return sub_id

    def unsubscribe(self, callback: Callable[[Event], None]):
        """Unsubscribe from events."""
        if callback in self._subscribers:
            self._subscribers.remove(callback)

    def emit(self, event: Event):
        """Emit an event to all subscribers."""
        # Add to history
        self._history.append(event)
        if len(self._history) > self._max_history:
            self._history = self._history[-self._max_history:]

        # Notify subscribers
        for callback in self._subscribers:
            try:
                callback(event)
            except Exception as e:
                print(f"Event callback error: {e}")

    def emit_flip(self, content: str, hex_id: str, basin: int,
                  layer: int, i_state: str, source_id: str = "system"):
        """Emit a FLIP event."""
        self.emit(Event(
            type=EventType.FLIP,
            source_id=source_id,
            data={
                'content': content[:100],
                'hex_id': hex_id,
                'basin': basin,
                'layer': layer,
                'i_state': i_state,
            },
            color=self._layer_color(layer),
            animation="pulse",
        ))

    def emit_dispatch(self, task: str, agent_name: str, agent_layer: int,
                      outcome: str, resolved: bool, source_id: str = "swarm"):
        """Emit a DISPATCH event."""
        self.emit(Event(
            type=EventType.DISPATCH,
            source_id=source_id,
            target_id=agent_name,
            data={
                'task': task[:100],
                'agent': agent_name,
                'layer': agent_layer,
                'outcome': outcome,
                'resolved': resolved,
            },
            color="#00ff88" if resolved else "#ff8800",
            animation="flow",
            duration_ms=800,
        ))

    def emit_dialogue(self, content: str, hex_id: str, route: str,
                      thinking_state: str, response: str,
                      source_id: str, bounce: int):
        """Emit a DIALOGUE event."""
        self.emit(Event(
            type=EventType.DIALOGUE,
            source_id=source_id,
            data={
                'content': content[:100],
                'hex_id': hex_id,
                'route': route,
                'thinking_state': thinking_state,
                'response': response[:100],
                'bounce': bounce,
            },
            color=self._state_color(thinking_state),
            animation="pulse",
        ))

    def emit_state_change(self, source_id: str, old_state: str,
                          new_state: str, trigger: str = ""):
        """Emit a STATE_CHANGE event."""
        self.emit(Event(
            type=EventType.STATE_CHANGE,
            source_id=source_id,
            data={
                'old_state': old_state,
                'new_state': new_state,
                'trigger': trigger,
            },
            color=self._state_color(new_state),
            animation="highlight",
        ))

    def emit_data_flow(self, from_id: str, to_id: str,
                       data_type: str, data_preview: str):
        """Emit a DATA_FLOW event (shows connection animation)."""
        self.emit(Event(
            type=EventType.DATA_FLOW,
            source_id=from_id,
            target_id=to_id,
            data={
                'data_type': data_type,
                'preview': data_preview[:50],
            },
            color="#00aaff",
            animation="flow",
            duration_ms=600,
        ))

    def emit_agent_active(self, agent_name: str, task: str):
        """Emit AGENT_ACTIVE event."""
        self.emit(Event(
            type=EventType.AGENT_ACTIVE,
            source_id=agent_name,
            data={'task': task[:100]},
            color="#ffff00",
            animation="pulse",
        ))

    def emit_tool_call(self, tool_name: str, command: str,
                       result_preview: str, source_id: str = "mcp"):
        """Emit TOOL_CALL event."""
        self.emit(Event(
            type=EventType.TOOL_CALL,
            source_id=source_id,
            target_id=tool_name,
            data={
                'tool': tool_name,
                'command': command,
                'result': result_preview[:100],
            },
            color="#aa88ff",
            animation="highlight",
        ))

    def get_history(self, limit: int = 100,
                    event_types: List[EventType] = None) -> List[Event]:
        """Get recent event history."""
        events = self._history[-limit:]
        if event_types:
            events = [e for e in events if e.type in event_types]
        return events

    def _layer_color(self, layer: int) -> str:
        """Color by layer."""
        colors = {
            1: "#ff6666",  # L1 Geometric - red
            2: "#ff9966",  # L2 Semantic - orange
            3: "#ffcc66",  # L3 Spatial - yellow
            4: "#99ff66",  # L4 Functional - lime
            5: "#66ff99",  # L5 Probabilistic - green
            6: "#66ffcc",  # L6 Emotional - teal
            7: "#66ccff",  # L7 Frequency - blue
            8: "#9966ff",  # L8 Electromagnetic - purple
            9: "#ff66cc",  # L9 Membrane - pink
        }
        return colors.get(layer, "#ffffff")

    def _state_color(self, state: str) -> str:
        """Color by I-Logic state."""
        colors = {
            'DIRECT': "#00ff00",
            'INVERSE': "#ff0000",
            'SPIRAL': "#00ffff",
            'ANTI_SPIRAL': "#ff00ff",
            'ABSORBED': "#888888",
        }
        return colors.get(state, "#ffffff")


# Global event bus instance
_global_bus: Optional[EventBus] = None

def get_event_bus() -> EventBus:
    """Get the global event bus."""
    global _global_bus
    if _global_bus is None:
        _global_bus = EventBus()
    return _global_bus
