"""
Signal bus publisher — publishes features, signals, and alerts to NATS.

Rate-limiting: max N publishes per second per symbol (configurable).
"""
from __future__ import annotations

import logging
import time

from nats.aio.client import Client as NatsClient

from senpai.md_consumer.config import settings
from senpai.md_consumer.models import AlertEvent, FeatureSnapshot, TradeSignal
from senpai.md_consumer import metrics as m

logger = logging.getLogger(__name__)


class Publisher:
    """
    Publishes FeatureSnapshots and TradeSignals to NATS.
    Built-in per-symbol rate limiter.
    """

    def __init__(self, nc: NatsClient) -> None:
        self._nc = nc
        self._last_publish: dict[str, float] = {}  # symbol → monotonic time
        self._min_interval = (
            1.0 / settings.features_pub_rate_hz
            if settings.features_pub_rate_hz > 0
            else 0.1
        )

    def _rate_ok(self, symbol: str) -> bool:
        """Check if we can publish for this symbol (rate limiter)."""
        now = time.monotonic()
        last = self._last_publish.get(symbol, 0.0)
        if now - last >= self._min_interval:
            self._last_publish[symbol] = now
            return True
        return False

    async def publish_features(self, snapshot: FeatureSnapshot) -> bool:
        """
        Publish feature snapshot if rate limit allows.
        Returns True if published, False if rate-limited or error.
        """
        if not settings.features_enabled:
            return False

        symbol = snapshot.symbol.upper()

        if not self._rate_ok(symbol):
            return False

        subject = f"{settings.features_pub_subject}.{symbol}"
        try:
            payload = snapshot.model_dump_json().encode("utf-8")
            await self._nc.publish(subject, payload)
            m.FEATURE_PUBLISH.labels(symbol=symbol).inc()
            return True
        except Exception as e:
            m.FEATURE_PUBLISH_ERRORS.labels(symbol=symbol).inc()
            logger.warning(
                "publisher.feature_error",
                extra={"symbol": symbol, "error": str(e)},
            )
            return False

    async def publish_signal(self, signal: TradeSignal) -> bool:
        """Publish trade signal (no rate limit — signals are rare)."""
        subject = f"{settings.signals_pub_subject}.{signal.symbol}"
        try:
            payload = signal.model_dump_json().encode("utf-8")
            await self._nc.publish(subject, payload)
            m.SIGNALS_EMITTED.labels(
                symbol=signal.symbol,
                direction=signal.direction,
            ).inc()
            logger.debug(
                "publisher.signal_emitted",
                extra={
                    "symbol": signal.symbol,
                    "direction": signal.direction,
                    "confidence": f"{signal.confidence:.2f}",
                    "reason": signal.reason,
                },
            )
            return True
        except Exception as e:
            logger.error(
                "publisher.signal_error",
                extra={"symbol": signal.symbol, "error": str(e)},
            )
            return False

    async def publish_alert(self, alert: AlertEvent) -> bool:
        """Publish alert event."""
        subject = settings.alerts_pub_subject
        try:
            payload = alert.model_dump_json().encode("utf-8")
            await self._nc.publish(subject, payload)
            m.ALERTS_EMITTED.labels(alert_type=alert.alert_type).inc()
            logger.warning(
                "publisher.alert",
                extra={
                    "type": alert.alert_type,
                    "level": alert.level,
                    "message": alert.message,
                },
            )
            return True
        except Exception as e:
            logger.error(
                "publisher.alert_error",
                extra={"error": str(e)},
            )
            return False
