"""
NATS output adapter — pushes normalised events to NATS subjects.

Subject schema:
    {prefix}.{event_type}.{symbol}
    e.g. md.events.trade.BTCUSDT
         md.events.quote.AAPL
         md.events.heartbeat.__system__

SenpAI (or any other consumer) can subscribe to:
    md.events.>           — all events
    md.events.trade.>     — all trades
    md.events.*.BTCUSDT   — all event types for BTC

Payload: JSON (event.model_dump_json())
"""
from __future__ import annotations

import logging

from app.config import settings
from app.domain.events import Event

logger = logging.getLogger(__name__)

# Lazy import — nats-py may not be installed in minimal setups
_nc = None


class NatsOutputConsumer:
    """
    Publishes every event to NATS as JSON.

    Auto-reconnects via nats-py built-in mechanism.
    If NATS is unavailable, logs warning and drops events (non-blocking).
    """

    def __init__(
        self,
        nats_url: str | None = None,
        subject_prefix: str | None = None,
    ) -> None:
        self._url = nats_url or settings.nats_url
        self._prefix = subject_prefix or settings.nats_subject_prefix
        self._nc = None
        self._connected = False
        self._publish_count = 0
        self._drop_count = 0

    async def start(self) -> None:
        """Connect to NATS."""
        try:
            import nats  # noqa: F811

            self._nc = await nats.connect(
                self._url,
                reconnect_time_wait=2,
                max_reconnect_attempts=-1,  # infinite
                name="market-data-service",
                error_cb=self._error_cb,
                disconnected_cb=self._disconnected_cb,
                reconnected_cb=self._reconnected_cb,
            )
            self._connected = True
            logger.info(
                "nats_output.connected",
                extra={"url": self._url, "prefix": self._prefix},
            )
        except ImportError:
            logger.error(
                "nats_output.nats_not_installed",
                extra={"hint": "pip install nats-py"},
            )
        except Exception as e:
            logger.error(
                "nats_output.connect_failed",
                extra={"url": self._url, "error": str(e)},
            )

    async def handle(self, event: Event) -> None:
        """Publish event to NATS subject."""
        if not self._nc or not self._connected:
            self._drop_count += 1
            return

        symbol = getattr(event, "symbol", "__system__")
        subject = f"{self._prefix}.{event.event_type.value}.{symbol}"

        try:
            payload = event.model_dump_json().encode("utf-8")
            await self._nc.publish(subject, payload)
            self._publish_count += 1
        except Exception as e:
            self._drop_count += 1
            if self._drop_count % 1000 == 1:
                logger.warning(
                    "nats_output.publish_failed",
                    extra={
                        "subject": subject,
                        "error": str(e),
                        "total_dropped": self._drop_count,
                    },
                )

    async def stop(self) -> None:
        """Flush and close NATS connection."""
        if self._nc:
            try:
                await self._nc.flush(timeout=5)
                await self._nc.close()
            except Exception as e:
                logger.warning("nats_output.close_error", extra={"error": str(e)})

        logger.info(
            "nats_output.stopped",
            extra={
                "published": self._publish_count,
                "dropped": self._drop_count,
            },
        )

    # ── NATS callbacks ────────────────────────────────────────────────

    async def _error_cb(self, e: Exception) -> None:
        logger.error("nats_output.error", extra={"error": str(e)})

    async def _disconnected_cb(self) -> None:
        self._connected = False
        logger.warning("nats_output.disconnected")

    async def _reconnected_cb(self) -> None:
        self._connected = True
        logger.info("nats_output.reconnected")
