import logging
from typing import Callable, Dict, List, Any

logger = logging.getLogger(__name__)

class EventBus:
    """Simple in-memory Pub/Sub Event Bus for decoupled mega-modules."""
    def __init__(self):
        self.subscribers: Dict[str, List[Callable]] = {}

    def subscribe(self, event_type: str, callback: Callable):
        if event_type not in self.subscribers:
            self.subscribers[event_type] = []
        self.subscribers[event_type].append(callback)
        logger.info(f"[EVENT BUS] Subscribed to {event_type}")

    async def publish(self, event_type: str, data: Any = None):
        if event_type in self.subscribers:
            logger.info(f"[EVENT BUS] Publishing {event_type} to {len(self.subscribers[event_type])} subscribers")
            for callback in self.subscribers[event_type]:
                try:
                    import asyncio
                    if asyncio.iscoroutinefunction(callback):
                        asyncio.create_task(callback(data))
                    else:
                        callback(data)
                except Exception as e:
                    logger.error(f"[EVENT BUS] Error in subscriber for {event_type}: {e}", exc_info=True)

    @staticmethod
    def publish_outbox(cur, event_type: str, payload: dict):
        """
        Publish an event transactionally by writing it to the outbox table.
        Needs an active psycopg2/psycopg3 cursor (cur).
        """
        import json
        from uuid import UUID
        
        def uuid_convert(obj):
            if isinstance(obj, UUID):
                return str(obj)
            raise TypeError
            
        payload_json = json.dumps(payload, default=uuid_convert)
        
        cur.execute(
            """
            INSERT INTO erp_outbox_events (event_type, payload, status)
            VALUES (%s, %s, 'pending')
            """,
            (event_type, payload_json)
        )
        logger.debug(f"[EVENT BUS] Event {event_type} written to outbox.")

event_bus = EventBus()

