#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Event Bus Subscriber Wiring — Phase 0.

Registers listeners for the 27+ event channels that previously
published into the void with 0 subscribers. Called once at
orchestrator boot via `wire_all_subscribers()`.

Each subscriber is defensive (try/except) so one failure doesn't
break the bus. Subscribers are idempotent — safe to call wire twice.
"""
import logging
import threading

from pulse.core import event_bus
from pulse.core.enums import EventChannel

_wired = False
_wire_lock = threading.Lock()
log = logging.getLogger("pulse.event_subscribers")


def wire_all_subscribers() -> int:
    """Register all event bus subscribers. Returns count wired. Idempotent."""
    global _wired
    with _wire_lock:
        if _wired:
            return 0
        _wired = True

    count = 0

    # 1. Reconsolidation sweep — clean expired labile items on any memory event
    event_bus.subscribe(EventChannel.MEMORY_REINFORCE, _on_reconsolidation_event)
    event_bus.subscribe(EventChannel.MEMORY_CONTRADICT, _on_reconsolidation_event)
    count += 2

    # 2. Sentinel alerts → log warning + interoception health tracking
    event_bus.subscribe(EventChannel.SENTINEL_ALERT, _on_sentinel_alert)
    count += 1

    # 3. Conscience drift → log + optional quarantine signal
    event_bus.subscribe(EventChannel.CONSCIENCE_DRIFT, _on_conscience_drift)
    count += 1

    # 4. Interoception alerts → log for system health dashboard
    event_bus.subscribe(EventChannel.INTEROCEPTION_ALERT, _on_interoception_alert)
    count += 1

    # 5. Pattern lifecycle — log evolution and deprecation for observability
    event_bus.subscribe(EventChannel.PATTERN_EVOLVED, _on_pattern_evolved)
    event_bus.subscribe(EventChannel.PATTERN_DEPRECATED, _on_pattern_deprecated)
    count += 2

    # 6. Memory resurrection → reinforce resurrected item
    event_bus.subscribe(EventChannel.MEMORY_RESURRECTION, _on_memory_resurrection)
    count += 1

    # 7. Deep consolidation complete → trigger metamemory refresh
    event_bus.subscribe(EventChannel.CONSOLIDATION_DEEP, _on_consolidation_complete)
    count += 1

    # 8. Reward signal → log to state/health/reward_log.jsonl
    event_bus.subscribe("reward.signal", _on_reward_signal)
    count += 1

    # 9. Workspace broadcast → no-op acknowledgement
    event_bus.subscribe(EventChannel.WORKSPACE_BROADCAST, _on_workspace_broadcast)
    count += 1

    # 10. Conscience brain_write → no-op acknowledgement
    event_bus.subscribe(EventChannel.CONSCIENCE_WRITE, _on_conscience_brain_write)
    count += 1

    log.info(f"[EventBus] Wired {count} subscribers across {count} channels")
    return count


# ── Subscriber Callbacks ─────────────────────────────────────────

def _on_reconsolidation_event(event: dict):
    """Sweep expired labile items whenever a reconsolidation event fires."""
    try:
        from pulse.brain.plasticity.reconsolidation import _sweep_expired
        _sweep_expired()
    except Exception as e:
        log.warning(f"Reconsolidation sweep failed: {e}")


def _on_sentinel_alert(event: dict):
    """Log sentinel alerts for system observability."""
    try:
        payload = event.get("payload", {})
        log.warning(f"[Sentinel] {payload.get('type', 'unknown')}: {payload.get('message', '')}")
    except Exception as e:
        log.warning(f"Sentinel alert handler failed: {e}")


def _on_conscience_drift(event: dict):
    """Log ethical drift detection events."""
    try:
        payload = event.get("payload", {})
        log.warning(f"[Conscience] Drift detected: {payload.get('violation_type', 'unknown')} "
                     f"— {payload.get('description', '')[:120]}")
    except Exception as e:
        log.warning(f"Conscience drift handler failed: {e}")


def _on_interoception_alert(event: dict):
    """Log interoception (system health) alerts."""
    try:
        payload = event.get("payload", {})
        log.info(f"[Interoception] {payload.get('metric', 'unknown')}: {payload.get('value', '?')}")
    except Exception as e:
        log.warning(f"Interoception handler failed: {e}")


def _on_pattern_evolved(event: dict):
    """Log when patterns evolve — useful for brain growth tracking."""
    try:
        payload = event.get("payload", {})
        new_count = len(payload.get("new_patterns", []))
        log.info(f"[Pattern] {new_count} patterns evolved")
    except Exception as e:
        log.warning(f"Pattern evolution handler failed: {e}")


def _on_pattern_deprecated(event: dict):
    """Log pattern deprecation for audit trail."""
    try:
        payload = event.get("payload", {})
        log.info(f"[Pattern] Deprecated: {payload.get('pattern_text', '')[:80]}")
    except Exception as e:
        log.warning(f"Pattern deprecation handler failed: {e}")


def _on_memory_resurrection(event: dict):
    """When a memory is resurrected, reinforce it in reconsolidation."""
    try:
        payload = event.get("payload", {})
        item_id = payload.get("item_id", "")
        if item_id:
            from pulse.brain.plasticity.reconsolidation import reinforce
            reinforce(item_id)
    except Exception as e:
        log.warning(f"Memory resurrection handler failed: {e}")


def _on_consolidation_complete(event: dict):
    """After deep consolidation, refresh the metamemory confidence map."""
    try:
        from pulse.brain.metacognition.metamemory import compute_metamemory
        result = compute_metamemory()
        gaps = sum(1 for v in result.values() if isinstance(v, dict) and v.get("gap_flag"))
        log.info(f"[Metamemory] Refreshed: {len(result)} domains, {gaps} gaps detected")
    except Exception as e:
        log.warning(f"Metamemory refresh after consolidation failed: {e}")


def _on_reward_signal(event: dict):
    """Log reward signals to state/health/reward_log.jsonl for observability."""
    try:
        import json
        from pulse.core.paths import get_state_dir
        log_path = get_state_dir() / 'health' / 'reward_log.jsonl'
        log_path.parent.mkdir(parents=True, exist_ok=True)
        with open(log_path, 'a', encoding='utf-8') as f:
            f.write(json.dumps(event, ensure_ascii=False) + '\n')
    except Exception as e:
        log.warning(f'Reward signal log failed: {e}')


def _on_workspace_broadcast(event: dict):
    """No-op subscriber -- acknowledges workspace.broadcast events."""
    pass


def _on_conscience_brain_write(event: dict):
    """No-op subscriber -- acknowledges conscience.brain_write events."""
    pass
